summaryrefslogtreecommitdiffstats
path: root/kftpgrabber
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2013-07-24 15:21:35 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2013-07-24 15:21:35 -0500
commit093ed975800ab1e5c0d73759f07fedf8d5aa2ca6 (patch)
treed625ba687b185a3984a410f56d9c2e0dade7b114 /kftpgrabber
downloadkftpgrabber-093ed975800ab1e5c0d73759f07fedf8d5aa2ca6.tar.gz
kftpgrabber-093ed975800ab1e5c0d73759f07fedf8d5aa2ca6.zip
Initial import of kftpgrabber 0.8.1 from sources
Diffstat (limited to 'kftpgrabber')
-rw-r--r--kftpgrabber/COPYING-DOCS397
-rw-r--r--kftpgrabber/LICENSE.OpenSSL127
-rw-r--r--kftpgrabber/Makefile.am12
-rw-r--r--kftpgrabber/NEWS0
-rw-r--r--kftpgrabber/Roadmap33
-rw-r--r--kftpgrabber/configure.in.in110
-rw-r--r--kftpgrabber/src/Makefile.am59
-rw-r--r--kftpgrabber/src/checksumverifier.cpp272
-rw-r--r--kftpgrabber/src/checksumverifier.h136
-rw-r--r--kftpgrabber/src/directoryscanner.cpp178
-rw-r--r--kftpgrabber/src/directoryscanner.h106
-rw-r--r--kftpgrabber/src/engine/Makefile.am11
-rw-r--r--kftpgrabber/src/engine/cache.cpp175
-rw-r--r--kftpgrabber/src/engine/cache.h176
-rw-r--r--kftpgrabber/src/engine/commands.cpp78
-rw-r--r--kftpgrabber/src/engine/commands.h136
-rw-r--r--kftpgrabber/src/engine/connectionretry.cpp112
-rw-r--r--kftpgrabber/src/engine/connectionretry.h86
-rw-r--r--kftpgrabber/src/engine/directorylisting.cpp188
-rw-r--r--kftpgrabber/src/engine/directorylisting.h141
-rw-r--r--kftpgrabber/src/engine/event.cpp166
-rw-r--r--kftpgrabber/src/engine/event.h372
-rw-r--r--kftpgrabber/src/engine/ftpdirectoryparser.cpp1144
-rw-r--r--kftpgrabber/src/engine/ftpdirectoryparser.h92
-rw-r--r--kftpgrabber/src/engine/ftpsocket.cpp2749
-rw-r--r--kftpgrabber/src/engine/ftpsocket.h154
-rw-r--r--kftpgrabber/src/engine/sftpsocket.cpp775
-rw-r--r--kftpgrabber/src/engine/sftpsocket.h92
-rw-r--r--kftpgrabber/src/engine/socket.cpp866
-rw-r--r--kftpgrabber/src/engine/socket.h605
-rw-r--r--kftpgrabber/src/engine/speedlimiter.cpp240
-rw-r--r--kftpgrabber/src/engine/speedlimiter.h158
-rw-r--r--kftpgrabber/src/engine/ssl.cpp264
-rw-r--r--kftpgrabber/src/engine/ssl.h176
-rw-r--r--kftpgrabber/src/engine/thread.cpp346
-rw-r--r--kftpgrabber/src/engine/thread.h133
-rw-r--r--kftpgrabber/src/hi16-app-kftpgrabber.pngbin0 -> 861 bytes
-rw-r--r--kftpgrabber/src/hi22-app-kftpgrabber.pngbin0 -> 1401 bytes
-rw-r--r--kftpgrabber/src/hi32-app-kftpgrabber.pngbin0 -> 2224 bytes
-rw-r--r--kftpgrabber/src/hi48-app-kftpgrabber.pngbin0 -> 4175 bytes
-rw-r--r--kftpgrabber/src/hi64-app-kftpgrabber.pngbin0 -> 5445 bytes
-rw-r--r--kftpgrabber/src/kftpbookmarkaction.cpp62
-rw-r--r--kftpgrabber/src/kftpbookmarkaction.h113
-rw-r--r--kftpgrabber/src/kftpbookmarks.cpp948
-rw-r--r--kftpgrabber/src/kftpbookmarks.h160
-rw-r--r--kftpgrabber/src/kftpfileexistsactions.cpp164
-rw-r--r--kftpgrabber/src/kftpfileexistsactions.h138
-rw-r--r--kftpgrabber/src/kftpgrabber-bi-wizard.pngbin0 -> 67270 bytes
-rw-r--r--kftpgrabber/src/kftpgrabber-logo.pngbin0 -> 212636 bytes
-rw-r--r--kftpgrabber/src/kftpgrabber.desktop73
-rw-r--r--kftpgrabber/src/kftpgrabberui.rc31
-rw-r--r--kftpgrabber/src/kftpqueue.cpp834
-rw-r--r--kftpgrabber/src/kftpqueue.h531
-rw-r--r--kftpgrabber/src/kftpqueueconverter.cpp211
-rw-r--r--kftpgrabber/src/kftpqueueconverter.h82
-rw-r--r--kftpgrabber/src/kftpqueueprocessor.cpp137
-rw-r--r--kftpgrabber/src/kftpqueueprocessor.h76
-rw-r--r--kftpgrabber/src/kftpsession.cpp920
-rw-r--r--kftpgrabber/src/kftpsession.h616
-rw-r--r--kftpgrabber/src/kftptransfer.cpp374
-rw-r--r--kftpgrabber/src/kftptransfer.h337
-rw-r--r--kftpgrabber/src/kftptransferdir.cpp151
-rw-r--r--kftpgrabber/src/kftptransferdir.h87
-rw-r--r--kftpgrabber/src/kftptransferfile.cpp413
-rw-r--r--kftpgrabber/src/kftptransferfile.h127
-rw-r--r--kftpgrabber/src/main.cpp137
-rw-r--r--kftpgrabber/src/mainactions.cpp189
-rw-r--r--kftpgrabber/src/mainactions.h115
-rw-r--r--kftpgrabber/src/mainwindow.cpp447
-rw-r--r--kftpgrabber/src/mainwindow.h141
-rw-r--r--kftpgrabber/src/misc/Makefile.am23
-rw-r--r--kftpgrabber/src/misc/config.kcfgc7
-rw-r--r--kftpgrabber/src/misc/configbase.cpp141
-rw-r--r--kftpgrabber/src/misc/configbase.h137
-rw-r--r--kftpgrabber/src/misc/customcommands/Makefile.am16
-rw-r--r--kftpgrabber/src/misc/customcommands/commands.xml113
-rw-r--r--kftpgrabber/src/misc/customcommands/entry.cpp155
-rw-r--r--kftpgrabber/src/misc/customcommands/entry.h236
-rw-r--r--kftpgrabber/src/misc/customcommands/handlers.cpp97
-rw-r--r--kftpgrabber/src/misc/customcommands/handlers.h147
-rw-r--r--kftpgrabber/src/misc/customcommands/manager.cpp211
-rw-r--r--kftpgrabber/src/misc/customcommands/manager.h118
-rw-r--r--kftpgrabber/src/misc/customcommands/parameterentrydialog.cpp131
-rw-r--r--kftpgrabber/src/misc/customcommands/parameterentrydialog.h79
-rw-r--r--kftpgrabber/src/misc/customcommands/responsedialog.cpp60
-rw-r--r--kftpgrabber/src/misc/customcommands/responsedialog.h65
-rw-r--r--kftpgrabber/src/misc/desencryptor.cpp119
-rw-r--r--kftpgrabber/src/misc/desencryptor.h71
-rw-r--r--kftpgrabber/src/misc/filter.cpp421
-rw-r--r--kftpgrabber/src/misc/filter.h506
-rw-r--r--kftpgrabber/src/misc/filterwidgethandler.cpp539
-rw-r--r--kftpgrabber/src/misc/filterwidgethandler.h279
-rw-r--r--kftpgrabber/src/misc/interfaces/Makefile.am15
-rw-r--r--kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.cpp55
-rw-r--r--kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.desktop29
-rw-r--r--kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.h83
-rw-r--r--kftpgrabber/src/misc/kftpapi.cpp81
-rw-r--r--kftpgrabber/src/misc/kftpapi.h73
-rw-r--r--kftpgrabber/src/misc/kftpgrabber.kcfg319
-rw-r--r--kftpgrabber/src/misc/kftpotpgenerator.cpp470
-rw-r--r--kftpgrabber/src/misc/kftpotpgenerator.h75
-rw-r--r--kftpgrabber/src/misc/kftppluginmanager.cpp76
-rw-r--r--kftpgrabber/src/misc/kftppluginmanager.h82
-rw-r--r--kftpgrabber/src/misc/kftpwalletconnection.cpp166
-rw-r--r--kftpgrabber/src/misc/kftpwalletconnection.h68
-rw-r--r--kftpgrabber/src/misc/kftpzeroconf.cpp67
-rw-r--r--kftpgrabber/src/misc/kftpzeroconf.h70
-rw-r--r--kftpgrabber/src/misc/libs/Makefile.am3
-rw-r--r--kftpgrabber/src/misc/libs/ssh/Makefile.am8
-rw-r--r--kftpgrabber/src/misc/libs/ssh/auth.c597
-rw-r--r--kftpgrabber/src/misc/libs/ssh/base64.c210
-rw-r--r--kftpgrabber/src/misc/libs/ssh/buffer.c161
-rw-r--r--kftpgrabber/src/misc/libs/ssh/channels.c691
-rw-r--r--kftpgrabber/src/misc/libs/ssh/client.c261
-rw-r--r--kftpgrabber/src/misc/libs/ssh/connect.c295
-rw-r--r--kftpgrabber/src/misc/libs/ssh/crypt.c100
-rw-r--r--kftpgrabber/src/misc/libs/ssh/crypto.h47
-rw-r--r--kftpgrabber/src/misc/libs/ssh/dh.c411
-rw-r--r--kftpgrabber/src/misc/libs/ssh/error.c67
-rw-r--r--kftpgrabber/src/misc/libs/ssh/gzip.c140
-rw-r--r--kftpgrabber/src/misc/libs/ssh/kex.c264
-rw-r--r--kftpgrabber/src/misc/libs/ssh/keyfiles.c341
-rw-r--r--kftpgrabber/src/misc/libs/ssh/keys.c353
-rw-r--r--kftpgrabber/src/misc/libs/ssh/libssh.h218
-rw-r--r--kftpgrabber/src/misc/libs/ssh/misc.c98
-rw-r--r--kftpgrabber/src/misc/libs/ssh/options.c341
-rw-r--r--kftpgrabber/src/misc/libs/ssh/packet.c303
-rw-r--r--kftpgrabber/src/misc/libs/ssh/priv.h384
-rw-r--r--kftpgrabber/src/misc/libs/ssh/sftp.c1289
-rw-r--r--kftpgrabber/src/misc/libs/ssh/sftp.h225
-rw-r--r--kftpgrabber/src/misc/libs/ssh/ssh2.h69
-rw-r--r--kftpgrabber/src/misc/libs/ssh/string.c65
-rw-r--r--kftpgrabber/src/misc/libs/ssh/wrapper.c241
-rw-r--r--kftpgrabber/src/misc/misc.cpp193
-rw-r--r--kftpgrabber/src/misc/misc.h74
-rw-r--r--kftpgrabber/src/misc/plugins/Makefile.am3
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/Makefile.am3
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/Makefile.am14
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.cpp166
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.h82
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportplugin_filezilla3.desktop38
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/gftp/Makefile.am14
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.cpp236
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.h83
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportplugin_gftp.desktop61
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/kftp/Makefile.am14
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.cpp118
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.h83
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportplugin_kftp.desktop57
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/Makefile.am14
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.cpp165
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.h80
-rw-r--r--kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportplugin_ncftp.desktop62
-rw-r--r--kftpgrabber/src/queuegroup.cpp128
-rw-r--r--kftpgrabber/src/queuegroup.h104
-rw-r--r--kftpgrabber/src/queueobject.cpp275
-rw-r--r--kftpgrabber/src/queueobject.h372
-rw-r--r--kftpgrabber/src/site.cpp99
-rw-r--r--kftpgrabber/src/site.h89
-rw-r--r--kftpgrabber/src/statistics.cpp89
-rw-r--r--kftpgrabber/src/statistics.h117
-rw-r--r--kftpgrabber/src/ui/Makefile.am16
-rw-r--r--kftpgrabber/src/ui/bookmark_properties.ui620
-rw-r--r--kftpgrabber/src/ui/checksum_verifier.ui340
-rw-r--r--kftpgrabber/src/ui/config_display.ui137
-rw-r--r--kftpgrabber/src/ui/config_filters.ui137
-rw-r--r--kftpgrabber/src/ui/config_general.ui440
-rw-r--r--kftpgrabber/src/ui/config_log.ui355
-rw-r--r--kftpgrabber/src/ui/config_transfers.ui734
-rw-r--r--kftpgrabber/src/ui/foobar.cpp19
-rw-r--r--kftpgrabber/src/ui/foobar.h24
-rw-r--r--kftpgrabber/src/ui/kftpbookmarkeditortlswidget.ui173
-rw-r--r--kftpgrabber/src/ui/kftpbookmarkimportlayout.ui280
-rw-r--r--kftpgrabber/src/ui/kftpfilteraddpatternlayout.ui125
-rw-r--r--kftpgrabber/src/ui/kftpqueueeditorlayout.ui327
-rw-r--r--kftpgrabber/src/ui/kftpquickconnectlayout.ui538
-rw-r--r--kftpgrabber/src/ui/kftpsearchlayout.ui245
-rw-r--r--kftpgrabber/src/widgets/Makefile.am28
-rw-r--r--kftpgrabber/src/widgets/balloon.cpp145
-rw-r--r--kftpgrabber/src/widgets/balloon.h86
-rw-r--r--kftpgrabber/src/widgets/bookmarks/Makefile.am13
-rw-r--r--kftpgrabber/src/widgets/bookmarks/editor.cpp492
-rw-r--r--kftpgrabber/src/widgets/bookmarks/editor.h133
-rw-r--r--kftpgrabber/src/widgets/bookmarks/editortls.cpp115
-rw-r--r--kftpgrabber/src/widgets/bookmarks/editortls.h85
-rw-r--r--kftpgrabber/src/widgets/bookmarks/importwizard.cpp167
-rw-r--r--kftpgrabber/src/widgets/bookmarks/importwizard.h85
-rw-r--r--kftpgrabber/src/widgets/bookmarks/listview.cpp532
-rw-r--r--kftpgrabber/src/widgets/bookmarks/listview.h193
-rw-r--r--kftpgrabber/src/widgets/bookmarks/sidebar.cpp158
-rw-r--r--kftpgrabber/src/widgets/bookmarks/sidebar.h88
-rw-r--r--kftpgrabber/src/widgets/browser/Makefile.am13
-rw-r--r--kftpgrabber/src/widgets/browser/actions.cpp764
-rw-r--r--kftpgrabber/src/widgets/browser/actions.h189
-rw-r--r--kftpgrabber/src/widgets/browser/detailsview.cpp596
-rw-r--r--kftpgrabber/src/widgets/browser/detailsview.h374
-rw-r--r--kftpgrabber/src/widgets/browser/dirlister.cpp169
-rw-r--r--kftpgrabber/src/widgets/browser/dirlister.h157
-rw-r--r--kftpgrabber/src/widgets/browser/filterwidget.cpp118
-rw-r--r--kftpgrabber/src/widgets/browser/filterwidget.h97
-rw-r--r--kftpgrabber/src/widgets/browser/locationnavigator.cpp173
-rw-r--r--kftpgrabber/src/widgets/browser/locationnavigator.h210
-rw-r--r--kftpgrabber/src/widgets/browser/propsplugin.cpp360
-rw-r--r--kftpgrabber/src/widgets/browser/propsplugin.h98
-rw-r--r--kftpgrabber/src/widgets/browser/treeview.cpp520
-rw-r--r--kftpgrabber/src/widgets/browser/treeview.h189
-rw-r--r--kftpgrabber/src/widgets/browser/view.cpp456
-rw-r--r--kftpgrabber/src/widgets/browser/view.h258
-rw-r--r--kftpgrabber/src/widgets/configdialog.cpp187
-rw-r--r--kftpgrabber/src/widgets/configdialog.h71
-rw-r--r--kftpgrabber/src/widgets/configfilter.cpp137
-rw-r--r--kftpgrabber/src/widgets/configfilter.h79
-rw-r--r--kftpgrabber/src/widgets/failedtransfers.cpp213
-rw-r--r--kftpgrabber/src/widgets/failedtransfers.h111
-rw-r--r--kftpgrabber/src/widgets/filtereditor.cpp625
-rw-r--r--kftpgrabber/src/widgets/filtereditor.h375
-rw-r--r--kftpgrabber/src/widgets/kftpfilteraddpatternlayout.cpp92
-rw-r--r--kftpgrabber/src/widgets/kftpfiltereditorlayout.cpp115
-rw-r--r--kftpgrabber/src/widgets/kftpselectserverdialog.cpp79
-rw-r--r--kftpgrabber/src/widgets/kftpselectserverdialog.h70
-rw-r--r--kftpgrabber/src/widgets/kftpserverlineedit.cpp105
-rw-r--r--kftpgrabber/src/widgets/kftpserverlineedit.h71
-rw-r--r--kftpgrabber/src/widgets/kftptabwidget.cpp53
-rw-r--r--kftpgrabber/src/widgets/kftptabwidget.h60
-rw-r--r--kftpgrabber/src/widgets/kftpzeroconflistview.cpp98
-rw-r--r--kftpgrabber/src/widgets/kftpzeroconflistview.h56
-rw-r--r--kftpgrabber/src/widgets/listview.cpp85
-rw-r--r--kftpgrabber/src/widgets/listview.h63
-rw-r--r--kftpgrabber/src/widgets/listviewitem.cpp96
-rw-r--r--kftpgrabber/src/widgets/listviewitem.h71
-rw-r--r--kftpgrabber/src/widgets/logview.cpp143
-rw-r--r--kftpgrabber/src/widgets/logview.h73
-rw-r--r--kftpgrabber/src/widgets/multitabbar.cpp1160
-rw-r--r--kftpgrabber/src/widgets/multitabbar.h281
-rw-r--r--kftpgrabber/src/widgets/multitabbar_p.h85
-rw-r--r--kftpgrabber/src/widgets/overlaywidget.cpp83
-rw-r--r--kftpgrabber/src/widgets/overlaywidget.h60
-rw-r--r--kftpgrabber/src/widgets/popupmessage.cpp314
-rw-r--r--kftpgrabber/src/widgets/popupmessage.h113
-rw-r--r--kftpgrabber/src/widgets/queueview/Makefile.am13
-rw-r--r--kftpgrabber/src/widgets/queueview/queueeditor.cpp323
-rw-r--r--kftpgrabber/src/widgets/queueview/queueeditor.h82
-rw-r--r--kftpgrabber/src/widgets/queueview/queueview.cpp888
-rw-r--r--kftpgrabber/src/widgets/queueview/queueview.h296
-rw-r--r--kftpgrabber/src/widgets/queueview/threadview.cpp202
-rw-r--r--kftpgrabber/src/widgets/queueview/threadview.h93
-rw-r--r--kftpgrabber/src/widgets/quickconnect.cpp476
-rw-r--r--kftpgrabber/src/widgets/quickconnect.h162
-rw-r--r--kftpgrabber/src/widgets/searchdialog.cpp165
-rw-r--r--kftpgrabber/src/widgets/searchdialog.h85
-rw-r--r--kftpgrabber/src/widgets/sidebar.cpp393
-rw-r--r--kftpgrabber/src/widgets/sidebar.h157
-rw-r--r--kftpgrabber/src/widgets/systemtray.cpp123
-rw-r--r--kftpgrabber/src/widgets/systemtray.h93
-rw-r--r--kftpgrabber/src/widgets/trafficgraph.cpp628
-rw-r--r--kftpgrabber/src/widgets/trafficgraph.h155
-rw-r--r--kftpgrabber/src/widgets/verifier.cpp148
-rw-r--r--kftpgrabber/src/widgets/verifier.h74
-rw-r--r--kftpgrabber/src/widgets/widgetlister.cpp172
-rw-r--r--kftpgrabber/src/widgets/widgetlister.h177
260 files changed, 55594 insertions, 0 deletions
diff --git a/kftpgrabber/COPYING-DOCS b/kftpgrabber/COPYING-DOCS
new file mode 100644
index 0000000..4a0fe1c
--- /dev/null
+++ b/kftpgrabber/COPYING-DOCS
@@ -0,0 +1,397 @@
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
diff --git a/kftpgrabber/LICENSE.OpenSSL b/kftpgrabber/LICENSE.OpenSSL
new file mode 100644
index 0000000..4027788
--- /dev/null
+++ b/kftpgrabber/LICENSE.OpenSSL
@@ -0,0 +1,127 @@
+
+ LICENSE ISSUES
+ ==============
+
+ The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+ the OpenSSL License and the original SSLeay license apply to the toolkit.
+ See below for the actual license texts. Actually both licenses are BSD-style
+ Open Source licenses. In case of any license issues related to OpenSSL
+ please contact openssl-core@openssl.org.
+
+ OpenSSL License
+ ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2004 The OpenSSL Project. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
diff --git a/kftpgrabber/Makefile.am b/kftpgrabber/Makefile.am
new file mode 100644
index 0000000..35c85b3
--- /dev/null
+++ b/kftpgrabber/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = src
+EXTRA_DIST = AUTHORS COPYING ChangeLog INSTALL README TODO
+
+messages: rc.cpp
+ $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui" -o -name "*.kcfg"` > rc.cpp
+ LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name *.cxx -o -name \*.ecpp -o -name \*.C`; \
+ if test -n "$$LIST"; then \
+ $(XGETTEXT) $$LIST -o $(podir)/kftpgrabber.pot; \
+ fi
+ rm -f rc.cpp
+
+
diff --git a/kftpgrabber/NEWS b/kftpgrabber/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kftpgrabber/NEWS
diff --git a/kftpgrabber/Roadmap b/kftpgrabber/Roadmap
new file mode 100644
index 0000000..5f6f0cf
--- /dev/null
+++ b/kftpgrabber/Roadmap
@@ -0,0 +1,33 @@
+PRIORITY
+**********************************************************
+- Fix slow SSL data connections in the new engine. - Kostko
+
+VERSION 0.8
+**********************************************************
+- Keep alive (NOOP). - someone
+
+- Drag drop changing position in queue. After server as top item have been done. - anyone(tobias if no one does it first).
+
+- Tooltips. - Tobias/first come.
+
+- Auto save queue on folder complete. (unless someone disagrees with this idea - tobias). - anybody.
+
+- Handle bookmark file conflicts properly. - ???.
+
+- Documentation (should be docbook, see http://quality.kde.org/develop/howto/howtodocs.php) for [priority, view/edit, FXP speed, and what else tobias didn't comment] - tobias(well.. DOH).
+
+- Unify all the filtering options (highlighting, priority lists, ASCII extensions) by creating a filtering API. A Thunderbird-like filtering dialog should be used for user configuration.
+
+UNDECIDED:
+**********************************************************
+- Kicker applet showing threads, and speed.(unless someone disagrees with this idea - tobias)
+
+- Split queue/logs (or rather.. any two(+) of the tabs).
+
+- Bookmark directories on server.
+
+- Mirror enter directories with alt+dbl click.. if directory names are similar in both panes.
+
+- Open an ssh window to the connected window.. and automagicly log in (how?.. i'm not sure i see the need)
+
+- Reconnect and do cd after disconnect (on timeout).
diff --git a/kftpgrabber/configure.in.in b/kftpgrabber/configure.in.in
new file mode 100644
index 0000000..f8aaff2
--- /dev/null
+++ b/kftpgrabber/configure.in.in
@@ -0,0 +1,110 @@
+#MIN_CONFIG(3.2.0)
+
+AM_INIT_AUTOMAKE(kftpgrabber, 0.8.0)
+
+dnl Check for the 3.4 kde version (for kdnssd)
+AC_DEFUN([CHECK_KDNSSD],
+[
+ AC_MSG_CHECKING(for KDNSSD support)
+ AC_CACHE_VAL(ac_cv_kdnssd,
+ [
+ AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+
+ save_CXXFLAGS="$CXXFLAGS"
+
+ CXXFLAGS="$CXXFLAGS $all_includes"
+
+ AC_TRY_LINK([
+ #include <kdeversion.h>
+ ],
+ [
+ #if ! KDE_IS_VERSION(3,4,0)
+ KDE_choke me
+ #endif
+ ],
+ ac_cv_kdnssd=yes,
+ ac_cv_kdnssd=no
+ )
+ CXXFLAGS="$save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ if test "$ac_cv_kdnssd" = "yes"; then
+ AC_MSG_RESULT(yes)
+ LIB_KDNSSD="-lkdnssd"
+ else
+ AC_MSG_RESULT(no)
+ LIB_KDNSSD=""
+ fi
+
+ AC_SUBST(LIB_KDNSSD)
+])
+
+
+dnl Check for the correct kde version
+AC_DEFUN([CHECK_KDEVERSION],
+[
+ AC_MSG_CHECKING(for KDE >= 3.3)
+ AC_CACHE_VAL(ac_cv_kde33,
+ [
+ AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+
+ save_CXXFLAGS="$CXXFLAGS"
+
+ CXXFLAGS="$CXXFLAGS $all_includes"
+
+ AC_TRY_LINK([
+ #include <kdeversion.h>
+ ],
+ [
+ #if ! KDE_IS_VERSION(3,3,0)
+ KDE_choke me
+ #endif
+ ],
+ ac_cv_kde33=yes,
+ ac_cv_kde33=no
+ )
+ CXXFLAGS="$save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ if test "$ac_cv_kde33" = "yes"; then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+
+ echo ""
+ echo " * You don't have KDE 3.3 or later installed on your system."
+ echo " * KFTPGrabber requires at least KDE 3.3 or later."
+ echo ""
+ exit 1
+ fi
+])
+
+dnl Stuff required for libssh
+AC_CHECK_LIB([resolv],[gethostbyname])
+AC_CHECK_LIB([nsl],[gethostbyname])
+
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h \
+sys/time.h termios.h unistd.h openssl/aes.h openssl/blowfish.h zlib.h \
+sys/poll.h ])
+
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_REALLOC
+AC_FUNC_SELECT_ARGTYPES
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([endpwent gethostbyaddr gethostbyname getpass memmove memset \
+ select socket strchr strdup strerror strstr poll])
+
+dnl Check for some stuff
+KDE_CHECK_SSL
+CHECK_KDEVERSION
+CHECK_KDNSSD
+
+AC_C_BIGENDIAN
+AC_CHECK_KDEMAXPATHLEN
diff --git a/kftpgrabber/src/Makefile.am b/kftpgrabber/src/Makefile.am
new file mode 100644
index 0000000..9464ee5
--- /dev/null
+++ b/kftpgrabber/src/Makefile.am
@@ -0,0 +1,59 @@
+# set the include path for X, qt and KDE
+INCLUDES = -I$(srcdir)/misc \
+ -I$(srcdir)/ui -I./ui \
+ -I$(srcdir)/widgets \
+ -I$(srcdir)/widgets/browser \
+ -I$(srcdir)/widgets/filtereditor \
+ -I$(srcdir)/widgets/queueview \
+ $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = checksumverifier.h kftpbookmarks.h kftpqueue.h \
+ kftpbookmarks.h mainwindow.h kftpqueue.h mainactions.h kftpbookmarkaction.h \
+ kftpqueueprocessor.h kftpsession.h kftpqueueconverter.h kftptransfer.h \
+ kftptransferfile.h kftptransferdir.h kftpfileexistsactions.h \
+ statistics.h site.h queueobject.h queuegroup.h \
+ directoryscanner.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+KDE_ICON = kftpgrabber
+
+#########################################################################
+# APPLICATION SECTION
+#########################################################################
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+bin_PROGRAMS = kftpgrabber
+
+# the application source, library search path, and link libraries
+kftpgrabber_SOURCES = main.cpp mainwindow.cpp checksumverifier.cpp \
+ kftpbookmarks.cpp kftpqueue.cpp mainactions.cpp kftpbookmarkaction.cpp \
+ kftpqueueprocessor.cpp kftpsession.cpp kftpqueueconverter.cpp kftptransfer.cpp \
+ kftptransferfile.cpp kftptransferdir.cpp \
+ kftpfileexistsactions.cpp statistics.cpp site.cpp queueobject.cpp \
+ queuegroup.cpp directoryscanner.cpp
+kftpgrabber_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+kftpgrabber_LDADD = \
+ widgets/bookmarks/libbookmarkwidgets.a \
+ widgets/browser/libbrowser.a \
+ widgets/queueview/libqueueviewwidget.a \
+ widgets/libwidgets.a \
+ engine/libengine.a \
+ misc/libkftpgrabbercore.a \
+ misc/customcommands/libcustomcommands.a \
+ ui/libui.a \
+ misc/interfaces/libkftpinterfaces.la \
+ misc/libs/ssh/libssh.a \
+ $(LIB_KDNSSD) -lkwalletclient $(LIBSSL) $(LIB_KPARTS) \
+ $(LIB_KIO) $(LIB_KDEUI)
+
+# this is where the desktop file will go
+xdg_apps_DATA = kftpgrabber.desktop
+
+# this is where the shell's XML-GUI resource file goes
+shellrcdir = $(kde_datadir)/kftpgrabber
+shellrc_DATA = kftpgrabberui.rc kftpgrabber-logo.png kftpgrabber-bi-wizard.png
+
+SUBDIRS = misc engine ui widgets
diff --git a/kftpgrabber/src/checksumverifier.cpp b/kftpgrabber/src/checksumverifier.cpp
new file mode 100644
index 0000000..022e79c
--- /dev/null
+++ b/kftpgrabber/src/checksumverifier.cpp
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "checksumverifier.h"
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qapplication.h>
+
+#include <stdio.h>
+
+#include <kmdcodec.h>
+#include <kurl.h>
+
+namespace KFTPCore {
+
+ChecksumVerifierThread::ChecksumVerifierThread(ChecksumVerifier *verifier)
+ : QThread(),
+ m_verifier(verifier)
+{
+}
+
+void ChecksumVerifierThread::run()
+{
+ switch (m_verifier->m_type) {
+ case ChecksumVerifier::CheckMd5: break;
+ case ChecksumVerifier::CheckSfv: checkSFV(m_verifier->m_filename); break;
+ }
+}
+
+
+void ChecksumVerifierThread::checkSFV(const QString &sfvfile, const QString &fileToCheck)
+{
+ QString fileDir = KURL(sfvfile).directory(false);
+ QDir fileSystem;
+ QFile file(sfvfile);
+ if (!file.open(IO_ReadOnly)) {
+ // Dispatch the event that we have failed the verification
+ ChecksumVerifierThreadEvent *e = new ChecksumVerifierThreadEvent(2);
+ qApp->postEvent(m_verifier, e);
+ return;
+ }
+
+ QValueList<QPair<QString, QString> > fileList;
+ while (!file.atEnd()) {
+ QString line;
+ file.readLine(line, 1024);
+ line = line.simplifyWhiteSpace();
+
+ // Check if the line is a comment or a blank line (<3 chars)
+ if (line[0] == ';' || line.length() < 3)
+ continue;
+
+ // filename.r00 C46D96FF
+ QString fileName = line.section(' ', 0, 0);
+ QString checksum = line.section(' ', 1, 1);
+ checksum = checksum.lower();
+
+ if (checksum.length() != 8) {
+ // Dispatch the event that we have failed the verification
+ ChecksumVerifierThreadEvent *e = new ChecksumVerifierThreadEvent(2);
+ qApp->postEvent(m_verifier, e);
+ file.close();
+ return;
+ }
+
+ if (fileToCheck != QString::null && fileName != fileToCheck)
+ continue;
+
+ fileList.append(QPair<QString, QString>(fileName, checksum));
+ }
+ file.close();
+
+ // Dispatch the event that we have the list
+ ChecksumVerifierThreadEvent *e = new ChecksumVerifierThreadEvent(fileList);
+ qApp->postEvent(m_verifier, e);
+
+ // Check the obtained checksums
+ int done = 0;
+ for (QValueList<QPair<QString, QString> >::iterator i = fileList.begin(); i != fileList.end(); ++i) {
+ if (!fileSystem.exists(fileDir + (*i).first)) {
+ ChecksumVerifierThreadEvent *e = new ChecksumVerifierThreadEvent((*i).first, ChecksumVerifier::NotFound);
+ qApp->postEvent(m_verifier, e);
+
+ e = new ChecksumVerifierThreadEvent(3, (++done * 100) / fileList.count());
+ qApp->postEvent(m_verifier, e);
+ continue;
+ }
+
+ long l_fileCRC = getFileCRC(QString(fileDir + (*i).first).ascii());
+ QString fileCRC;
+ fileCRC.sprintf("%08lX", l_fileCRC);
+ fileCRC = fileCRC.lower();
+
+ ChecksumVerifierThreadEvent *e = new ChecksumVerifierThreadEvent((*i).first, fileCRC == (*i).second ? ChecksumVerifier::Ok : ChecksumVerifier::Error);
+ qApp->postEvent(m_verifier, e);
+
+ e = new ChecksumVerifierThreadEvent(3, (++done * 100) / fileList.count());
+ qApp->postEvent(m_verifier, e);
+ }
+}
+
+inline long ChecksumVerifierThread::UpdateCRC(register unsigned long CRC, register char *buffer, register long count)
+{
+ unsigned long CRCTABLE[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+
+ if (buffer && count) {
+ do {
+ CRC =
+ ((CRC >> 8) & 0xFFFFFF) ^
+ CRCTABLE[(unsigned char)((CRC & 0xff) ^
+ *buffer++)];
+ }
+ while (--count);
+
+ }
+ return CRC;
+}
+
+long ChecksumVerifierThread::getFileCRC(const char *filename)
+{
+ register unsigned long crc = 0xffffffff;
+ FILE *f;
+ long totalread = 0;
+ long localread;
+
+ /*
+ * Note: different buffer sizes may result in noticable
+ * different performance depending on system, so feel
+ * free to modify.
+ */
+#define BUFFERSIZE 65536*16
+ char buffer[BUFFERSIZE];
+
+ if ((f = fopen(filename, "rb")) != NULL) {
+ do {
+ if ((localread = fread(buffer, 1, BUFFERSIZE, f))) {
+ crc = UpdateCRC(crc, buffer, localread);
+ totalread = totalread + localread;
+ }
+ }
+ while (localread > 0);
+ fclose(f);
+
+ crc = crc ^ 0xffffffff;
+ } else {
+ return -1;
+ }
+
+ return crc;
+#undef BUFFERSIZE
+}
+
+ChecksumVerifier::ChecksumVerifier(const QString &filename, Type type)
+ : QObject(),
+ m_filename(filename),
+ m_type(type),
+ m_thread(0)
+{
+}
+
+ChecksumVerifier::~ChecksumVerifier()
+{
+ if (m_thread) {
+ m_thread->terminate();
+ m_thread->wait();
+
+ delete m_thread;
+ }
+}
+
+void ChecksumVerifier::verify()
+{
+ m_thread = new ChecksumVerifierThread(this);
+ m_thread->start();
+}
+
+void ChecksumVerifier::customEvent(QCustomEvent *e)
+{
+ if (e->type() == CV_THR_EVENT_ID) {
+ ChecksumVerifierThreadEvent *ev = static_cast<ChecksumVerifierThreadEvent*>(e);
+
+ switch (ev->m_type) {
+ case 0: emit fileDone(ev->m_filename, ev->m_result); break;
+ case 1: emit fileList(ev->m_list); break;
+ case 2: emit error(); break;
+ case 3: emit progress(ev->m_progress); break;
+ }
+ }
+}
+
+}
+
+#include "checksumverifier.moc"
+
diff --git a/kftpgrabber/src/checksumverifier.h b/kftpgrabber/src/checksumverifier.h
new file mode 100644
index 0000000..c2a5e14
--- /dev/null
+++ b/kftpgrabber/src/checksumverifier.h
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPCORE_CHECKSUMVERIFIER_H
+#define KFTPCORE_CHECKSUMVERIFIER_H
+
+#include <qstring.h>
+#include <qthread.h>
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qpair.h>
+
+namespace KFTPCore {
+
+class ChecksumVerifierThread;
+
+/**
+ * @author Jernej Kos
+ */
+class ChecksumVerifier : public QObject
+{
+friend class ChecksumVerifierThread;
+Q_OBJECT
+public:
+ enum Type {
+ CheckMd5,
+ CheckSfv
+ };
+
+ enum Result {
+ Ok,
+ NotFound,
+ Error
+ };
+
+ ChecksumVerifier(const QString &filename, Type type = CheckSfv);
+ ~ChecksumVerifier();
+
+ void verify();
+protected:
+ void customEvent(QCustomEvent *e);
+private:
+ QString m_filename;
+ Type m_type;
+
+ ChecksumVerifierThread *m_thread;
+signals:
+ void progress(int percent);
+ void fileDone(const QString &filename, KFTPCore::ChecksumVerifier::Result result);
+ void fileList(QValueList<QPair<QString, QString> > list);
+ void error();
+};
+
+#define CV_THR_EVENT_ID 65300
+
+class ChecksumVerifierThreadEvent : public QCustomEvent
+{
+friend class ChecksumVerifier;
+public:
+ ChecksumVerifierThreadEvent(const QString &filename, ChecksumVerifier::Result result)
+ : QCustomEvent((QEvent::Type) CV_THR_EVENT_ID),
+ m_type(0),
+ m_filename(filename),
+ m_result(result)
+ {}
+
+ ChecksumVerifierThreadEvent(QValueList<QPair<QString, QString> > list)
+ : QCustomEvent((QEvent::Type) CV_THR_EVENT_ID),
+ m_type(1),
+ m_list(list)
+ {}
+
+ ChecksumVerifierThreadEvent(int type, int progress = 0)
+ : QCustomEvent((QEvent::Type) CV_THR_EVENT_ID),
+ m_type(type),
+ m_progress(progress)
+ {}
+private:
+ int m_type;
+ int m_progress;
+ QString m_filename;
+ ChecksumVerifier::Result m_result;
+ QValueList<QPair<QString, QString> > m_list;
+};
+
+class ChecksumVerifierThread : public QThread
+{
+public:
+ ChecksumVerifierThread(ChecksumVerifier *verifier);
+protected:
+ void run();
+private:
+ ChecksumVerifier *m_verifier;
+
+ void checkSFV(const QString &sfvfile, const QString &fileToCheck = QString::null);
+
+ static inline long UpdateCRC(register unsigned long CRC, register char *buffer, register long count);
+ static long getFileCRC(const char *filename);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/directoryscanner.cpp b/kftpgrabber/src/directoryscanner.cpp
new file mode 100644
index 0000000..5adaf45
--- /dev/null
+++ b/kftpgrabber/src/directoryscanner.cpp
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "directoryscanner.h"
+#include "kftpqueue.h"
+
+#include "misc/config.h"
+#include "misc/filter.h"
+
+#include <kfileitem.h>
+
+#include <qapplication.h>
+#include <qdir.h>
+
+using namespace KFTPQueue;
+using namespace KFTPCore::Filter;
+using namespace KFTPEngine;
+
+DirectoryScanner::DirectoryScanner(Transfer *transfer)
+{
+ // Lock the transfer
+ transfer->lock();
+
+ // Construct a new thread and run it
+ ScannerThread *thread = new ScannerThread(this, transfer);
+ thread->start();
+}
+
+void DirectoryScanner::customEvent(QCustomEvent *e)
+{
+ if (e->type() == QS_THR_EVENT_ID) {
+ KFTPQueue::Transfer *transfer = static_cast<KFTPQueue::Transfer*>(e->data());
+ transfer->unlock();
+ emit completed();
+
+ // Destroy thiy object
+ delete this;
+ }
+}
+
+DirectoryScanner::ScannerThread::ScannerThread(QObject *parent, Transfer *item)
+ : QThread(),
+ m_parent(parent),
+ m_item(item)
+{
+}
+
+void DirectoryScanner::ScannerThread::run()
+{
+ // First scan the folder
+ scanFolder(m_item);
+
+ // We are done, post event to notify the GUI
+ ScannerThreadEvent *e = new ScannerThreadEvent(m_item);
+ qApp->postEvent(m_parent, e);
+}
+
+void DirectoryScanner::ScannerThread::scanFolder(Transfer *parent)
+{
+ QDir fs(parent->getSourceUrl().path());
+ fs.setFilter(QDir::Readable | QDir::Hidden | QDir::All);
+
+ const QFileInfoList *p_list = fs.entryInfoList();
+ QFileInfoListIterator i(*p_list);
+ QFileInfo *file;
+
+ QValueList<DirectoryEntry> list;
+
+ while ((file = i.current()) != 0) {
+ ++i;
+
+ if (file->fileName() == "." || file->fileName() == "..")
+ continue;
+
+ KURL sourceUrl;
+ sourceUrl.setPath(file->absFilePath());
+
+ // This is needed, since QFileInfo works with uint for the filesize
+ filesize_t realSize = KFileItem(KFileItem::Unknown, KFileItem::Unknown, sourceUrl, true).size();
+
+ // Check if we should skip this entry
+ const ActionChain *actionChain = Filters::self()->process(sourceUrl, realSize, file->isDir());
+
+ if (actionChain && actionChain->getAction(Action::Skip))
+ continue;
+
+ DirectoryEntry entry;
+ entry.setFilename(file->fileName());
+ entry.setType(file->isDir() ? 'd' : 'f');
+ entry.setSize(realSize);
+
+ list.append(entry);
+ }
+
+ // Sort by priority
+ qHeapSort(list);
+
+ QValueList<DirectoryEntry>::ConstIterator listEnd = list.end();
+ for (QValueList<DirectoryEntry>::ConstIterator j = list.begin(); j != listEnd; ++j) {
+ // Spawn transfer
+ KURL destUrlBase = parent->getDestUrl();
+ KURL sourceUrlBase = parent->getSourceUrl();
+
+ destUrlBase.addPath((*j).filename());
+ sourceUrlBase.addPath((*j).filename());
+
+ if ((*j).isDirectory()) {
+ // Directory
+ qApp->lock();
+ KFTPQueue::TransferDir *transfer = new KFTPQueue::TransferDir(parent);
+ transfer->setSourceUrl(sourceUrlBase);
+ transfer->setDestUrl(destUrlBase);
+ transfer->addSize((*j).size());
+ transfer->setTransferType(parent->getTransferType());
+ transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
+
+ emit KFTPQueue::Manager::self()->newTransfer(transfer);
+ qApp->unlock();
+
+ // Call this function in recursion
+ scanFolder(transfer);
+
+ if (KFTPCore::Config::skipEmptyDirs() && !transfer->hasChildren()) {
+ qApp->lock();
+ KFTPQueue::Manager::self()->removeTransfer(transfer, false);
+ qApp->unlock();
+ }
+ } else {
+ // File
+ qApp->lock();
+ KFTPQueue::TransferFile *transfer = new KFTPQueue::TransferFile(parent);
+ transfer->setSourceUrl(sourceUrlBase);
+ transfer->setDestUrl(destUrlBase);
+ transfer->addSize((*j).size());
+ transfer->setTransferType(parent->getTransferType());
+ transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
+
+ emit KFTPQueue::Manager::self()->newTransfer(transfer);
+ qApp->unlock();
+ }
+ }
+}
+
+#include "directoryscanner.moc"
+
+
diff --git a/kftpgrabber/src/directoryscanner.h b/kftpgrabber/src/directoryscanner.h
new file mode 100644
index 0000000..68e05d0
--- /dev/null
+++ b/kftpgrabber/src/directoryscanner.h
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef DIRECTORYSCANNER_H
+#define DIRECTORYSCANNER_H
+
+#define QS_THR_EVENT_ID 65200
+
+#include <qobject.h>
+#include <qthread.h>
+
+namespace KFTPQueue {
+ class Transfer;
+ class Manager;
+}
+
+/**
+ * This class can be used to scan a local directory using a separate
+ * thread and create any needed child transfers.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class DirectoryScanner : public QObject {
+Q_OBJECT
+friend class KFTPQueue::Manager;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param transfer The transfer to scan
+ */
+ DirectoryScanner(KFTPQueue::Transfer *transfer);
+protected:
+ void customEvent(QCustomEvent *e);
+private:
+ /**
+ * An event posted by the scanner thread.
+ */
+ class ScannerThreadEvent : public QCustomEvent
+ {
+ public:
+ ScannerThreadEvent(void *data)
+ : QCustomEvent((QEvent::Type) QS_THR_EVENT_ID, data) {}
+ };
+
+ /**
+ * The actual thread that does the scanning.
+ */
+ class ScannerThread : public QThread {
+ public:
+ ScannerThread(QObject *parent, KFTPQueue::Transfer *item);
+ protected:
+ /**
+ * Thread entry point.
+ */
+ void run();
+ private:
+ QObject *m_parent;
+ KFTPQueue::Transfer *m_item;
+
+ /**
+ * A method to recursively scan a given folder.
+ */
+ void scanFolder(KFTPQueue::Transfer *parent);
+ };
+signals:
+ /**
+ * This signal is emitted when scanning complets. This object is automaticly
+ * destroyed immediately after the signal returns!
+ */
+ void completed();
+};
+
+#endif
diff --git a/kftpgrabber/src/engine/Makefile.am b/kftpgrabber/src/engine/Makefile.am
new file mode 100644
index 0000000..ad6adb1
--- /dev/null
+++ b/kftpgrabber/src/engine/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I.. -I$(srcdir)/.. \
+ -I../misc -I$(srcdir)/../misc \
+ $(all_includes)
+METASOURCES = AUTO
+noinst_LIBRARIES = libengine.a
+noinst_HEADERS = socket.h thread.h directorylisting.h commands.h event.h \
+ ftpsocket.h ftpdirectoryparser.h cache.h sftpsocket.h connectionretry.h \
+ speedlimiter.h ssl.h
+libengine_a_SOURCES = socket.cpp thread.cpp directorylisting.cpp commands.cpp \
+ event.cpp ftpsocket.cpp ftpdirectoryparser.cpp cache.cpp sftpsocket.cpp \
+ connectionretry.cpp speedlimiter.cpp ssl.cpp
diff --git a/kftpgrabber/src/engine/cache.cpp b/kftpgrabber/src/engine/cache.cpp
new file mode 100644
index 0000000..1b28dd6
--- /dev/null
+++ b/kftpgrabber/src/engine/cache.cpp
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "cache.h"
+#include "socket.h"
+
+#include <kstaticdeleter.h>
+
+namespace KFTPEngine {
+
+Cache *Cache::m_self = 0;
+static KStaticDeleter<Cache> staticCacheDeleter;
+
+Cache *Cache::self()
+{
+ if (!m_self) {
+ staticCacheDeleter.setObject(m_self, new Cache());
+ }
+
+ return m_self;
+}
+
+Cache::Cache()
+{
+}
+
+Cache::~Cache()
+{
+ if (m_self == this)
+ staticCacheDeleter.setObject(m_self, 0, false);
+}
+
+void Cache::addDirectory(KURL &url, DirectoryListing listing)
+{
+ url.adjustPath(-1);
+ m_listingCache[url] = listing;
+}
+
+void Cache::addDirectory(Socket *socket, DirectoryListing listing)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(socket->getCurrentDirectory());
+
+ addDirectory(url, listing);
+}
+
+void Cache::updateDirectoryEntry(Socket *socket, KURL &path, filesize_t filesize)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(path.directory());
+ url.adjustPath(-1);
+
+ if (m_listingCache.contains(url)) {
+ DirectoryListing listing = m_listingCache[url];
+ listing.updateEntry(path.fileName(), filesize);
+
+ m_listingCache.replace(url, listing);
+ }
+}
+
+void Cache::addPath(KURL &url, const QString &target)
+{
+ url.adjustPath(-1);
+ m_pathCache[url] = target;
+}
+
+void Cache::addPath(Socket *socket, const QString &target)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(socket->getCurrentDirectory());
+
+ addPath(url, target);
+}
+
+void Cache::invalidateEntry(KURL &url)
+{
+ url.adjustPath(-1);
+ m_listingCache.remove(url);
+}
+
+void Cache::invalidateEntry(Socket *socket, const QString &path)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(path);
+
+ invalidateEntry(url);
+}
+
+void Cache::invalidatePath(KURL &url)
+{
+ url.adjustPath(-1);
+ m_pathCache.remove(url);
+}
+
+void Cache::invalidatePath(Socket *socket, const QString &path)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(path);
+
+ invalidatePath(url);
+}
+
+DirectoryListing Cache::findCached(KURL &url)
+{
+ url.adjustPath(-1);
+
+ if (m_listingCache.contains(url))
+ return m_listingCache[url];
+
+ DirectoryListing invalid;
+ invalid.setValid(false);
+
+ return invalid;
+}
+
+DirectoryListing Cache::findCached(Socket *socket, const QString &path)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(path);
+
+ return findCached(url);
+}
+
+QString Cache::findCachedPath(KURL &url)
+{
+ url.adjustPath(-1);
+
+ if (m_pathCache.contains(url))
+ return m_pathCache[url];
+
+ return QString::null;
+}
+
+QString Cache::findCachedPath(Socket *socket, const QString &path)
+{
+ KURL url = socket->getCurrentUrl();
+ url.setPath(path);
+
+ return findCachedPath(url);
+}
+
+}
diff --git a/kftpgrabber/src/engine/cache.h b/kftpgrabber/src/engine/cache.h
new file mode 100644
index 0000000..7b6cf21
--- /dev/null
+++ b/kftpgrabber/src/engine/cache.h
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPENGINECACHE_H
+#define KFTPENGINECACHE_H
+
+#include <qmap.h>
+#include <kurl.h>
+
+#include "directorylisting.h"
+
+namespace KFTPEngine {
+
+class Socket;
+
+/**
+ * This class provides a cache of paths and directory listings to be used for
+ * faster operations.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class Cache {
+public:
+ static Cache *self();
+ ~Cache();
+
+ /**
+ * Cache a directory listing.
+ *
+ * @param url The listing url (including host information)
+ * @param listing The directory listing to cache
+ */
+ void addDirectory(KURL &url, DirectoryListing listing);
+
+ /**
+ * Cache a directory listing, extracting the host information from the
+ * socket and using the current directory path.
+ *
+ * @param socket The socket to extract the host info from
+ * @param listing The directory listing to cache
+ */
+ void addDirectory(Socket *socket, DirectoryListing listing);
+
+ /**
+ * Updates a single directory entry.
+ *
+ * @param socket The socket to extract the host info from
+ * @param path Entry location
+ * @param filesize New file size
+ */
+ void updateDirectoryEntry(Socket *socket, KURL &path, filesize_t filesize);
+
+ /**
+ * Cache path information.
+ *
+ * @param url The url (including host information)
+ * @param target Actual target directory
+ */
+ void addPath(KURL &url, const QString &target);
+
+ /**
+ * Cache path information, extracting the host information from the
+ * socket and using the current directory path.
+ *
+ * @param socket The socket to extract the host info from
+ * @param target Actual target directory
+ */
+ void addPath(Socket *socket, const QString &target);
+
+ /**
+ * Invalidate a cached entry.
+ *
+ * @param url Url of the entry
+ */
+ void invalidateEntry(KURL &url);
+
+ /**
+ * Invalidate a cached entry.
+ *
+ * @param socket The socket to extract the host info from
+ * @param path Path of the entry
+ */
+ void invalidateEntry(Socket *socket, const QString &path);
+
+ /**
+ * Invalidate a cached path.
+ *
+ * @param url Url of the entry
+ */
+ void invalidatePath(KURL &url);
+
+ /**
+ * Invalidate a cached path.
+ *
+ * @param socket The socket to extract the host info from
+ * @param path Path of the entry
+ */
+ void invalidatePath(Socket *socket, const QString &path);
+
+ /**
+ * Retrieve a cached directory listing.
+ *
+ * @param url Url of the entry
+ * @return A valid DirectoryListing if found, an empty DirectoryListing otherwise
+ */
+ DirectoryListing findCached(KURL &url);
+
+ /**
+ * Retrieve a cached directory listing.
+ *
+ * @param socket The socket to extract the host info from
+ * @param path Path of the entry
+ * @return A valid DirectoryListing if found, an empty DirectoryListing otherwise
+ */
+ DirectoryListing findCached(Socket *socket, const QString &path);
+
+ /**
+ * Retrieve a cached path.
+ *
+ * @param url Url of the entry
+ * @return A target path if found, QString::null otherwise
+ */
+ QString findCachedPath(KURL &url);
+
+ /**
+ * Retrieve a cached path.
+ *
+ * @param socket The socket to extract the host info from
+ * @param path Path of the entry
+ * @return A target path if found, QString::null otherwise
+ */
+ QString findCachedPath(Socket *socket, const QString &path);
+protected:
+ Cache();
+ static Cache *m_self;
+private:
+ QMap<KURL, DirectoryListing> m_listingCache;
+ QMap<KURL, QString> m_pathCache;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/commands.cpp b/kftpgrabber/src/engine/commands.cpp
new file mode 100644
index 0000000..5e0569f
--- /dev/null
+++ b/kftpgrabber/src/engine/commands.cpp
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "commands.h"
+
+namespace KFTPEngine {
+
+namespace Commands {
+
+Base::Base(Socket *socket, Type command)
+ : m_command(command),
+ m_socket(socket),
+ m_wakeupEvent(0),
+ m_processing(false),
+ m_autoDestruct(false),
+ m_clean(false)
+{
+}
+
+void Base::setProcessing(bool value)
+{
+ if (value)
+ m_processing++;
+ else
+ m_processing--;
+}
+
+void Base::autoDestruct(ResetCode code)
+{
+ m_autoDestruct = true;
+ m_resetCode = code;
+}
+
+void Base::wakeup(WakeupEvent *event)
+{
+ // The default implementation just calls process()
+ m_wakeupEvent = event;
+ process();
+ m_wakeupEvent = 0;
+}
+
+}
+
+}
+
diff --git a/kftpgrabber/src/engine/commands.h b/kftpgrabber/src/engine/commands.h
new file mode 100644
index 0000000..679c673
--- /dev/null
+++ b/kftpgrabber/src/engine/commands.h
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+#include "event.h"
+
+#define ENGINE_STANDARD_COMMAND_CONSTRUCTOR(class, type, cmd) public: \
+ class(type *socket) : Commands::Base(socket, Commands::cmd), currentState(None) {} \
+ private: \
+ State currentState; \
+ \
+ type *socket() {\
+ return static_cast<type*>(m_socket);\
+ }\
+ public:
+
+#define ENGINE_CANCELLATION_POINT { if (isDestructable()) \
+ return; }
+
+#define setupCommandClass(class) if (m_cmdData) \
+ delete m_cmdData; \
+ m_cmdData = new class(this);
+
+#define chainCommandClass(class) Commands::Base *_cmd = new class(socket()); \
+ socket()->addToCommandChain(_cmd); \
+ socket()->nextCommand(); \
+ return;
+
+#define activateCommandClass(class) if (m_cmdData) { \
+ Commands::Base *_cmd = new class(this); \
+ addToCommandChain(_cmd); \
+ nextCommand(); \
+ } else { \
+ m_cmdData = new class(this); \
+ m_cmdData->process(); \
+ }
+
+namespace KFTPEngine {
+
+class Socket;
+
+namespace Commands {
+
+enum Type {
+ CmdNone,
+ CmdNext,
+
+ // Actual commands
+ CmdConnect,
+ CmdConnectRetry,
+ CmdDisconnect,
+ CmdList,
+ CmdScan,
+ CmdGet,
+ CmdPut,
+ CmdDelete,
+ CmdRename,
+ CmdMkdir,
+ CmdChmod,
+ CmdRaw,
+ CmdFxp,
+ CmdKeepAlive
+};
+
+class Base {
+public:
+ Base(Socket *socket, Type type);
+
+ void setProcessing(bool value);
+ bool isProcessing() { return m_processing > 0; }
+
+ void autoDestruct(ResetCode code);
+ bool isDestructable() { return m_autoDestruct && !isProcessing(); }
+ ResetCode resetCode() { return m_resetCode; }
+
+ bool isClean() { return m_clean; }
+
+ Type command() { return m_command; }
+
+ bool isWakeup() { return m_wakeupEvent != 0; }
+ virtual void wakeup(WakeupEvent *event);
+ virtual void process() = 0;
+ virtual void cleanup() {}
+protected:
+ void markClean() { m_clean = true; }
+protected:
+ Type m_command;
+ Socket *m_socket;
+ WakeupEvent *m_wakeupEvent;
+
+ int m_processing;
+ bool m_autoDestruct;
+ ResetCode m_resetCode;
+ bool m_clean;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/connectionretry.cpp b/kftpgrabber/src/engine/connectionretry.cpp
new file mode 100644
index 0000000..e93a1b9
--- /dev/null
+++ b/kftpgrabber/src/engine/connectionretry.cpp
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "connectionretry.h"
+#include "socket.h"
+#include "thread.h"
+#include "event.h"
+
+#include <klocale.h>
+
+namespace KFTPEngine {
+
+ConnectionRetry::ConnectionRetry(Socket *socket)
+ : QObject(),
+ m_socket(socket),
+ m_delay(socket->getConfigInt("retry_delay")),
+ m_max(socket->getConfigInt("max_retries")),
+ m_iteration(0)
+{
+ m_timer = new QTimer(this);
+
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(slotShouldRetry()));
+ connect(m_socket->thread()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+}
+
+void ConnectionRetry::startRetry()
+{
+ if ((m_iteration++ >= m_max && m_max != 0) || m_delay < 1) {
+ abortRetry();
+ return;
+ }
+
+ m_socket->setCurrentCommand(Commands::CmdConnectRetry);
+ m_socket->emitEvent(Event::EventMessage, i18n("Waiting %1 seconds before reconnect...").arg(m_delay));
+ m_socket->emitEvent(Event::EventState, i18n("Waiting..."));
+
+ m_timer->start(1000 * m_delay, true);
+}
+
+void ConnectionRetry::slotShouldRetry()
+{
+ m_socket->setCurrentCommand(Commands::CmdNone);
+ if (m_max > 0)
+ m_socket->emitEvent(Event::EventMessage, i18n("Retrying connection (%1/%2)...").arg(m_iteration).arg(m_max));
+ else
+ m_socket->emitEvent(Event::EventMessage, i18n("Retrying connection...").arg(m_iteration).arg(m_max));
+
+ // Reconnect
+ Thread *thread = m_socket->thread();
+ thread->connect(m_socket->getCurrentUrl());
+}
+
+void ConnectionRetry::abortRetry()
+{
+ m_timer->stop();
+
+ // Disable retry so we avoid infinite loops
+ m_socket->setConfig("retry", 0);
+
+ m_socket->setCurrentCommand(Commands::CmdNone);
+ m_socket->emitEvent(Event::EventMessage, i18n("Retry aborted."));
+ m_socket->emitEvent(Event::EventState, i18n("Idle."));
+ m_socket->emitEvent(Event::EventReady);
+ m_socket->emitError(ConnectFailed);
+
+ // This object should be automagicly removed
+ QObject::deleteLater();
+}
+
+void ConnectionRetry::slotEngineEvent(KFTPEngine::Event *event)
+{
+ if (event->type() == Event::EventConnect) {
+ m_socket->emitEvent(Event::EventRetrySuccess);
+
+ // This object should be automagicly removed
+ QObject::deleteLater();
+ }
+}
+
+}
diff --git a/kftpgrabber/src/engine/connectionretry.h b/kftpgrabber/src/engine/connectionretry.h
new file mode 100644
index 0000000..59a351d
--- /dev/null
+++ b/kftpgrabber/src/engine/connectionretry.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPENGINECONNECTIONRETRY_H
+#define KFTPENGINECONNECTIONRETRY_H
+
+#include <qobject.h>
+#include <qtimer.h>
+
+namespace KFTPEngine {
+
+class Socket;
+class Event;
+
+/**
+ * This class will retry to reconnect to the currently set URL for the
+ * socket specified in constructor.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class ConnectionRetry : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Constructs a new ConnectionRetry class instance.
+ */
+ ConnectionRetry(Socket *socket);
+
+ /**
+ * Start the reconnect cycle.
+ */
+ void startRetry();
+
+ /**
+ * Abort the running reconnect cycle and schedule this object's
+ * destruction.
+ */
+ void abortRetry();
+private:
+ Socket *m_socket;
+ int m_delay;
+ int m_max;
+ int m_iteration;
+
+ QTimer *m_timer;
+private slots:
+ void slotShouldRetry();
+ void slotEngineEvent(KFTPEngine::Event *event);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/directorylisting.cpp b/kftpgrabber/src/engine/directorylisting.cpp
new file mode 100644
index 0000000..1647bb2
--- /dev/null
+++ b/kftpgrabber/src/engine/directorylisting.cpp
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "directorylisting.h"
+#include "misc/filter.h"
+
+#include <qdatetime.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kmimetype.h>
+
+#include <sys/stat.h>
+
+using namespace KFTPCore::Filter;
+using namespace KIO;
+
+namespace KFTPEngine {
+
+DirectoryEntry::DirectoryEntry()
+{
+}
+
+KIO::UDSEntry DirectoryEntry::toUdsEntry() const
+{
+ bool directory = m_type == 'd';
+ UDSAtom atom;
+ UDSEntry entry;
+
+ atom.m_uds = UDS_NAME;
+ atom.m_str = m_filename;
+ entry.append(atom);
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = m_size;
+ entry.append(atom);
+
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = m_time;
+ entry.append(atom);
+
+ atom.m_uds = UDS_USER;
+ atom.m_str = m_owner;
+ entry.append(atom);
+
+ atom.m_uds = UDS_GROUP;
+ atom.m_str = m_group;
+ entry.append(atom);
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = m_permissions;
+ entry.append(atom);
+
+ if (!m_link.isEmpty()) {
+ atom.m_uds = UDS_LINK_DEST;
+ atom.m_str = m_link;
+ entry.append(atom);
+
+ KMimeType::Ptr mime = KMimeType::findByURL(KURL("ftp://host/" + m_filename));
+ if (mime->name() == KMimeType::defaultMimeType()) {
+ atom.m_uds = UDS_GUESSED_MIME_TYPE;
+ atom.m_str = "inode/directory";
+ entry.append(atom);
+
+ directory = true;
+ }
+ }
+
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = directory ? S_IFDIR : S_IFREG;
+ entry.append(atom);
+
+ return entry;
+}
+
+QString DirectoryEntry::timeAsString()
+{
+ QDateTime dt;
+ dt.setTime_t(time());
+
+ return KGlobal::locale()->formatDateTime(dt);
+}
+
+bool DirectoryEntry::operator<(const DirectoryEntry &entry) const
+{
+ const Action *firstAction = Filters::self()->process(*this, Action::Priority);
+ const Action *secondAction = Filters::self()->process(entry, Action::Priority);
+
+ int priorityFirst = firstAction ? firstAction->value().toInt() : 0;
+ int prioritySecond = secondAction ? secondAction->value().toInt() : 0;
+
+ if (priorityFirst == prioritySecond) {
+ if (isDirectory() != entry.isDirectory())
+ return isDirectory();
+
+ return m_filename < entry.m_filename;
+ }
+
+ return priorityFirst > prioritySecond;
+}
+
+DirectoryTree::DirectoryTree(DirectoryEntry entry)
+ : m_entry(entry)
+{
+ m_directories.setAutoDelete(true);
+}
+
+void DirectoryTree::addFile(DirectoryEntry entry)
+{
+ m_files.append(entry);
+}
+
+DirectoryTree *DirectoryTree::addDirectory(DirectoryEntry entry)
+{
+ DirectoryTree *tree = new DirectoryTree(entry);
+ m_directories.append(tree);
+
+ return tree;
+}
+
+DirectoryListing::DirectoryListing(const KURL &path)
+ : m_valid(true),
+ m_path(path)
+{
+}
+
+DirectoryListing::~DirectoryListing()
+{
+ m_list.clear();
+}
+
+void DirectoryListing::addEntry(DirectoryEntry entry)
+{
+ m_list.append(entry);
+}
+
+void DirectoryListing::updateEntry(const QString &filename, ::filesize_t size)
+{
+ QValueList<DirectoryEntry>::iterator listEnd = m_list.end();
+ for (QValueList<DirectoryEntry>::iterator i = m_list.begin(); i != listEnd; i++) {
+ if ((*i).filename() == filename) {
+ (*i).setSize(size);
+ return;
+ }
+ }
+
+ // Entry not found, add one
+ DirectoryEntry entry;
+ entry.setFilename(filename);
+ entry.setSize(size);
+
+ addEntry(entry);
+}
+
+}
diff --git a/kftpgrabber/src/engine/directorylisting.h b/kftpgrabber/src/engine/directorylisting.h
new file mode 100644
index 0000000..b332d37
--- /dev/null
+++ b/kftpgrabber/src/engine/directorylisting.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPNETWORKDIRECTORYLISTING_H
+#define KFTPNETWORKDIRECTORYLISTING_H
+
+#include <kio/global.h>
+#include <kurl.h>
+
+#include <qvaluelist.h>
+#include <qptrlist.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+typedef unsigned long long int filesize_t;
+
+namespace KFTPEngine {
+
+class DirectoryEntry {
+public:
+ DirectoryEntry();
+
+ void setFilename(const QString &filename) { m_filename = filename; }
+ void setOwner(const QString &owner) { m_owner = owner; }
+ void setGroup(const QString &group) { m_group = group; }
+ void setLink(const QString &link) { m_link = link; }
+ void setPermissions(int permissions) { m_permissions = permissions; }
+ void setSize(filesize_t size) { m_size = size; }
+ void setType(char type) { m_type = type; }
+ void setTime(time_t time) { m_time = time; }
+
+ QString filename() const { return m_filename; }
+ QString owner() const { return m_owner; }
+ QString group() const { return m_group; }
+ QString link() const { return m_link; }
+ int permissions() const { return m_permissions; }
+ filesize_t size() const { return m_size; }
+ char type() const { return m_type; }
+ time_t time() const { return m_time; }
+ QString timeAsString();
+
+ bool isDirectory() const { return m_type == 'd'; }
+ bool isFile() const { return m_type == 'f'; }
+ bool isDevice() const { return m_type == 'c' || m_type == 'b'; }
+ bool isSymlink() const { return !m_link.isEmpty(); }
+
+ KIO::UDSEntry toUdsEntry() const;
+
+ struct tm timeStruct;
+
+ bool operator<(const DirectoryEntry &entry) const;
+private:
+ QString m_filename;
+ QString m_owner;
+ QString m_group;
+ QString m_link;
+
+ int m_permissions;
+ filesize_t m_size;
+ char m_type;
+ time_t m_time;
+};
+
+class DirectoryTree {
+public:
+ typedef QValueList<DirectoryEntry>::ConstIterator FileIterator;
+ typedef QPtrList<DirectoryTree>::ConstIterator DirIterator;
+
+ DirectoryTree() {}
+ DirectoryTree(DirectoryEntry entry);
+
+ void addFile(DirectoryEntry entry);
+ DirectoryTree *addDirectory(DirectoryEntry entry);
+
+ DirectoryEntry info() { return m_entry; }
+
+ QValueList<DirectoryEntry> *files() { return &m_files; }
+ QPtrList<DirectoryTree> *directories() { return &m_directories; }
+private:
+ DirectoryEntry m_entry;
+ QValueList<DirectoryEntry> m_files;
+ QPtrList<DirectoryTree> m_directories;
+};
+
+/**
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class DirectoryListing {
+public:
+ DirectoryListing(const KURL &path = KURL());
+ ~DirectoryListing();
+
+ void addEntry(DirectoryEntry entry);
+ void updateEntry(const QString &filename, filesize_t size);
+ QValueList<DirectoryEntry> list() { return m_list; }
+
+ void setValid(bool value) { m_valid = value; }
+ bool isValid() { return m_valid; }
+private:
+ bool m_valid;
+ KURL m_path;
+ QValueList<DirectoryEntry> m_list;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/event.cpp b/kftpgrabber/src/engine/event.cpp
new file mode 100644
index 0000000..bfbf6f6
--- /dev/null
+++ b/kftpgrabber/src/engine/event.cpp
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "event.h"
+#include "thread.h"
+
+namespace KFTPEngine {
+
+EventParameter::EventParameter()
+{
+}
+
+EventParameter::EventParameter(const QString &string)
+{
+ m_type = ParamString;
+ m_string = string;
+}
+
+EventParameter::EventParameter(const KURL &url)
+{
+ m_type = ParamUrl;
+ m_url = url;
+}
+
+EventParameter::EventParameter(DirectoryListing listing)
+{
+ m_type = ParamDirListing;
+ m_directoryListing = listing;
+}
+
+EventParameter::EventParameter(DirectoryTree tree)
+{
+ m_type = ParamDirTree;
+ m_directoryTree = tree;
+}
+
+EventParameter::EventParameter(ErrorCode error)
+{
+ m_type = ParamErrorCode;
+ m_errorCode = error;
+}
+
+EventParameter::EventParameter(filesize_t size)
+{
+ m_type = ParamSize;
+ m_fileSize = size;
+}
+
+EventParameter::EventParameter(void *data)
+{
+ m_type = ParamData;
+ m_data = data;
+}
+
+QString EventParameter::asString() const
+{
+ return m_string;
+}
+
+KURL EventParameter::asUrl() const
+{
+ return m_url;
+}
+
+DirectoryListing EventParameter::asDirectoryListing() const
+{
+ return m_directoryListing;
+}
+
+DirectoryTree EventParameter::asDirectoryTree() const
+{
+ return m_directoryTree;
+}
+
+ErrorCode EventParameter::asErrorCode() const
+{
+ return m_errorCode;
+}
+
+filesize_t EventParameter::asFileSize() const
+{
+ return m_fileSize;
+}
+
+bool EventParameter::asBoolean() const
+{
+ return (bool) m_fileSize;
+}
+
+void *EventParameter::asData() const
+{
+ return m_data;
+}
+
+Event::Event(Type type, QValueList<EventParameter> params)
+ : QCustomEvent(65123),
+ m_type(type),
+ m_params(params)
+{
+}
+
+Event::~Event()
+{
+}
+
+EventHandler::EventHandler(Thread *thread)
+ : QObject(),
+ m_thread(thread)
+{
+}
+
+void EventHandler::customEvent(QCustomEvent *e)
+{
+ if (e->type() == 65123) {
+ Event *ev = static_cast<Event*>(e);
+
+ emit engineEvent(ev);
+
+ switch (ev->type()) {
+ case Event::EventConnect: emit connected(); break;
+ case Event::EventDisconnect: emit disconnected(); break;
+ case Event::EventResponse:
+ case Event::EventMultiline: {
+ emit gotResponse(ev->getParameter(0).asString());
+ break;
+ }
+ case Event::EventRaw: emit gotRawResponse(ev->getParameter(0).asString()); break;
+ default: break;
+ }
+ }
+}
+
+}
diff --git a/kftpgrabber/src/engine/event.h b/kftpgrabber/src/engine/event.h
new file mode 100644
index 0000000..e21a45e
--- /dev/null
+++ b/kftpgrabber/src/engine/event.h
@@ -0,0 +1,372 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPNETWORKEVENT_H
+#define KFTPNETWORKEVENT_H
+
+#include <qobject.h>
+#include <qevent.h>
+#include <qshared.h>
+
+#include "directorylisting.h"
+
+namespace KFTPEngine {
+
+/**
+ * Engine reset codes. TODO description of each reset code.
+ */
+enum ResetCode {
+ Ok,
+ UserAbort,
+ Failed,
+ FailedSilently
+};
+
+/**
+ * Engine error codes. TODO: description of each error code.
+ */
+enum ErrorCode {
+ ConnectFailed,
+ LoginFailed,
+ PermissionDenied,
+ FileNotFound,
+ OperationFailed,
+ ListFailed,
+ FileOpenFailed
+};
+
+/**
+ * This class is used for event parameter passing between the socket
+ * thread and the main thread. It supports multiple parameter types.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class EventParameter {
+public:
+ /**
+ * Parameter type enum.
+ */
+ enum Type {
+ ParamString,
+ ParamUrl,
+ ParamDirListing,
+ ParamDirTree,
+ ParamErrorCode,
+ ParamSize,
+ ParamData
+ };
+
+ EventParameter();
+
+ /**
+ * Constructs a new string parameter.
+ *
+ * @param string The QString value
+ */
+ EventParameter(const QString &string);
+
+ /**
+ * Construct a new url parameter.
+ *
+ * @param url The KURL value
+ */
+ EventParameter(const KURL &url);
+
+ /**
+ * Construct a new directory listing parameter.
+ *
+ * @param listing The DirectoryListing value
+ */
+ EventParameter(DirectoryListing listing);
+
+ /**
+ * Construct a new directory tree parameter.
+ *
+ * @param tree The DirectoryTree value
+ */
+ EventParameter(DirectoryTree tree);
+
+ /**
+ * Construct a new error code parameter.
+ *
+ * @param error The ErrorCode value
+ */
+ EventParameter(ErrorCode error);
+
+ /**
+ * Construct a new filesize parameter.
+ *
+ * @param size The filesize_t value
+ */
+ EventParameter(filesize_t size);
+
+ /**
+ * Constructs a new data parameter.
+ *
+ * @param data A pointer to some data.
+ */
+ EventParameter(void *data);
+
+ /**
+ * Returns the parameter's string value.
+ *
+ * @return Parameter's string value
+ */
+ QString asString() const;
+
+ /**
+ * Returns the parameter's url value.
+ *
+ * @return Parameter's url value
+ */
+ KURL asUrl() const;
+
+ /**
+ * Returns the parameter's directory listing value.
+ *
+ * @return Parameter's directory listing value
+ */
+ DirectoryListing asDirectoryListing() const;
+
+ /**
+ * Returns the parameter's directory tree value.
+ *
+ * @return Parameter's directory tree value.
+ */
+ DirectoryTree asDirectoryTree() const;
+
+ /**
+ * Returns the parameter's error code value.
+ *
+ * @return Parameter's error code value
+ */
+ ErrorCode asErrorCode() const;
+
+ /**
+ * Returns the parameter's filesize value.
+ *
+ * @return Parameter's filesize value
+ */
+ filesize_t asFileSize() const;
+
+ /**
+ * Returns the parameter's boolean value.
+ *
+ * @return Parameter's boolean value
+ */
+ bool asBoolean() const;
+
+ /**
+ * Returns raw parameter data pointer.
+ *
+ * @return Raw parameter data pointer
+ */
+ void *asData() const;
+private:
+ Type m_type;
+
+ QString m_string;
+ KURL m_url;
+ DirectoryListing m_directoryListing;
+ DirectoryTree m_directoryTree;
+ ErrorCode m_errorCode;
+ filesize_t m_fileSize;
+ void *m_data;
+};
+
+/**
+ * A wakeup event is a special type event used to transfer some response from
+ * the GUI to the engine that has been temporarly suspended. After receiving
+ * this event, the current command handler's wakeup() method will be called
+ * with this event as a parameter.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class WakeupEvent {
+public:
+ /**
+ * Possible wakeup event types. Each type should subclass this class to
+ * provide any custom methods needed.
+ */
+ enum Type {
+ WakeupFileExists,
+ WakeupPubkey
+ };
+
+ /**
+ * Constructs a new wakeup event of specified type.
+ *
+ * @param type Event type
+ */
+ WakeupEvent(Type type) : m_type(type) {}
+private:
+ Type m_type;
+};
+
+/**
+ * A file exists wakeup event that is used to continue pending transfers.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class FileExistsWakeupEvent : public WakeupEvent {
+public:
+ /**
+ * Possible actions the engine can take.
+ */
+ enum Action {
+ Overwrite,
+ Rename,
+ Resume,
+ Skip
+ };
+
+ /**
+ * Constructs a new file exists wakeup event with Skip action as default.
+ */
+ FileExistsWakeupEvent() : WakeupEvent(WakeupFileExists), action(Skip) {}
+
+ Action action;
+ QString newFileName;
+};
+
+/**
+ * A public key password request event for SFTP connections.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class PubkeyWakeupEvent : public WakeupEvent {
+public:
+ /**
+ * Constructs a new public key wakeup event.
+ */
+ PubkeyWakeupEvent() : WakeupEvent(WakeupPubkey) {}
+
+ QString password;
+};
+
+/**
+ * This class represents an event that is passed to the EventHandler for
+ * processing. It can have multiple EventParameters.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class Event : public QCustomEvent {
+public:
+ enum Type {
+ EventMessage,
+ EventCommand,
+ EventResponse,
+ EventMultiline,
+ EventRaw,
+ EventDirectoryListing,
+ EventDisconnect,
+ EventError,
+ EventConnect,
+ EventReady,
+ EventState,
+ EventScanComplete,
+ EventRetrySuccess,
+ EventReloadNeeded,
+
+ // Transfer events
+ EventTransferComplete,
+ EventResumeOffset,
+
+ // Events that require wakeup events
+ EventFileExists,
+ EventPubkeyPassword
+ };
+
+ /**
+ * Construct a new event with a parameter list.
+ *
+ * @param params Parameter list
+ */
+ Event(Type type, QValueList<EventParameter> params);
+ ~Event();
+
+ /**
+ * Return the event's type.
+ *
+ * @return Event's type
+ */
+ Type type() { return m_type; }
+
+ /**
+ * Returns the parameter with a specific index.
+ *
+ * @param index Parameter's index
+ * @return An EventParameter object
+ */
+ EventParameter getParameter(int index) { return m_params[index]; }
+protected:
+ Type m_type;
+ QValueList<EventParameter> m_params;
+};
+
+class Thread;
+
+/**
+ * This class handles events receieved from the thread and passes them
+ * on to the GUI as normal Qt signals.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class EventHandler : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Construct a new event handler.
+ *
+ * @param thread The thread this event handler belongs to
+ */
+ EventHandler(Thread *thread);
+protected:
+ void customEvent(QCustomEvent *e);
+protected:
+ Thread *m_thread;
+signals:
+ void engineEvent(KFTPEngine::Event *event);
+
+ void connected();
+ void disconnected();
+ void gotResponse(const QString &text);
+ void gotRawResponse(const QString &text);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/ftpdirectoryparser.cpp b/kftpgrabber/src/engine/ftpdirectoryparser.cpp
new file mode 100644
index 0000000..074328c
--- /dev/null
+++ b/kftpgrabber/src/engine/ftpdirectoryparser.cpp
@@ -0,0 +1,1144 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "ftpdirectoryparser.h"
+#include "ftpsocket.h"
+
+#include <qvaluevector.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+
+#include <time.h>
+#include <sys/stat.h>
+
+namespace KFTPEngine {
+
+class DToken {
+public:
+ enum TokenTypeInfo {
+ Unknown,
+ Yes,
+ No
+ };
+
+ DToken()
+ : m_token(QString::null),
+ m_valid(false)
+ {
+ }
+
+ DToken(const QString &token, int start = 0)
+ : m_token(token),
+ m_length(token.length()),
+ m_start(start),
+ m_valid(true),
+ m_numeric(Unknown),
+ m_leftNumeric(Unknown),
+ m_rightNumeric(Unknown)
+ {
+ }
+
+ int getStart()
+ {
+ return m_start;
+ }
+
+ QString getToken()
+ {
+ return m_token;
+ }
+
+ int getLength()
+ {
+ return m_length;
+ }
+
+ QString getString(int type = 0)
+ {
+ switch (type) {
+ case 0: return m_token; break;
+ case 1: {
+ if (!isRightNumeric() || isNumeric())
+ return QString::null;
+
+ int pos = m_length - 1;
+ while (m_token[pos] >= '0' && m_token[pos] <= '9')
+ pos--;
+
+ return m_token.mid(0, pos + 1);
+ break;
+ }
+ case 2: {
+ if (!isLeftNumeric() || isNumeric())
+ return QString::null;
+
+ int len = 0;
+ while (m_token[len] >= '0' && m_token[len] <= '9')
+ len++;
+
+ return m_token.mid(0, len);
+ break;
+ }
+ }
+
+ return QString::null;
+ }
+
+ int find(const char *chr, unsigned int start = 0) const
+ {
+ if (!chr)
+ return -1;
+
+ for (unsigned int i = start; i < m_length; i++) {
+ for (int c = 0; chr[c]; c++) {
+ if (m_token[i] == chr[c])
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ unsigned long long getInteger()
+ {
+ return m_token.toULongLong();
+ }
+
+ unsigned long long getInteger(unsigned int start, int len)
+ {
+ return m_token.mid(start, len).toULongLong();
+ }
+
+ bool isValid()
+ {
+ return m_valid;
+ }
+
+ bool isNumeric()
+ {
+ if (m_numeric == Unknown) {
+ bool ok;
+ (void) m_token.toInt(&ok);
+
+ m_numeric = ok ? Yes : No;
+ }
+
+ return m_numeric == Yes;
+ }
+
+
+ bool isNumeric(unsigned int start, unsigned int len)
+ {
+ len = start + len < m_length ? start + len : m_length;
+
+ for (unsigned int i = start; i < len; i++) {
+ if (m_token[i] < '0' || m_token[i] > '9')
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isLeftNumeric()
+ {
+ if (m_leftNumeric == Unknown) {
+ if (m_length < 2)
+ m_leftNumeric = No;
+ else if (m_token[0] < '0' || m_token[0] > '9')
+ m_leftNumeric = No;
+ else
+ m_leftNumeric = Yes;
+ }
+
+ return m_leftNumeric == Yes;
+ }
+
+ bool isRightNumeric()
+ {
+ if (m_rightNumeric == Unknown) {
+ if (m_length < 2)
+ m_rightNumeric = No;
+ else if (m_token[m_length - 1] < '0' || m_token[m_length - 1] > '9')
+ m_rightNumeric = No;
+ else
+ m_rightNumeric = Yes;
+ }
+
+ return m_rightNumeric == Yes;
+ }
+
+ QChar operator[](unsigned int n) const
+ {
+ return m_token[n];
+ }
+private:
+ QString m_token;
+ unsigned int m_length;
+ int m_start;
+ bool m_valid;
+
+ TokenTypeInfo m_numeric;
+ TokenTypeInfo m_leftNumeric;
+ TokenTypeInfo m_rightNumeric;
+};
+
+class DLine {
+public:
+ DLine(const QString &line)
+ : m_line(line.stripWhiteSpace()),
+ m_parsePos(0)
+ {
+ }
+
+ bool getToken(unsigned int index, DToken &token, bool toEnd = false)
+ {
+ if (!toEnd) {
+ if (m_tokens.count() > index) {
+ token = m_tokens[index];
+ return true;
+ }
+
+ unsigned int start = m_parsePos;
+ while (m_parsePos < m_line.length()) {
+ if (m_line[m_parsePos] == ' ') {
+ m_tokens.append(DToken(m_line.mid(start, m_parsePos - start), start));
+
+ while (m_line[m_parsePos] == ' ' && m_parsePos < m_line.length())
+ m_parsePos++;
+
+ if (m_tokens.count() > index) {
+ token = m_tokens[index];
+ return true;
+ }
+
+ start = m_parsePos;
+ }
+
+ m_parsePos++;
+ }
+
+ if (m_parsePos != start) {
+ m_tokens.append(DToken(m_line.mid(start, m_parsePos - start), start));
+ }
+
+ if (m_tokens.count() > index) {
+ token = m_tokens[index];
+ return true;
+ }
+
+ return false;
+ } else {
+ if (m_endLineTokens.count() > index) {
+ token = m_endLineTokens[index];
+ return true;
+ }
+
+ if (m_tokens.count() <= index && !getToken(index, token))
+ return false;
+
+ for (unsigned int i = m_endLineTokens.count(); i <= index; i++) {
+ m_endLineTokens.append(DToken(m_line.mid(m_tokens[i].getStart())));
+ }
+
+ token = m_endLineTokens[index];
+ return true;
+ }
+ }
+private:
+ QStringList m_stringList;
+ QValueVector<DToken> m_tokens;
+ QValueVector<DToken> m_endLineTokens;
+ QString m_line;
+ unsigned int m_parsePos;
+};
+
+FtpDirectoryParser::FtpDirectoryParser(FtpSocket *socket)
+ : m_socket(socket),
+ m_listing(DirectoryListing(socket->getCurrentDirectory()))
+{
+ // Populate month names as they appear in the listing
+ m_monthNameMap["jan"] = 1;
+ m_monthNameMap["feb"] = 2;
+ m_monthNameMap["mar"] = 3;
+ m_monthNameMap["apr"] = 4;
+ m_monthNameMap["may"] = 5;
+ m_monthNameMap["jun"] = 6;
+ m_monthNameMap["june"] = 6;
+ m_monthNameMap["jul"] = 7;
+ m_monthNameMap["july"] = 7;
+ m_monthNameMap["aug"] = 8;
+ m_monthNameMap["sep"] = 9;
+ m_monthNameMap["sept"] = 9;
+ m_monthNameMap["oct"] = 10;
+ m_monthNameMap["nov"] = 11;
+ m_monthNameMap["dec"] = 12;
+
+ m_monthNameMap["1"] = 1;
+ m_monthNameMap["01"] = 1;
+ m_monthNameMap["2"] = 2;
+ m_monthNameMap["02"] = 2;
+ m_monthNameMap["3"] = 3;
+ m_monthNameMap["03"] = 3;
+ m_monthNameMap["4"] = 4;
+ m_monthNameMap["04"] = 4;
+ m_monthNameMap["5"] = 5;
+ m_monthNameMap["05"] = 5;
+ m_monthNameMap["6"] = 6;
+ m_monthNameMap["06"] = 6;
+ m_monthNameMap["7"] = 7;
+ m_monthNameMap["07"] = 7;
+ m_monthNameMap["8"] = 8;
+ m_monthNameMap["08"] = 8;
+ m_monthNameMap["9"] = 9;
+ m_monthNameMap["09"] = 9;
+ m_monthNameMap["10"] = 10;
+ m_monthNameMap["11"] = 11;
+ m_monthNameMap["12"] = 12;
+}
+
+void FtpDirectoryParser::addDataLine(const QString &line)
+{
+ QString tmp(line);
+ tmp.append("\n");
+ addData(tmp.ascii(), tmp.length());
+}
+
+void FtpDirectoryParser::addData(const char *data, int len)
+{
+ // Append new data to the buffer and check for any new lines
+ m_buffer.append(QString::fromAscii(data, len));
+
+ int pos;
+ while ((pos = m_buffer.find('\n')) > -1) {
+ DirectoryEntry entry;
+ QString line = m_buffer.mid(0, pos).stripWhiteSpace();
+ line = m_socket->remoteEncoding()->decode(QCString(line.ascii()));
+
+ if (parseLine(line, entry) && !entry.filename().isEmpty()) {
+ if (entry.type() == '-')
+ entry.setType('f');
+
+ m_listing.addEntry(entry);
+ }
+
+ // Remove what we just parsed
+ m_buffer.remove(0, pos + 1);
+ }
+}
+
+bool FtpDirectoryParser::parseMlsd(const QString &line, DirectoryEntry &entry)
+{
+ QStringList facts = QStringList::split(';', line);
+ QStringList::Iterator end = facts.end();
+
+ for (QStringList::Iterator i = facts.begin(); i != end; ++i) {
+ if ((*i).contains('=')) {
+ QString key = (*i).section('=', 0, 0).lower();
+ QString value = (*i).section('=', 1, 1);
+
+ if (key == "type") {
+ if (value == "file")
+ entry.setType('f');
+ else if (value == "dir")
+ entry.setType('d');
+ } else if (key == "size") {
+ entry.setSize(value.toULongLong());
+ } else if (key == "modify") {
+ struct tm dt;
+
+ dt.tm_year = value.left(4).toInt() - 1900;
+ dt.tm_mon = value.mid(4, 2).toInt() - 1;
+ dt.tm_mday = value.mid(6, 2).toInt();
+ dt.tm_hour = value.mid(8, 2).toInt();
+ dt.tm_min = value.mid(10, 2).toInt();
+ dt.tm_sec = value.mid(12, 2).toInt();
+ entry.setTime(mktime(&dt));
+ } else if (key == "unix.mode") {
+ entry.setPermissions(value.toInt(0, 8));
+ } else if (key == "unix.uid") {
+ entry.setOwner(value);
+ } else if (key == "unix.gid") {
+ entry.setGroup(value);
+ }
+ } else {
+ entry.setFilename((*i).stripWhiteSpace());
+ }
+ }
+
+ return true;
+}
+
+bool FtpDirectoryParser::parseUnixPermissions(const QString &permissions, DirectoryEntry &entry)
+{
+ int p = 0;
+
+ if (permissions[1] == 'r') p |= S_IRUSR;
+ if (permissions[2] == 'w') p |= S_IWUSR;
+ if (permissions[3] == 'x' || permissions[3] == 's') p |= S_IXUSR;
+
+ if (permissions[4] == 'r') p |= S_IRGRP;
+ if (permissions[5] == 'w') p |= S_IWGRP;
+ if (permissions[6] == 'x' || permissions[6] == 's') p |= S_IXGRP;
+
+ if (permissions[7] == 'r') p |= S_IROTH;
+ if (permissions[8] == 'w') p |= S_IWOTH;
+ if (permissions[9] == 'x' || permissions[9] == 't') p |= S_IXOTH;
+
+ if (permissions[3] == 's' || permissions[3] == 'S') p |= S_ISUID;
+ if (permissions[6] == 's' || permissions[6] == 'S') p |= S_ISGID;
+ if (permissions[9] == 't' || permissions[9] == 'T') p |= S_ISVTX;
+
+ entry.setPermissions(p);
+}
+
+bool FtpDirectoryParser::parseLine(const QString &line, DirectoryEntry &entry)
+{
+ DLine *tLine = new DLine(line);
+ bool done = false;
+
+ // Invalidate timestamp
+ entry.setTime(-1);
+ entry.timeStruct.tm_year = 0;
+ entry.timeStruct.tm_mon = 0;
+ entry.timeStruct.tm_hour = 0;
+ entry.timeStruct.tm_mday = 0;
+ entry.timeStruct.tm_min = 0;
+ entry.timeStruct.tm_sec = 0;
+ entry.timeStruct.tm_wday = 0;
+ entry.timeStruct.tm_yday = 0;
+ entry.timeStruct.tm_isdst = 0;
+
+ // Attempt machine friendly format first, when socket supports MLSD
+ if (m_socket->getConfigInt("feat.mlsd"))
+ done = parseMlsd(line, entry);
+
+ if (!done)
+ done = parseUnix(tLine, entry);
+ if (!done)
+ done = parseDos(tLine, entry);
+ if (!done)
+ done = parseVms(tLine, entry);
+
+ if (done) {
+ // Convert datetime to UNIX epoch
+ if (entry.time() == -1) {
+ // Correct format for mktime
+ entry.timeStruct.tm_year -= 1900;
+ entry.timeStruct.tm_mon -= 1;
+ entry.setTime(mktime(&entry.timeStruct));
+ }
+
+ // Add symlink if any
+ if (entry.filename().contains(" -> ") > 0) {
+ int pos = entry.filename().findRev(" -> ");
+
+ entry.setLink(entry.filename().mid(pos + 4));
+ entry.setFilename(entry.filename().mid(0, pos));
+ }
+
+ // Parse owner into group/owner
+ if (entry.owner().contains(" ") > 0) {
+ int pos = entry.owner().find(" ");
+
+ entry.setGroup(entry.owner().mid(pos + 1));
+ entry.setOwner(entry.owner().mid(0, pos));
+ }
+
+ // Remove unwanted names
+ if (entry.filename() == "." || entry.filename() == "..") {
+ entry.setFilename(QString::null);
+ }
+ }
+
+ delete tLine;
+ return done;
+}
+
+bool FtpDirectoryParser::parseUnix(DLine *line, DirectoryEntry &entry)
+{
+ int index = 0;
+ DToken token;
+
+ if (!line->getToken(index, token))
+ return false;
+
+
+ char chr = token[0];
+ if (chr != 'b' &&
+ chr != 'c' &&
+ chr != 'd' &&
+ chr != 'l' &&
+ chr != 'p' &&
+ chr != 's' &&
+ chr != '-')
+ return false;
+
+ QString permissions = token.getString();
+ entry.setType(chr);
+
+ // Check for netware servers, which split the permissions into two parts
+ bool netware = false;
+ if (token.getLength() == 1) {
+ if (!line->getToken(++index, token))
+ return false;
+
+ permissions += " " + token.getString();
+ netware = true;
+ }
+
+ parseUnixPermissions(permissions, entry);
+
+ int numOwnerGroup = 3;
+ if (!netware) {
+ // Filter out groupid, we don't need it
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (!token.isNumeric())
+ index--;
+ }
+
+ // Repeat until numOwnerGroup is 0 since not all servers send every possible field
+ int startindex = index;
+ do {
+ // Reset index
+ index = startindex;
+
+ entry.setOwner(QString::null);
+ for (int i = 0; i < numOwnerGroup; i++) {
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (i)
+ entry.setOwner(entry.owner() + " ");
+
+ entry.setOwner(entry.owner() + token.getString());
+ }
+
+ if (!line->getToken(++index, token))
+ return false;
+
+
+ // Check for concatenated groupname and size fields
+ filesize_t size;
+ if (!parseComplexFileSize(token, size)) {
+ if (!token.isRightNumeric())
+ continue;
+
+ entry.setSize(token.getInteger());
+ } else {
+ entry.setSize(size);
+ }
+
+ // Append missing group to ownerGroup
+ if (!token.isNumeric() && token.isRightNumeric()) {
+ if (!entry.owner().isEmpty())
+ entry.setOwner(entry.owner() + " ");
+
+ entry.setOwner(entry.owner() + token.getString(1));
+ }
+
+ if (!parseUnixDateTime(line, index, entry))
+ continue;
+
+ // Get the filename
+ if (!line->getToken(++index, token, true))
+ continue;
+
+ entry.setFilename(token.getString());
+
+ // Filter out cpecial chars at the end of the filenames
+ chr = token[token.getLength() - 1];
+ if (chr == '/' ||
+ chr == '|' ||
+ chr == '*')
+ entry.setFilename(entry.filename().mid(0, entry.filename().length() - 1));
+
+ return true;
+ } while (--numOwnerGroup);
+
+ return false;
+}
+
+bool FtpDirectoryParser::parseUnixDateTime(DLine *line, int &index, DirectoryEntry &entry)
+{
+ DToken token;
+
+ // Get the month date field
+ QString dateMonth;
+ if (!line->getToken(++index, token))
+ return false;
+
+ // Some servers use the following date formats:
+ // 26-05 2002, 2002-10-14, 01-jun-99
+ // slashes instead of dashes are also possible
+ int pos = token.find("-/");
+
+ if (pos != -1) {
+ int pos2 = token.find("-/", pos + 1);
+
+ if (pos2 == -1) {
+ // something like 26-05 2002
+ int day = token.getInteger(pos + 1, token.getLength() - pos - 1);
+
+ if (day < 1 || day > 31)
+ return false;
+
+ entry.timeStruct.tm_mday = day;
+ dateMonth = token.getString().left(pos);
+ } else if (!parseShortDate(token, entry)) {
+ return false;
+ }
+ } else {
+ dateMonth = token.getString();
+ }
+
+ bool bHasYearAndTime = false;
+ if (!entry.timeStruct.tm_mday) {
+ // Get day field
+ if (!line->getToken(++index, token))
+ return false;
+
+ int dateDay;
+
+ // Check for non-numeric day
+ if (!token.isNumeric() && !token.isLeftNumeric()) {
+ if (dateMonth.right(1) == ".")
+ dateMonth.remove(dateMonth.length() - 1, 1);
+
+ bool tmp;
+ dateDay = dateMonth.toInt(&tmp);
+ if (!tmp)
+ return false;
+
+ dateMonth = token.getString();
+ } else {
+ dateDay = token.getInteger();
+
+ if (token[token.getLength() - 1] == ',')
+ bHasYearAndTime = true;
+ }
+
+ if (dateDay < 1 || dateDay > 31)
+ return false;
+
+ entry.timeStruct.tm_mday = dateDay;
+ }
+
+ if (!entry.timeStruct.tm_mon) {
+ // Check month name
+ if (dateMonth.right(1) == "," || dateMonth.right(1) == ".")
+ dateMonth.remove(dateMonth.length() - 1, 1);
+
+ dateMonth = dateMonth.lower();
+
+ QMap<QString, int>::iterator iter = m_monthNameMap.find(dateMonth);
+ if (iter == m_monthNameMap.end())
+ return false;
+
+ entry.timeStruct.tm_mon = iter.data();
+ }
+
+ // Get time/year field
+ if (!line->getToken(++index, token))
+ return false;
+
+ pos = token.find(":.-");
+ if (pos != -1) {
+ // token is a time
+ if (!pos || static_cast<size_t>(pos) == (token.getLength() - 1))
+ return false;
+
+ QString str = token.getString();
+ bool tmp;
+ int hour = str.left(pos).toInt(&tmp);
+ if (!tmp)
+ return false;
+
+ int minute = str.mid(pos + 1).toInt(&tmp);
+ if (!tmp)
+ return false;
+
+ if (hour < 0 || hour > 23)
+ return false;
+
+ if (minute < 0 || minute > 59)
+ return false;
+
+ entry.timeStruct.tm_hour = hour;
+ entry.timeStruct.tm_min = minute;
+
+ // Some servers use times only for files nweer than 6 months,
+ int year = QDate::currentDate().year();
+ int now = QDate::currentDate().day() + 31 * QDate::currentDate().month();
+ int file = entry.timeStruct.tm_mon * 31 + entry.timeStruct.tm_mday;
+
+ if (now >= file)
+ entry.timeStruct.tm_year = year;
+ else
+ entry.timeStruct.tm_year = year - 1;
+ } else if (!entry.timeStruct.tm_year) {
+ // token is a year
+ if (!token.isNumeric() && !token.isLeftNumeric())
+ return false;
+
+ int year = token.getInteger();
+ if (year > 3000)
+ return false;
+
+ if (year < 1000)
+ year += 1900;
+
+ entry.timeStruct.tm_year = year;
+
+ if (bHasYearAndTime) {
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (token.find(":") == 2 && token.getLength() == 5 && token.isLeftNumeric() && token.isRightNumeric()) {
+ int pos = token.find(":");
+
+ // token is a time
+ if (!pos || static_cast<size_t>(pos) == (token.getLength() - 1))
+ return false;
+
+ QString str = token.getString();
+ bool tmp;
+ long hour = str.left(pos).toInt(&tmp);
+ if (!tmp)
+ return false;
+
+ long minute = str.mid(pos + 1).toInt(&tmp);
+ if (!tmp)
+ return false;
+
+ if (hour < 0 || hour > 23)
+ return false;
+
+ if (minute < 0 || minute > 59)
+ return false;
+
+ entry.timeStruct.tm_hour = hour;
+ entry.timeStruct.tm_min = minute;
+ } else {
+ index--;
+ }
+ }
+ } else {
+ index--;
+ }
+
+ return true;
+}
+
+bool FtpDirectoryParser::parseShortDate(DToken &token, DirectoryEntry &entry)
+{
+ if (token.getLength() < 1)
+ return false;
+
+ bool gotYear = false;
+ bool gotMonth = false;
+ bool gotDay = false;
+ bool gotMonthName = false;
+
+ int value = 0;
+
+ int pos = token.find("-./");
+ if (pos < 1)
+ return false;
+
+ if (!token.isNumeric(0, pos)) {
+ // Seems to be monthname-dd-yy
+
+ // Check month name
+ QString dateMonth = token.getString().mid(0, pos);
+ dateMonth = dateMonth.lower();
+
+ QMap<QString, int>::iterator iter = m_monthNameMap.find(dateMonth);
+ if (iter == m_monthNameMap.end())
+ return false;
+
+ entry.timeStruct.tm_mon = iter.data();
+ gotMonth = true;
+ gotMonthName = true;
+ } else if (pos == 4) {
+ // Seems to be yyyy-mm-dd
+ int year = token.getInteger(0, pos);
+
+ if (year < 1900 || year > 3000)
+ return false;
+
+ entry.timeStruct.tm_year = year;
+ gotYear = true;
+ } else if (pos <= 2) {
+ int value = token.getInteger(0, pos);
+
+ if (token[pos] == '.') {
+ // Maybe dd.mm.yyyy
+ if (value < 1900 || value > 3000)
+ return false;
+
+ entry.timeStruct.tm_mday = value;
+ gotDay = true;
+ } else {
+ // Detect mm-dd-yyyy or mm/dd/yyyy and
+ // dd-mm-yyyy or dd/mm/yyyy
+ if (value < 1)
+ return false;
+
+ if (value > 12) {
+ if (value > 31)
+ return false;
+
+ entry.timeStruct.tm_mday = value;
+ gotDay = true;
+ } else {
+ entry.timeStruct.tm_mon = value;
+ gotMonth = true;
+ }
+ }
+ } else {
+ return false;
+ }
+
+
+ int pos2 = token.find("-./", pos + 1);
+
+ if (pos2 == -1 || (pos2 - pos) == 1)
+ return false;
+
+ if (static_cast<size_t>(pos2) == (token.getLength() - 1))
+ return false;
+
+ // If we already got the month and the second field is not numeric,
+ // change old month into day and use new token as month
+ if (!token.isNumeric(pos + 1, pos2 - pos - 1) && gotMonth) {
+ if (gotMonthName)
+ return false;
+
+ if (gotDay)
+ return false;
+
+ gotDay = true;
+ gotMonth = false;
+ entry.timeStruct.tm_mday = entry.timeStruct.tm_mon;
+ }
+
+ if (gotYear || gotDay) {
+ // Month field in yyyy-mm-dd or dd-mm-yyyy
+ // Check month name
+ QString dateMonth = token.getString().mid(pos + 1, pos2 - pos - 1);
+ dateMonth = dateMonth.lower();
+
+ QMap<QString, int>::iterator iter = m_monthNameMap.find(dateMonth);
+ if (iter == m_monthNameMap.end())
+ return false;
+
+ entry.timeStruct.tm_mon = iter.data();
+ gotMonth = true;
+ } else {
+ int value = token.getInteger(pos + 1, pos2 - pos - 1);
+
+ // Day field in mm-dd-yyyy
+ if (value < 1 || value > 31)
+ return false;
+
+ entry.timeStruct.tm_mday = value;
+ gotDay = true;
+ }
+
+ value = token.getInteger(pos2 + 1, token.getLength() - pos2 - 1);
+ if (gotYear) {
+ // Day field in yyy-mm-dd
+ if (!value || value > 31)
+ return false;
+
+ entry.timeStruct.tm_mday = value;
+ gotDay = true;
+ } else {
+ if (value < 0)
+ return false;
+
+ if (value < 50) {
+ value += 2000;
+ } else if (value < 1000) {
+ value += 1900;
+ }
+
+ entry.timeStruct.tm_year = value;
+ gotYear = true;
+ }
+
+ if (!gotMonth || !gotDay || !gotYear)
+ return false;
+
+ return true;
+}
+
+bool FtpDirectoryParser::parseDos(DLine *line, DirectoryEntry &entry)
+{
+ int index = 0;
+ DToken token;
+
+ // Get first token, has to be a valid date
+ if (!line->getToken(index, token))
+ return false;
+
+ if (!parseShortDate(token, entry))
+ return false;
+
+ // Extract time
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (!parseTime(token, entry))
+ return false;
+
+ // If next token is <DIR>, entry is a directory
+ // else, it should be the filesize.
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (token.getString() == "<DIR>") {
+ entry.setType('d');
+ entry.setSize(0);
+ } else if (token.isNumeric() || token.isLeftNumeric()) {
+ // Convert size, filter out separators
+ unsigned long size = 0;
+ int len = token.getLength();
+
+ for (int i = 0; i < len; i++) {
+ char chr = token[i];
+
+ if (chr == ',' || chr == '.')
+ continue;
+
+ if (chr < '0' || chr > '9')
+ return false;
+
+ size *= 10;
+ size += chr - '0';
+ }
+
+ entry.setSize(size);
+ entry.setType('f');
+ } else {
+ return false;
+ }
+
+ // Extract filename
+ if (!line->getToken(++index, token, true))
+ return false;
+
+ entry.setFilename(token.getString());
+
+ return true;
+}
+
+
+bool FtpDirectoryParser::parseTime(DToken &token, DirectoryEntry &entry)
+{
+ int pos = token.find(":");
+ if (pos < 1 || static_cast<unsigned int>(pos) >= (token.getLength() - 1))
+ return false;
+
+ int hour = token.getInteger(0, pos);
+ if (hour < 0 || hour > 23)
+ return false;
+
+ int minute = token.getInteger(pos + 1, 2);
+ if (minute < 0 || minute > 59)
+ return false;
+
+ // Convert to 24h format
+ if (!token.isRightNumeric()) {
+ if (token[token.getLength() - 2] == 'P') {
+ if (hour < 12) {
+ hour += 12;
+ }
+ } else if (hour == 12) {
+ hour = 0;
+ }
+ }
+
+ entry.timeStruct.tm_hour = hour;
+ entry.timeStruct.tm_min = minute;
+
+ return true;
+}
+
+bool FtpDirectoryParser::parseVms(DLine *line, DirectoryEntry &entry)
+{
+ DToken token;
+ int index = 0;
+
+ if (!line->getToken(index, token))
+ return false;
+
+ int pos = token.find(";");
+
+ if (pos == -1)
+ return false;
+
+ if (pos > 4 && token.getString().mid(pos - 4, 4) == ".DIR") {
+ entry.setType('d');
+ entry.setFilename(token.getString().left(pos - 4) + token.getString().mid(pos));
+ } else {
+ entry.setType('f');
+ entry.setFilename(token.getString());
+ }
+
+ // Get size
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (!token.isNumeric() && !token.isLeftNumeric())
+ return false;
+
+ entry.setSize(token.getInteger());
+
+ // Get date
+ if (!line->getToken(++index, token))
+ return false;
+
+ if (!parseShortDate(token, entry))
+ return false;
+
+ // Get time
+ if (!line->getToken(++index, token))
+ return true;
+
+ if (!parseTime(token, entry)) {
+ int len = token.getLength();
+
+ if (token[0] == '[' && token[len] != ']')
+ return false;
+ if (token[0] == '(' && token[len] != ')')
+ return false;
+ if (token[0] != '[' && token[len] == ']')
+ return false;
+ if (token[0] != '(' && token[len] == ')')
+ return false;
+
+ index--;
+ }
+
+ // Owner / group
+ while (line->getToken(++index, token)) {
+ int len = token.getLength();
+
+ if (len > 2 && token[0] == '(' && token[len - 1] == ')')
+ entry.setPermissions(0);
+ else if (len > 2 && token[0] == '[' && token[len - 1] == ']')
+ entry.setOwner(token.getString().mid(1, len - 2));
+ else
+ entry.setPermissions(0);
+ }
+
+ return true;
+}
+
+bool FtpDirectoryParser::parseComplexFileSize(DToken &token, filesize_t &size)
+{
+ if (token.isNumeric()) {
+ size = token.getInteger();
+ return true;
+ }
+
+ int len = token.getLength() - 1;
+
+ char last = token[len];
+ if (last == 'B' || last == 'b') {
+ char c = token[len];
+
+ if (c < '0' || c > '9') {
+ last = token[len];
+ len--;
+ }
+ }
+
+ size = 0;
+
+ int dot = -1;
+ for (int i = 0; i < len; i++) {
+ char c = token[i];
+
+ if (c >= '0' && c <= '9') {
+ size *= 10;
+ size += c - '0';
+ } else if (c == '.') {
+ if (dot != -1)
+ return false;
+
+ dot = len - i - 1;
+ } else {
+ return false;
+ }
+ }
+
+ switch (last) {
+ case 'k':
+ case 'K': {
+ size *= 1000;
+ break;
+ }
+ case 'm':
+ case 'M': {
+ size *= 1000 * 1000;
+ break;
+ }
+ case 'g':
+ case 'G': {
+ size *= 1000 * 1000 * 1000;
+ break;
+ }
+ case 't':
+ case 'T': {
+ size *= 1000 * 1000;
+ size *= 1000 * 1000;
+ break;
+ }
+ case 'b':
+ case 'B': break;
+ default: return false;
+ }
+
+ while (dot-- > 0)
+ size /= 10;
+
+ return true;
+}
+
+}
diff --git a/kftpgrabber/src/engine/ftpdirectoryparser.h b/kftpgrabber/src/engine/ftpdirectoryparser.h
new file mode 100644
index 0000000..1b56831
--- /dev/null
+++ b/kftpgrabber/src/engine/ftpdirectoryparser.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPENGINEFTPDIRECTORYPARSER_H
+#define KFTPENGINEFTPDIRECTORYPARSER_H
+
+#include <qmap.h>
+
+#include "directorylisting.h"
+
+namespace KFTPEngine {
+
+class FtpSocket;
+
+class DToken;
+class DLine;
+
+/**
+ * This class can parse multiple directory formats. Some code portions have
+ * been taken from a windows FTP client "FileZilla by Tim Kosse" - the
+ * logic is mostly the same, the code has just been ported so it is more Qt
+ * and so it integrates nicely with the rest of the engine.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ * @author Tim Kosse <tim.kosse@gmx.de>
+ */
+class FtpDirectoryParser {
+public:
+ FtpDirectoryParser(FtpSocket *socket);
+
+ void addData(const char *data, int len);
+ void addDataLine(const QString &line);
+
+ bool parseLine(const QString &line, DirectoryEntry &entry);
+ DirectoryListing getListing() { return m_listing; }
+private:
+ FtpSocket *m_socket;
+ QString m_buffer;
+ DirectoryListing m_listing;
+
+ QMap<QString, int> m_monthNameMap;
+
+ bool parseMlsd(const QString &line, DirectoryEntry &entry);
+ bool parseUnix(DLine *line, DirectoryEntry &entry);
+ bool parseDos(DLine *line, DirectoryEntry &entry);
+ bool parseVms(DLine *line, DirectoryEntry &entry);
+
+ bool parseUnixDateTime(DLine *line, int &index, DirectoryEntry &entry);
+ bool parseShortDate(DToken &token, DirectoryEntry &entry);
+ bool parseTime(DToken &token, DirectoryEntry &entry);
+
+ bool parseComplexFileSize(DToken &token, filesize_t &size);
+
+ bool parseUnixPermissions(const QString &permissions, DirectoryEntry &entry);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/ftpsocket.cpp b/kftpgrabber/src/engine/ftpsocket.cpp
new file mode 100644
index 0000000..2741f4d
--- /dev/null
+++ b/kftpgrabber/src/engine/ftpsocket.cpp
@@ -0,0 +1,2749 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "ftpsocket.h"
+#include "thread.h"
+#include "ftpdirectoryparser.h"
+#include "cache.h"
+#include "speedlimiter.h"
+#include "ssl.h"
+
+#include "misc/kftpotpgenerator.h"
+#include "misc/config.h"
+
+#include <qdir.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <ksocketdevice.h>
+
+#include <utime.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+namespace KFTPEngine {
+
+FtpSocket::FtpSocket(Thread *thread)
+ : KNetwork::KStreamSocket(),
+ Socket(thread, "ftp"),
+ SpeedLimiterItem(),
+ m_login(false),
+ m_transferSocket(0),
+ m_directoryParser(0),
+ m_controlConnecting(false),
+ m_controlSsl(0),
+ m_dataSsl(0),
+ m_clientCert(0)
+{
+ enableRead(false);
+ setBlocking(false);
+}
+
+FtpSocket::~FtpSocket()
+{
+ protoDisconnect();
+}
+
+void FtpSocket::poll()
+{
+ if (m_controlConnecting) {
+ if (isFatalError(error())) {
+ slotError();
+ resetError();
+ m_controlConnecting = false;
+ return;
+ }
+
+ if (state() == Connected) {
+ m_controlConnecting = false;
+ slotConnected();
+ }
+
+ return;
+ }
+
+ slotControlTryRead();
+
+ if (!m_buffer.isEmpty())
+ processBuffer();
+
+ if (m_transferSocket) {
+ if (m_transferConnecting && m_transferSocket->state() == Connected) {
+ m_transferConnecting = false;
+ slotDataConnected();
+ } else if (!m_transferConnecting) {
+ if (getCurrentCommand() == Commands::CmdPut) {
+ if (m_transferStart >= 2)
+ slotDataTryWrite();
+ } else {
+ bool input;
+ m_transferSocket->socketDevice()->poll(&input, 0, 0, 0);
+
+ if (input)
+ slotDataTryRead();
+ }
+ }
+ } else if (m_serverSocket) {
+ bool input;
+ m_serverSocket->socketDevice()->poll(&input, 0, 0, 0);
+
+ if (input) {
+ KNetwork::KActiveSocketBase *socket = m_serverSocket->accept();
+
+ if (socket) {
+ slotDataAccept(static_cast<KNetwork::KStreamSocket*>(socket));
+ m_transferConnecting = false;
+ }
+ }
+ }
+
+ // Check for timeouts
+ // NOTE This should be moved to a QTimer's slot when ported to Qt 4
+ timeoutCheck();
+ keepaliveCheck();
+}
+
+void FtpSocket::slotControlTryRead()
+{
+ QString tmpStr;
+ Q_LONG size = 0;
+
+ // Read what we can
+ if (getConfigInt("ssl") && m_controlSsl) {
+ size = m_controlSsl->read(m_controlBuffer, sizeof(m_controlBuffer) - 1);
+
+ if (size == -1) {
+ protoDisconnect();
+ return;
+ }
+ } else
+ size = readBlock(m_controlBuffer, sizeof(m_controlBuffer) - 1);
+
+ if (error() != NoError) {
+ // Have we been disconnected ?
+ if (error() != WouldBlock)
+ protoDisconnect();
+
+ return;
+ }
+
+ if (size == 0)
+ return;
+
+ for (int i = 0; i < size; i++)
+ if (m_controlBuffer[i] == 0)
+ m_controlBuffer[i] = '!';
+
+ memset(m_controlBuffer + size, 0, sizeof(m_controlBuffer) - size);
+ m_buffer.append(m_controlBuffer);
+}
+
+void FtpSocket::processBuffer()
+{
+ // Parse any lines we might have
+ int pos;
+ while ((pos = m_buffer.find('\n')) > -1) {
+ QString line = m_buffer.mid(0, pos);
+ line = m_remoteEncoding->decode(QCString(line.ascii()));
+ parseLine(line);
+
+ // Remove what we just parsed
+ m_buffer.remove(0, pos + 1);
+ }
+}
+
+void FtpSocket::parseLine(const QString &line)
+{
+ // Is this the end of multiline response ?
+ if (!m_multiLineCode.isEmpty() && line.left(4) == m_multiLineCode) {
+ m_multiLineCode = "";
+ emitEvent(Event::EventResponse, line);
+ } else if (line[3] == '-' && m_multiLineCode.isEmpty()) {
+ m_multiLineCode = line.left(3) + " ";
+ emitEvent(Event::EventMultiline, line);
+ } else if (!m_multiLineCode.isEmpty()) {
+ emitEvent(Event::EventMultiline, line);
+ } else {
+ // Normal response
+ emitEvent(Event::EventResponse, line);
+ }
+
+ timeoutWait(false);
+
+ // Parse our response
+ m_response = line;
+ nextCommand();
+}
+
+bool FtpSocket::isResponse(const QString &code)
+{
+ QString ref;
+
+ if (isMultiline())
+ ref = m_multiLineCode;
+ else
+ ref = m_response;
+
+ return ref.left(code.length()) == code;
+}
+
+void FtpSocket::sendCommand(const QString &command)
+{
+ emitEvent(Event::EventCommand, command);
+ QCString buffer(m_remoteEncoding->encode(command) + "\r\n");
+
+ if (getConfigInt("ssl") && m_controlSsl)
+ m_controlSsl->write(buffer.data(), buffer.length());
+ else
+ writeBlock(buffer.data(), buffer.length());
+
+ timeoutWait(true);
+}
+
+void FtpSocket::resetCommandClass(ResetCode code)
+{
+ timeoutWait(false);
+
+ if (m_transferSocket && code != Ok) {
+ // Invalidate the socket
+ closeDataTransferSocket();
+
+ // Close the file that failed transfer
+ if (getTransferFile()->isOpen()) {
+ getTransferFile()->close();
+
+ if (getCurrentCommand() == Commands::CmdGet && getTransferFile()->size() == 0)
+ getTransferFile()->remove();
+ }
+ }
+
+ if (m_serverSocket && code != Ok)
+ delete m_serverSocket;
+
+ Socket::resetCommandClass(code);
+}
+
+// *******************************************************************************************
+// ***************************************** CONNECT *****************************************
+// *******************************************************************************************
+
+class FtpCommandConnect : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentAuthTls,
+ SentUser,
+ SentPass,
+ SentPbsz,
+ SentProt,
+ DoingSyst,
+ DoingFeat,
+ SentPwd
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandConnect, FtpSocket, CmdNone)
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ if (!socket()->isMultiline()) {
+ if (socket()->isResponse("2")) {
+ // Negotiate a SSL connection if configured
+ if (socket()->getConfigInt("ssl.use_tls")) {
+ currentState = SentAuthTls;
+ socket()->sendCommand("AUTH TLS");
+ } else {
+ // Send username
+ currentState = SentUser;
+ socket()->sendCommand("USER " + socket()->getCurrentUrl().user());
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Connection has failed."));
+
+ socket()->protoAbort();
+ socket()->emitError(ConnectFailed);
+ }
+ }
+ break;
+ }
+ case SentAuthTls: {
+ if (socket()->isResponse("2")) {
+ socket()->m_controlSsl = new Ssl(socket());
+
+ // Setup client certificate if one was provided
+ if (socket()->m_clientCert)
+ socket()->m_controlSsl->setClientCertificate(socket()->m_clientCert);
+
+ if (socket()->m_controlSsl->connect()) {
+ socket()->emitEvent(Event::EventMessage, i18n("SSL negotiation successful. Connection is secured with %1 bit cipher %2.").arg(socket()->m_controlSsl->connectionInfo().getCipherUsedBits()).arg(socket()->m_controlSsl->connectionInfo().getCipher()));
+ socket()->setConfig("ssl", 1);
+
+ // Now send the username
+ currentState = SentUser;
+ socket()->sendCommand("USER " + socket()->getCurrentUrl().user());
+ } else {
+ delete socket()->m_controlSsl;
+ socket()->m_controlSsl = 0;
+
+ socket()->emitEvent(Event::EventMessage, i18n("SSL negotiation failed. Login aborted."));
+ socket()->resetCommandClass(Failed);
+
+ socket()->protoAbort();
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("SSL negotiation request failed. Login aborted."));
+ socket()->resetCommandClass(Failed);
+
+ socket()->protoAbort();
+ }
+ break;
+ }
+ case SentUser: {
+ if (socket()->isResponse("331")) {
+ // Send password
+ if (socket()->isResponse("331 Response to otp-") ||
+ socket()->isResponse("331 Response to s/key")) {
+ // OTP: 331 Response to otp-md5 41 or4828 ext required for foo.
+ QString tmp = socket()->getResponse();
+ tmp = tmp.section(' ', 3, 5);
+
+ KFTPOTPGenerator otp(tmp, socket()->getCurrentUrl().pass());
+ currentState = SentPass;
+ socket()->sendCommand("PASS " + otp.generateOTP());
+ } else {
+ socket()->sendCommand("PASS " + socket()->getCurrentUrl().pass());
+ currentState = SentPass;
+ }
+ } else if (socket()->isResponse("230")) {
+ // Some servers imediately send the 230 response for anonymous accounts
+ if (!socket()->isMultiline()) {
+ if (socket()->getConfigInt("ssl")) {
+ currentState = SentPbsz;
+ socket()->sendCommand("PBSZ 0");
+ } else {
+ // Do SYST
+ socket()->sendCommand("SYST");
+ currentState = DoingSyst;
+ }
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Login has failed."));
+
+ socket()->protoAbort();
+ socket()->emitError(LoginFailed);
+ }
+ break;
+ }
+ case SentPass: {
+ if (socket()->isResponse("230")) {
+ if (!socket()->isMultiline()) {
+ if (socket()->getConfigInt("ssl")) {
+ currentState = SentPbsz;
+ socket()->sendCommand("PBSZ 0");
+ } else {
+ // Do SYST
+ socket()->sendCommand("SYST");
+ currentState = DoingSyst;
+ }
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Login has failed."));
+
+ socket()->protoAbort();
+ socket()->emitError(LoginFailed);
+ }
+ break;
+ }
+ case SentPbsz: {
+ currentState = SentProt;
+ QString prot = "PROT ";
+
+ if (socket()->getConfigInt("ssl.prot_mode") == 0)
+ prot.append('P');
+ else
+ prot.append('C');
+
+ socket()->sendCommand(prot);
+ break;
+ }
+ case SentProt: {
+ if (socket()->isResponse("5")) {
+ // Fallback to unencrypted data channel
+ socket()->setConfig("ssl.prot_mode", 2);
+ }
+
+ currentState = DoingSyst;
+ socket()->sendCommand("SYST");
+ break;
+ }
+ case DoingSyst: {
+ socket()->sendCommand("FEAT");
+ currentState = DoingFeat;
+ break;
+ }
+ case DoingFeat: {
+ if (socket()->isMultiline()) {
+ parseFeat();
+ } else {
+ socket()->sendCommand("PWD");
+ currentState = SentPwd;
+ }
+ break;
+ }
+ case SentPwd: {
+ // Parse the current working directory
+ if (socket()->isResponse("2")) {
+ // 257 "/home/default/path"
+ QString tmp = socket()->getResponse();
+ int first = tmp.find('"') + 1;
+ tmp = tmp.mid(first, tmp.findRev('"') - first);
+
+ socket()->setDefaultDirectory(tmp);
+ socket()->setCurrentDirectory(tmp);
+ }
+
+ // Enable transmission of keepalive events
+ socket()->keepaliveStart();
+
+ currentState = None;
+ socket()->emitEvent(Event::EventMessage, i18n("Connected."));
+ socket()->emitEvent(Event::EventConnect);
+ socket()->m_login = true;
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+
+ void parseFeat()
+ {
+ QString feat = socket()->getResponse().stripWhiteSpace().upper();
+
+ if (feat.left(3).toInt() > 0 && feat[3] == '-')
+ feat.remove(0, 4);
+
+ if (feat.left(4) == "MDTM") {
+ // Server has MDTM (MoDification TiMe) support
+ socket()->setConfig("feat.mdtm", 1);
+ } else if (feat.left(4) == "PRET") {
+ // Server is a distributed ftp server and requires PRET for transfers
+ socket()->setConfig("feat.pret", 1);
+ } else if (feat.left(4) == "MLSD") {
+ // Server supports machine-friendly directory listings
+ socket()->setConfig("feat.mlsd", 1);
+ } else if (feat.left(4) == "REST") {
+ // Server supports resume operations
+ socket()->setConfig("feat.rest", 1);
+ } else if (feat.left(4) == "SSCN") {
+ // Server supports SSCN for secure site-to-site transfers
+ socket()->setConfig("feat.sscn", 1);
+ socket()->setConfig("feat.cpsv", 0);
+ } else if (feat.left(4) == "CPSV" && !socket()->getConfigInt("feat.sscn")) {
+ // Server supports CPSV for secure site-to-site transfers
+ socket()->setConfig("feat.cpsv", 1);
+ }
+ }
+};
+
+void FtpSocket::protoConnect(const KURL &url)
+{
+ emitEvent(Event::EventState, i18n("Connecting..."));
+ emitEvent(Event::EventMessage, i18n("Connecting to %1:%2...").arg(url.host()).arg(url.port()));
+
+ if (!getConfig("encoding").isEmpty())
+ changeEncoding(getConfig("encoding"));
+
+ // Start the connect procedure
+ m_controlConnecting = true;
+ setCurrentUrl(url);
+ KNetwork::KStreamSocket::connect(url.host(), QString::number(url.port()));
+}
+
+void FtpSocket::slotConnected()
+{
+ if (getConfigInt("ssl.use_implicit")) {
+ m_controlSsl = new Ssl(this);
+
+ // Setup client certificate if one was provided
+ if (m_clientCert)
+ m_controlSsl->setClientCertificate(m_clientCert);
+
+ if (m_controlSsl->connect()) {
+ emitEvent(Event::EventMessage, i18n("SSL negotiation successful. Connection is secured with %1 bit cipher %2.").arg(m_controlSsl->connectionInfo().getCipherUsedBits()).arg(m_controlSsl->connectionInfo().getCipher()));
+ setConfig("ssl", 1);
+ } else {
+ delete m_controlSsl;
+ m_controlSsl = 0;
+
+ emitEvent(Event::EventMessage, i18n("SSL negotiation failed. Connect aborted."));
+ resetCommandClass(Failed);
+
+ protoAbort();
+ }
+ }
+
+ timeoutWait(true);
+
+ emitEvent(Event::EventState, i18n("Logging in..."));
+ emitEvent(Event::EventMessage, i18n("Connected with server, waiting for welcome message..."));
+ setupCommandClass(FtpCommandConnect);
+}
+
+void FtpSocket::slotError()
+{
+ if (isFatalError(error())) {
+ emitEvent(Event::EventMessage, i18n("Failed to connect (%1)").arg(errorString(error())));
+ emitError(ConnectFailed);
+
+ resetCommandClass(FailedSilently);
+ }
+}
+
+// *******************************************************************************************
+// **************************************** DISCONNECT ***************************************
+// *******************************************************************************************
+
+void FtpSocket::protoDisconnect()
+{
+ Socket::protoDisconnect();
+
+ // Close SSL
+ if (getConfigInt("ssl") && m_controlSsl) {
+ m_controlSsl->close();
+ delete m_controlSsl;
+ m_controlSsl = 0;
+
+ if (m_clientCert) {
+ delete m_clientCert;
+ m_clientCert = 0;
+ }
+ }
+
+ // Terminate the connection
+ m_login = false;
+ KNetwork::KStreamSocket::close();
+}
+
+void FtpSocket::protoAbort()
+{
+ Socket::protoAbort();
+
+ if (getCurrentCommand() != Commands::CmdNone) {
+ // Abort current command
+ if (getCurrentCommand() == Commands::CmdConnect)
+ protoDisconnect();
+
+ if (m_cmdData)
+ resetCommandClass(UserAbort);
+
+ emitEvent(Event::EventMessage, i18n("Aborted."));
+ }
+}
+
+// *******************************************************************************************
+// ********************************* NEGOTIATE DATA CONNECTION *******************************
+// *******************************************************************************************
+
+class FtpCommandNegotiateData : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentSscnOff,
+ SentType,
+ SentProt,
+ SentPret,
+ NegotiateActive,
+ NegotiatePasv,
+ NegotiateEpsv,
+ HaveConnection,
+ SentRest,
+ SentDataCmd,
+ WaitTransfer
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandNegotiateData, FtpSocket, CmdNone)
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ if (socket()->getConfigInt("sscn.activated")) {
+ // First disable SSCN
+ currentState = SentSscnOff;
+ socket()->sendCommand("SSCN OFF");
+ return;
+ }
+ }
+ case SentSscnOff: {
+ if (currentState == SentSscnOff)
+ socket()->setConfig("sscn.activated", 0);
+
+ // Change type
+ currentState = SentType;
+ socket()->resetTransferStart();
+
+ QString type = "TYPE ";
+ type.append(socket()->getConfigInt("params.data_type"));
+ socket()->sendCommand(type);
+ break;
+ }
+ case SentType: {
+ if (socket()->getConfigInt("ssl") && socket()->getConfigInt("ssl.prot_mode") == 1) {
+ currentState = SentProt;
+
+ if (socket()->getPreviousCommand() == Commands::CmdList)
+ socket()->sendCommand("PROT P");
+ else
+ socket()->sendCommand("PROT C");
+ } else if (socket()->getConfigInt("feat.pret")) {
+ currentState = SentPret;
+ socket()->sendCommand("PRET " + socket()->getConfig("params.data_command"));
+ } else {
+ negotiateDataConnection();
+ }
+ break;
+ }
+ case SentProt: {
+ if (socket()->getConfigInt("feat.pret")) {
+ currentState = SentPret;
+ socket()->sendCommand("PRET " + socket()->getConfig("params.data_command"));
+ } else {
+ negotiateDataConnection();
+ }
+ break;
+ }
+ case SentPret: {
+ // PRET failed because of filesystem problems, abort right away!
+ if (socket()->isResponse("530")) {
+ socket()->emitError(PermissionDenied);
+ socket()->resetCommandClass(Failed);
+ return;
+ } else if (socket()->isResponse("550")) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ return;
+ } else if (socket()->isResponse("5")) {
+ // PRET is not supported, disable for future use
+ socket()->setConfig("feat.pret", 0);
+ }
+
+ negotiateDataConnection();
+ break;
+ }
+ case NegotiateActive: negotiateActive(); break;
+ case NegotiateEpsv: negotiateEpsv(); break;
+ case NegotiatePasv: negotiatePasv(); break;
+ case HaveConnection: {
+ // We have the connection
+ if (socket()->getConfigInt("params.data_rest_do")) {
+ currentState = SentRest;
+ socket()->sendCommand("REST " + QString::number(socket()->getConfigFs("params.data_rest")));
+ } else {
+ currentState = SentDataCmd;
+ socket()->sendCommand(socket()->getConfig("params.data_command"));
+ }
+ break;
+ }
+ case SentRest: {
+ if (!socket()->isResponse("2") && !socket()->isResponse("3")) {
+ socket()->setConfig("feat.rest", 0);
+ socket()->getTransferFile()->close();
+
+ bool ok;
+
+ if (socket()->getPreviousCommand() == Commands::CmdGet)
+ ok = socket()->getTransferFile()->open(IO_WriteOnly | IO_Truncate);
+ else
+ ok = socket()->getTransferFile()->open(IO_ReadOnly);
+
+ // Check if there was a problem opening the file
+ if (!ok) {
+ socket()->emitError(FileOpenFailed);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+ }
+
+ // We have sent REST, now send the data command
+ currentState = SentDataCmd;
+ socket()->sendCommand(socket()->getConfig("params.data_command"));
+ break;
+ }
+ case SentDataCmd: {
+ if (!socket()->isResponse("1")) {
+ // Some problems while executing the data command
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ if (!socket()->isMultiline()) {
+ socket()->checkTransferStart();
+ currentState = WaitTransfer;
+ }
+ break;
+ }
+ case WaitTransfer: {
+ if (!socket()->isResponse("2")) {
+ // Transfer has failed
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ if (!socket()->isMultiline()) {
+ // Transfer has been completed
+ socket()->checkTransferEnd();
+ }
+ break;
+ }
+ }
+ }
+
+ void negotiateDataConnection()
+ {
+ if (socket()->getConfigInt("feat.epsv")) {
+ negotiateEpsv();
+ } else if (socket()->getConfigInt("feat.pasv")) {
+ negotiatePasv();
+ } else {
+ negotiateActive();
+ }
+ }
+
+ void negotiateEpsv()
+ {
+ if (currentState == NegotiateEpsv) {
+ if (!socket()->isResponse("2")) {
+ // Negotiation failed
+ socket()->setConfig("feat.epsv", "0");
+
+ // Try the next thing
+ negotiateDataConnection();
+ return;
+ }
+
+ // 229 Entering Extended Passive Mode (|||55016|)
+ char *begin = strchr(socket()->getResponse().ascii(), '(');
+ int port;
+
+ if (!begin || sscanf(begin, "(|||%d|)", &port) != 1) {
+ // Unable to parse, try the next thing
+ socket()->setConfig("feat.epsv", "0");
+ negotiateDataConnection();
+ return;
+ }
+
+ // We have the address, let's setup the transfer socket and then
+ // we are done.
+ currentState = HaveConnection;
+ socket()->setupPassiveTransferSocket(QString::null, port);
+ } else {
+ // Just send the EPSV command
+ currentState = NegotiateEpsv;
+ socket()->sendCommand("EPSV");
+ }
+ }
+
+ void negotiatePasv()
+ {
+ if (currentState == NegotiatePasv) {
+ if (!socket()->isResponse("2")) {
+ // Negotiation failed
+ socket()->setConfig("feat.pasv", "0");
+
+ // Try the next thing
+ negotiateDataConnection();
+ return;
+ }
+
+ // Ok PASV command successfull - let's parse the result
+ int ip[6];
+ char *begin = strchr(socket()->getResponse().ascii(), '(');
+
+ // Some stinky servers don't respect RFC and do it on their own
+ if (!begin)
+ begin = strchr(socket()->getResponse().ascii(), '=');
+
+ if (!begin || (sscanf(begin, "(%d,%d,%d,%d,%d,%d)",&ip[0], &ip[1], &ip[2], &ip[3], &ip[4], &ip[5]) != 6 &&
+ sscanf(begin, "=%d,%d,%d,%d,%d,%d",&ip[0], &ip[1], &ip[2], &ip[3], &ip[4], &ip[5]) != 6)) {
+ // Unable to parse, try the next thing
+ socket()->setConfig("feat.pasv", "0");
+ negotiateDataConnection();
+ return;
+ }
+
+ // Convert to string
+ QString host;
+ int port;
+
+ host.sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ port = ip[4] << 8 | ip[5];
+
+ // If the reported IP address is from a private IP range, this might be because the
+ // remote server is not properly configured. So we just use the server's real IP instead
+ // of the one we got (if the host is really local, then this should work as well).
+ if (!socket()->getConfigInt("feat.pret")) {
+ if (host.startsWith("192.168.") || host.startsWith("10.") || host.startsWith("172.16."))
+ host = socket()->peerAddress().nodeName();
+ }
+
+ // We have the address, let's setup the transfer socket and then
+ // we are done.
+ currentState = HaveConnection;
+ socket()->setupPassiveTransferSocket(host, port);
+ } else {
+ // Just send the PASV command
+ currentState = NegotiatePasv;
+ socket()->sendCommand("PASV");
+ }
+ }
+
+ void negotiateActive()
+ {
+ if (currentState == NegotiateActive) {
+ if (!socket()->isResponse("2")) {
+ if (socket()->getConfigInt("feat.eprt")) {
+ socket()->setConfig("feat.eprt", 0);
+ } else {
+ // Negotiation failed, reset since active is the last fallback
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+ } else {
+ currentState = HaveConnection;
+ socket()->nextCommandAsync();
+ return;
+ }
+ }
+
+ // Setup the socket and set the apropriate port command
+ currentState = NegotiateActive;
+
+ KNetwork::KSocketAddress address = socket()->setupActiveTransferSocket();
+ if (address.address()) {
+ if (socket()->getConfigInt("feat.eprt")) {
+ QString ianaFamily = QString::number(address.ianaFamily());
+
+ socket()->sendCommand("EPRT |" + ianaFamily + "|" + address.nodeName() + "|" + address.serviceName() + "|");
+ } else if (address.ianaFamily() == 1) {
+ QString format = address.nodeName().replace(".", ",");
+
+ format.append(",");
+ format.append(QString::number((unsigned char) address.address()->sa_data[0]));
+ format.append(",");
+ format.append(QString::number((unsigned char) address.address()->sa_data[1]));
+
+ socket()->sendCommand("PORT " + format);
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Incompatible address family for PORT, but EPRT not supported, aborting!"));
+ socket()->resetCommandClass(Failed);
+ }
+ }
+ }
+};
+
+void FtpSocket::initializeTransferSocket()
+{
+ m_transferConnecting = true;
+ m_transferEnd = 0;
+ m_transferBytes = 0;
+ m_transferBufferSize = 4096;
+ m_transferBuffer = (char*) malloc(m_transferBufferSize);
+
+ m_speedLastTime = time(0);
+ m_speedLastBytes = 0;
+
+ // Setup the speed limiter
+ switch (getPreviousCommand()) {
+ case Commands::CmdGet: SpeedLimiter::self()->append(this, SpeedLimiter::Download); break;
+ case Commands::CmdPut: SpeedLimiter::self()->append(this, SpeedLimiter::Upload); break;
+ default: break;
+ }
+
+ m_transferSocket->enableRead(false);
+ m_transferSocket->setBlocking(false);
+ m_transferSocket->setAddressReuseable(true);
+}
+
+void FtpSocket::setupPassiveTransferSocket(const QString &host, int port)
+{
+ // Use the host from control connection if empty
+ QString realHost = host;
+ if (host.isEmpty() || getConfigInt("pasv.use_site_ip"))
+ realHost = peerAddress().nodeName();
+
+ // Let's connect
+ emitEvent(Event::EventMessage, i18n("Establishing data connection with %1:%2...").arg(realHost).arg(port));
+
+ if (!m_transferSocket)
+ m_transferSocket = new KNetwork::KStreamSocket();
+
+ initializeTransferSocket();
+ m_transferSocket->connect(realHost, QString::number(port));
+}
+
+KNetwork::KSocketAddress FtpSocket::setupActiveTransferSocket()
+{
+ if (!m_serverSocket)
+ m_serverSocket = new KNetwork::KServerSocket();
+
+ m_serverSocket->setAcceptBuffered(false);
+ m_serverSocket->setFamily(KNetwork::KResolver::InetFamily);
+
+ if (KFTPCore::Config::activeForcePort()) {
+ // Bind only to ports in a specified portrange
+ bool found = false;
+ unsigned int max = KFTPCore::Config::activeMaxPort();
+ unsigned int min = KFTPCore::Config::activeMinPort();
+
+ for (unsigned int port = min + rand() % (max - min + 1); port <= max; port++) {
+ m_serverSocket->setAddress(QString::number(port));
+ bool success = m_serverSocket->listen();
+
+ if (found = (success && m_serverSocket->error() == KSocketBase::NoError))
+ break;
+
+ m_serverSocket->close();
+ }
+
+ if (!found) {
+ emitEvent(Event::EventMessage, i18n("Unable to establish a listening socket."));
+ resetCommandClass(Failed);
+ return KNetwork::KSocketAddress();
+ }
+ } else {
+ m_serverSocket->setAddress("0");
+
+ if (!m_serverSocket->listen()) {
+ emitEvent(Event::EventMessage, i18n("Unable to establish a listening socket."));
+ resetCommandClass(Failed);
+ return KNetwork::KSocketAddress();
+ }
+ }
+
+ KNetwork::KSocketAddress serverAddr = m_serverSocket->localAddress();
+ KNetwork::KSocketAddress controlAddr = localAddress();
+ KNetwork::KSocketAddress request;
+
+ if (KFTPCore::Config::portForceIp() && !getConfigInt("active.no_force_ip")) {
+ QString remoteIp = peerAddress().nodeName();
+
+ if (KFTPCore::Config::ignoreExternalIpForLan() &&
+ (remoteIp.startsWith("192.168.") || remoteIp.startsWith("10.") || remoteIp.startsWith("172.16."))) {
+ request = controlAddr;
+ } else {
+ // Force a specified IP/hostname to be used in PORT
+ KNetwork::KResolverResults resolverResults;
+
+ resolverResults = KNetwork::KResolver::resolve(KFTPCore::Config::portIp(), "21");
+ if (resolverResults.error() < 0) {
+ // Well, we are unable to resolve the name, so we should use what we got
+ // from control socket
+ request = controlAddr;
+ } else {
+ // The name has been resolved and we have the address, so we should
+ // use it
+ request = resolverResults[0].address();
+ }
+ }
+ } else {
+ // Just use our IP we bound to when connecting to the remote server
+ request = controlAddr;
+ }
+
+ // Set the proper port
+ request.address()->sa_data[0] = serverAddr.address()->sa_data[0];
+ request.address()->sa_data[1] = serverAddr.address()->sa_data[1];
+
+ emitEvent(Event::EventMessage, i18n("Waiting for data connection on port %1...").arg(serverAddr.serviceName()));
+
+ return request;
+}
+
+void FtpSocket::slotDataAccept(KNetwork::KStreamSocket *socket)
+{
+ m_transferSocket = socket;
+ initializeTransferSocket();
+
+ // Socket has been accepted so the server is not needed anymore
+ delete m_serverSocket;
+
+ emitEvent(Event::EventMessage, i18n("Data connection established."));
+ checkTransferStart();
+}
+
+void FtpSocket::closeDataTransferSocket()
+{
+ if (m_dataSsl) {
+ m_dataSsl->close();
+ delete m_dataSsl;
+ m_dataSsl = 0;
+ }
+
+ // Free the buffer and invalidate the socket
+ free(m_transferBuffer);
+
+ m_transferSocket->close();
+ delete m_transferSocket;
+ m_transferBytes = 0;
+
+ SpeedLimiter::self()->remove(this);
+}
+
+void FtpSocket::transferCompleted()
+{
+ // Transfer has been completed, cleanup
+ closeDataTransferSocket();
+ checkTransferEnd();
+}
+
+void FtpSocket::checkTransferStart()
+{
+ if (++m_transferStart >= 2) {
+ // Setup SSL data connection
+ if (getConfigInt("ssl") && (getConfigInt("ssl.prot_mode") == 0 ||
+ (getConfigInt("ssl.prot_mode") == 1 && getToplevelCommand() == Commands::CmdList)) && !m_dataSsl) {
+ m_dataSsl = new Ssl(m_transferSocket);
+
+ if (m_dataSsl->connect()) {
+ emitEvent(Event::EventMessage, i18n("Data channel secured with %1 bit SSL.").arg(m_dataSsl->connectionInfo().getCipherUsedBits()));
+ } else {
+ emitEvent(Event::EventMessage, i18n("SSL negotiation for the data channel has failed. Aborting transfer."));
+ resetCommandClass(Failed);
+ return;
+ }
+ }
+ }
+}
+
+void FtpSocket::checkTransferEnd()
+{
+ if (++m_transferEnd >= 2) {
+ emitEvent(Event::EventMessage, i18n("Transfer completed."));
+ resetCommandClass();
+ }
+}
+
+void FtpSocket::slotDataConnected()
+{
+ emitEvent(Event::EventMessage, i18n("Data connection established."));
+
+ checkTransferStart();
+ nextCommand();
+}
+
+void FtpSocket::variableBufferUpdate(Q_LONG size)
+{
+ if (size > m_transferBufferSize - 64) {
+ if (m_transferBufferSize + 512 <= 32768) {
+ m_transferBufferSize += 512;
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ }
+ } else if (size < m_transferBufferSize - 65) {
+ if (m_transferBufferSize - 512 >= 4096) {
+ m_transferBufferSize -= 512;
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ }
+ }
+}
+
+void FtpSocket::slotDataTryWrite()
+{
+ bool updateVariableBuffer = true;
+
+ // Enforce speed limits
+ if (allowedBytes() > -1) {
+ m_transferBufferSize = allowedBytes();
+
+ if (m_transferBufferSize > 32768)
+ m_transferBufferSize = 32768;
+ else if (m_transferBufferSize == 0)
+ return;
+
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ updateVariableBuffer = false;
+ } else if (m_transferBufferSize == 0) {
+ m_transferBufferSize = 4096;
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ }
+
+ if (!getTransferFile()->isOpen())
+ return;
+
+ // If there is nothing to upload, just close the connection right away
+ if (getTransferFile()->size() == 0) {
+ transferCompleted();
+ return;
+ }
+
+ QFile::Offset tmpOffset = getTransferFile()->at();
+ Q_LONG readSize = getTransferFile()->readBlock(m_transferBuffer, m_transferBufferSize);
+
+ Q_LONG size = 0;
+
+ if (m_dataSsl)
+ size = m_dataSsl->write(m_transferBuffer, readSize);
+ else
+ size = m_transferSocket->writeBlock(m_transferBuffer, readSize);
+
+ if (size < 0) {
+ getTransferFile()->at(tmpOffset);
+ return;
+ } else if (size < readSize)
+ getTransferFile()->at(tmpOffset + size);
+
+ m_transferBytes += size;
+ updateUsage(size);
+ timeoutPing();
+
+ if (getTransferFile()->atEnd()) {
+ // We have reached the end of file, so we should terminate the connection
+ transferCompleted();
+ return;
+ }
+
+ if (updateVariableBuffer)
+ variableBufferUpdate(size);
+}
+
+void FtpSocket::slotDataTryRead()
+{
+ bool updateVariableBuffer = true;
+
+ // Enforce speed limits
+ if (allowedBytes() > -1) {
+ m_transferBufferSize = allowedBytes();
+
+ if (m_transferBufferSize > 32768)
+ m_transferBufferSize = 32768;
+ else if (m_transferBufferSize == 0)
+ return;
+
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ updateVariableBuffer = false;
+ } else if (m_transferBufferSize == 0) {
+ m_transferBufferSize = 4096;
+ m_transferBuffer = (char*) realloc(m_transferBuffer, m_transferBufferSize);
+ }
+
+ Q_LONG size = 0;
+
+ if (m_dataSsl) {
+ size = m_dataSsl->read(m_transferBuffer, m_transferBufferSize);
+
+ if (size == -1) {
+ transferCompleted();
+ return;
+ }
+ } else {
+ size = m_transferSocket->readBlock(m_transferBuffer, m_transferBufferSize);
+
+ // Check if the connection has been closed
+ if (m_transferSocket->error() != NoError) {
+ if (m_transferSocket->error() != WouldBlock) {
+ transferCompleted();
+ return;
+ }
+ }
+ }
+
+ if (size <= 0) {
+ if (!m_dataSsl)
+ transferCompleted();
+
+ return;
+ }
+
+ updateUsage(size);
+ timeoutPing();
+
+ switch (getPreviousCommand()) {
+ case Commands::CmdList: {
+ // Feed the data to the directory listing parser
+ if (m_directoryParser)
+ m_directoryParser->addData(m_transferBuffer, size);
+ break;
+ }
+ case Commands::CmdGet: {
+ // Write to file
+ getTransferFile()->writeBlock(m_transferBuffer, size);
+ m_transferBytes += size;
+ break;
+ }
+ default: {
+ qDebug("WARNING: slotDataReadActivity called for an invalid command!");
+ return;
+ }
+ }
+
+ if (updateVariableBuffer)
+ variableBufferUpdate(size);
+}
+
+// *******************************************************************************************
+// ******************************************* LIST ******************************************
+// *******************************************************************************************
+
+class FtpCommandList : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentCwd,
+ SentStat,
+ WaitList
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandList, FtpSocket, CmdList)
+
+ QString path;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ path = socket()->getConfig("params.list.path");
+
+ if (socket()->isChained())
+ socket()->m_lastDirectoryListing = DirectoryListing();
+
+ // Change working directory
+ currentState = SentCwd;
+ socket()->changeWorkingDirectory(path);
+ break;
+ }
+ case SentCwd: {
+ if (!socket()->getConfigInt("status.cwd")) {
+ // Change directory has failed and we should be silent (=error reporting is off)
+ socket()->resetCommandClass();
+ return;
+ }
+
+ // Check the directory listing cache
+ DirectoryListing cached = Cache::self()->findCached(socket(), socket()->getCurrentDirectory());
+ if (cached.isValid()) {
+ socket()->emitEvent(Event::EventMessage, i18n("Using cached directory listing."));
+
+ if (socket()->isChained()) {
+ // We don't emit an event, because this list has been called from another
+ // command. Just save the listing.
+ socket()->m_lastDirectoryListing = cached;
+ } else
+ socket()->emitEvent(Event::EventDirectoryListing, cached);
+
+ socket()->resetCommandClass();
+ return;
+ }
+
+ socket()->m_directoryParser = new FtpDirectoryParser(socket());
+
+ // Support for faster stat directory listings over the control connection
+ if (socket()->getConfigInt("stat_listings")) {
+ currentState = SentStat;
+ socket()->sendCommand("STAT .");
+ return;
+ }
+
+ // First we have to initialize the data connection, another class will
+ // do this for us, so we just add it to the command chain
+ socket()->setConfig("params.data_rest_do", 0);
+ socket()->setConfig("params.data_type", 'A');
+
+ if (socket()->getConfigInt("feat.mlsd"))
+ socket()->setConfig("params.data_command", "MLSD");
+ else
+ socket()->setConfig("params.data_command", "LIST -a");
+
+ currentState = WaitList;
+ chainCommandClass(FtpCommandNegotiateData);
+ break;
+ }
+ case SentStat: {
+ if (!socket()->isResponse("2")) {
+ // The server doesn't support STAT, disable it and fallback
+ socket()->setConfig("stat_listings", 0);
+
+ socket()->setConfig("params.data_rest_do", 0);
+ socket()->setConfig("params.data_type", 'A');
+
+ if (socket()->getConfigInt("feat.mlsd"))
+ socket()->setConfig("params.data_command", "MLSD");
+ else
+ socket()->setConfig("params.data_command", "LIST -a");
+
+ currentState = WaitList;
+ chainCommandClass(FtpCommandNegotiateData);
+ return;
+ } else if (socket()->isMultiline()) {
+ // Some servers put the response code into the multiline reply
+ QString response = socket()->getResponse();
+ if (response.left(3) == "211")
+ response = response.mid(4);
+
+ socket()->m_directoryParser->addDataLine(response);
+ return;
+ }
+
+ // If we are done, just go on and emit the listing
+ }
+ case WaitList: {
+ // List has been received
+ if (socket()->isChained()) {
+ // We don't emit an event, because this list has been called from another
+ // command. Just save the listing.
+ socket()->m_lastDirectoryListing = socket()->m_directoryParser->getListing();
+ } else
+ socket()->emitEvent(Event::EventDirectoryListing, socket()->m_directoryParser->getListing());
+
+ // Cache the directory listing
+ Cache::self()->addDirectory(socket(), socket()->m_directoryParser->getListing());
+
+ delete socket()->m_directoryParser;
+ socket()->m_directoryParser = 0;
+
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoList(const KURL &path)
+{
+ emitEvent(Event::EventState, i18n("Fetching directory listing..."));
+ emitEvent(Event::EventMessage, i18n("Fetching directory listing..."));
+
+ // Set the directory that should be listed
+ setConfig("params.list.path", path.path());
+
+ activateCommandClass(FtpCommandList);
+}
+
+// *******************************************************************************************
+// ******************************************* GET *******************************************
+// *******************************************************************************************
+
+class FtpCommandGet : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentCwd,
+ SentMdtm,
+ StatDone,
+ DestChecked,
+ WaitTransfer
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandGet, FtpSocket, CmdGet)
+
+ KURL sourceFile;
+ KURL destinationFile;
+ time_t modificationTime;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ modificationTime = 0;
+ sourceFile.setPath(socket()->getConfig("params.get.source"));
+ destinationFile.setPath(socket()->getConfig("params.get.destination"));
+
+ // Attempt to CWD to the parent directory
+ currentState = SentCwd;
+ socket()->changeWorkingDirectory(sourceFile.directory());
+ break;
+ }
+ case SentCwd: {
+ // Send MDTM
+ if (socket()->getConfigInt("feat.mdtm")) {
+ currentState = SentMdtm;
+ socket()->sendCommand("MDTM " + sourceFile.path());
+ break;
+ } else {
+ // Don't break so we will get on to checking for file existance
+ }
+ }
+ case SentMdtm: {
+ if (currentState == SentMdtm) {
+ if (socket()->isResponse("550")) {
+ // The file probably doesn't exist, just ignore it
+ } else if (!socket()->isResponse("213")) {
+ socket()->setConfig("feat.mdtm", 0);
+ } else {
+ // Parse MDTM response
+ struct tm dt = {0,0,0,0,0,0,0,0,0,0,0};
+ QString tmp(socket()->getResponse());
+
+ tmp.remove(0, 4);
+ dt.tm_year = tmp.left(4).toInt() - 1900;
+ dt.tm_mon = tmp.mid(4, 2).toInt() - 1;
+ dt.tm_mday = tmp.mid(6, 2).toInt();
+ dt.tm_hour = tmp.mid(8, 2).toInt();
+ dt.tm_min = tmp.mid(10, 2).toInt();
+ dt.tm_sec = tmp.mid(12, 2).toInt();
+ modificationTime = mktime(&dt);
+ }
+ }
+
+ // Check if the local file exists and stat the remote file if so
+ if (QDir::root().exists(destinationFile.path())) {
+ socket()->protoStat(sourceFile);
+ currentState = StatDone;
+ return;
+ } else {
+ KStandardDirs::makeDir(destinationFile.directory());
+
+ // Don't break so we will get on to initiating the data connection
+ }
+ }
+ case StatDone: {
+ if (currentState == StatDone) {
+ DirectoryListing list;
+ list.addEntry(socket()->getStatResponse());
+
+ currentState = DestChecked;
+ socket()->emitEvent(Event::EventFileExists, list);
+ return;
+ }
+ }
+ case DestChecked: {
+ socket()->setConfig("params.data_rest_do", 0);
+
+ if (isWakeup()) {
+ // We have been waken up because a decision has been made
+ FileExistsWakeupEvent *event = static_cast<FileExistsWakeupEvent*>(m_wakeupEvent);
+
+ if (!socket()->getConfigInt("feat.rest") && event->action == FileExistsWakeupEvent::Resume)
+ event->action = FileExistsWakeupEvent::Overwrite;
+
+ switch (event->action) {
+ case FileExistsWakeupEvent::Rename: {
+ // Change the destination filename, otherwise it is the same as overwrite
+ destinationFile.setPath(event->newFileName);
+ }
+ case FileExistsWakeupEvent::Overwrite: {
+ socket()->getTransferFile()->setName(destinationFile.path());
+ socket()->getTransferFile()->open(IO_WriteOnly | IO_Truncate);
+
+ if (socket()->getConfigInt("feat.rest")) {
+ socket()->setConfig("params.data_rest_do", 1);
+ socket()->setConfig("params.data_rest", 0);
+ }
+ break;
+ }
+ case FileExistsWakeupEvent::Resume: {
+ socket()->getTransferFile()->setName(destinationFile.path());
+ socket()->getTransferFile()->open(IO_WriteOnly | IO_Append);
+
+ // Signal resume
+ socket()->emitEvent(Event::EventResumeOffset, socket()->getTransferFile()->size());
+
+ socket()->setConfig("params.data_rest_do", 1);
+ socket()->setConfig("params.data_rest", (filesize_t) socket()->getTransferFile()->size());
+ break;
+ }
+ case FileExistsWakeupEvent::Skip: {
+ // Transfer should be aborted
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ return;
+ }
+ }
+ } else {
+ // The file doesn't exist so we are free to overwrite
+ socket()->getTransferFile()->setName(destinationFile.path());
+ socket()->getTransferFile()->open(IO_WriteOnly | IO_Truncate);
+ }
+
+ // Check if there was a problem opening the file
+ if (!socket()->getTransferFile()->isOpen()) {
+ socket()->emitError(FileOpenFailed);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ // First we have to initialize the data connection, another class will
+ // do this for us, so we just add it to the command chain
+ socket()->setConfig("params.data_type", KFTPCore::Config::self()->ftpMode(sourceFile.path()));
+ socket()->setConfig("params.data_command", "RETR " + sourceFile.filename());
+
+ currentState = WaitTransfer;
+ chainCommandClass(FtpCommandNegotiateData);
+ break;
+ }
+ case WaitTransfer: {
+ // Transfer has been completed
+ socket()->getTransferFile()->close();
+
+ if (modificationTime != 0) {
+ // Use the modification time we got from MDTM
+ utimbuf tmp;
+ tmp.actime = time(0);
+ tmp.modtime = modificationTime;
+ utime(destinationFile.path().latin1(), &tmp);
+ }
+
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoGet(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Transfering..."));
+ emitEvent(Event::EventMessage, i18n("Downloading file '%1'...").arg(source.fileName()));
+
+ // Set the source and destination
+ setConfig("params.get.source", source.path());
+ setConfig("params.get.destination", destination.path());
+
+ activateCommandClass(FtpCommandGet);
+}
+
+// *******************************************************************************************
+// ******************************************* CWD *******************************************
+// *******************************************************************************************
+
+class FtpCommandCwd : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentCwd,
+ SentPwd,
+ SentMkd,
+ SentCwdEnd
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandCwd, FtpSocket, CmdNone)
+
+ QString targetDirectory;
+ QString currentPathPart;
+ QString cached;
+ int currentPart;
+ int numParts;
+ bool shouldCreate;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ targetDirectory = socket()->getConfig("params.cwd.path");
+ socket()->setConfig("status.cwd", 1);
+
+ // If we are already there, no need to CWD
+ if (socket()->getCurrentDirectory() == targetDirectory) {
+ socket()->resetCommandClass();
+ return;
+ }
+
+ cached = Cache::self()->findCachedPath(socket(), targetDirectory);
+ if (!cached.isEmpty()) {
+ if (socket()->getCurrentDirectory() == cached) {
+ // We are already there
+ socket()->resetCommandClass();
+ return;
+ }
+ }
+
+ // First check the toplevel directory and if it exists we are done
+ currentState = SentCwd;
+ currentPart = 0;
+ numParts = targetDirectory.contains('/');
+ shouldCreate = socket()->getConfigInt("params.cwd.create");
+
+ socket()->sendCommand("CWD " + targetDirectory);
+ break;
+ }
+ case SentCwd: {
+ if (socket()->isMultiline())
+ return;
+
+ if (socket()->isResponse("250") && currentPart == 0) {
+ if (!cached.isEmpty()) {
+ socket()->setCurrentDirectory(cached);
+ socket()->resetCommandClass();
+ } else {
+ // Directory exists, check where we are
+ currentState = SentPwd;
+ socket()->sendCommand("PWD");
+ }
+ } else {
+ // Changing the working directory has failed
+ if (shouldCreate) {
+ currentPathPart = targetDirectory.section('/', 0, ++currentPart);
+ currentState = SentMkd;
+ socket()->sendCommand("MKD " + currentPathPart);
+ } else if (socket()->errorReporting()) {
+ socket()->emitError(socket()->getPreviousCommand() == Commands::CmdList ? ListFailed : FileNotFound);
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("status.cwd", 0);
+ socket()->resetCommandClass();
+ }
+ }
+ break;
+ }
+ case SentPwd: {
+ // Parse the current working directory
+ if (socket()->isResponse("2")) {
+ QString tmp = socket()->getResponse();
+ int first = tmp.find('"') + 1;
+ tmp = tmp.mid(first, tmp.findRev('"') - first);
+
+ // Set the current directory and cache it
+ socket()->setCurrentDirectory(tmp);
+ Cache::self()->addPath(socket(), tmp);
+
+ socket()->resetCommandClass();
+ } else if (socket()->errorReporting()) {
+ socket()->emitError(socket()->getPreviousCommand() == Commands::CmdList ? ListFailed : FileNotFound);
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("status.cwd", 0);
+ socket()->resetCommandClass();
+ }
+ break;
+ }
+ case SentMkd: {
+ // Invalidate parent cache
+ if (socket()->isResponse("2")) {
+ Cache::self()->invalidateEntry(socket(), KURL(currentPathPart).directory());
+ }
+
+ if (currentPart == numParts) {
+ // We are done, since all directories have been created
+ currentState = SentCwdEnd;
+ socket()->sendCommand("CWD " + targetDirectory);
+ } else {
+ currentPathPart = targetDirectory.section('/', 0, ++currentPart);
+ currentState = SentMkd;
+ socket()->sendCommand("MKD " + currentPathPart);
+ }
+ break;
+ }
+ case SentCwdEnd: {
+ if (socket()->isMultiline())
+ return;
+
+ // See where we are and set current working directory
+ currentState = SentPwd;
+ socket()->sendCommand("PWD");
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::changeWorkingDirectory(const QString &path, bool shouldCreate)
+{
+ // Set the path to cwd to
+ setConfig("params.cwd.path", path);
+ setConfig("params.cwd.create", shouldCreate);
+
+ activateCommandClass(FtpCommandCwd);
+}
+
+// *******************************************************************************************
+// ******************************************* PUT *******************************************
+// *******************************************************************************************
+
+class FtpCommandPut : public Commands::Base {
+public:
+ enum State {
+ None,
+ WaitCwd,
+ SentSize,
+ StatDone,
+ DestChecked,
+ WaitTransfer
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandPut, FtpSocket, CmdPut)
+
+ KURL sourceFile;
+ KURL destinationFile;
+
+ bool fetchedSize;
+ filesize_t destinationSize;
+
+ void cleanup()
+ {
+ // Unclean upload termination, be sure to erase the cached stat infos
+ Cache::self()->invalidateEntry(socket(), destinationFile.directory());
+ }
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ sourceFile.setPath(socket()->getConfig("params.get.source"));
+ destinationFile.setPath(socket()->getConfig("params.get.destination"));
+ fetchedSize = false;
+
+ // Check if the local file exists
+ if (!QDir::root().exists(sourceFile.path())) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ // Change to the current working directory, creating any directories that are
+ // still missing
+ currentState = WaitCwd;
+ socket()->changeWorkingDirectory(destinationFile.directory(), true);
+ break;
+ }
+ case WaitCwd: {
+ // Check if the remote file exists
+ if (socket()->getConfigInt("feat.size")) {
+ currentState = SentSize;
+ socket()->sendCommand("SIZE " + destinationFile.path());
+ } else {
+ // SIZE is not available, try stat directly
+ currentState = StatDone;
+ socket()->protoStat(destinationFile);
+ }
+ break;
+ }
+ case SentSize: {
+ if (socket()->isResponse("213")) {
+ destinationSize = socket()->getResponse().mid(4).toULongLong();
+ fetchedSize = true;
+
+ // File exists, we have to stat to get more data
+ currentState = StatDone;
+ socket()->protoStat(destinationFile);
+ } else if (socket()->isResponse("500") || socket()->getResponse().contains("Operation not permitted", false)) {
+ // Yes, some servers don't support the SIZE command :/
+ socket()->setConfig("feat.size", 0);
+
+ currentState = StatDone;
+ socket()->protoStat(destinationFile);
+ } else {
+ currentState = DestChecked;
+ process();
+ }
+ break;
+ }
+ case StatDone: {
+ if (!socket()->getStatResponse().filename().isEmpty()) {
+ if (fetchedSize) {
+ if (socket()->getStatResponse().size() != destinationSize) {
+ // It would seem that the size has changed, cached data is invalid
+ Cache::self()->invalidateEntry(socket(), destinationFile.directory());
+
+ currentState = StatDone;
+ socket()->protoStat(destinationFile);
+ return;
+ }
+ }
+
+ // Remote file exists, emit a request for action
+ DirectoryListing list;
+ list.addEntry(socket()->getStatResponse());
+
+ currentState = DestChecked;
+ socket()->emitEvent(Event::EventFileExists, list);
+ return;
+ }
+
+ // Don't break here
+ }
+ case DestChecked: {
+ socket()->setConfig("params.data_rest_do", 0);
+
+ if (isWakeup()) {
+ // We have been waken up because a decision has been made
+ FileExistsWakeupEvent *event = static_cast<FileExistsWakeupEvent*>(m_wakeupEvent);
+
+ if (!socket()->getConfigInt("feat.rest") && event->action == FileExistsWakeupEvent::Resume)
+ event->action = FileExistsWakeupEvent::Overwrite;
+
+ switch (event->action) {
+ case FileExistsWakeupEvent::Rename: {
+ // Change the destination filename, otherwise it is the same as overwrite
+ destinationFile.setPath(event->newFileName);
+ }
+ case FileExistsWakeupEvent::Overwrite: {
+ socket()->getTransferFile()->setName(sourceFile.path());
+ socket()->getTransferFile()->open(IO_ReadOnly);
+
+ if (socket()->getConfigInt("feat.rest")) {
+ socket()->setConfig("params.data_rest_do", 1);
+ socket()->setConfig("params.data_rest", 0);
+ }
+ break;
+ }
+ case FileExistsWakeupEvent::Resume: {
+ socket()->getTransferFile()->setName(sourceFile.path());
+ socket()->getTransferFile()->open(IO_ReadOnly);
+ socket()->getTransferFile()->at(socket()->getStatResponse().size());
+
+ // Signal resume
+ socket()->emitEvent(Event::EventResumeOffset, socket()->getStatResponse().size());
+
+ socket()->setConfig("params.data_rest_do", 1);
+ socket()->setConfig("params.data_rest", (filesize_t) socket()->getStatResponse().size());
+ break;
+ }
+ case FileExistsWakeupEvent::Skip: {
+ // Transfer should be aborted
+ markClean();
+
+ socket()->resetCommandClass(UserAbort);
+ socket()->emitEvent(Event::EventTransferComplete);
+ return;
+ }
+ }
+ } else {
+ // The file doesn't exist so we are free to overwrite
+ socket()->getTransferFile()->setName(sourceFile.path());
+ socket()->getTransferFile()->open(IO_ReadOnly);
+ }
+
+ // Check if there was a problem opening the file
+ if (!socket()->getTransferFile()->isOpen()) {
+ socket()->emitError(FileOpenFailed);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ // First we have to initialize the data connection, another class will
+ // do this for us, so we just add it to the command chain
+ socket()->setConfig("params.data_type", KFTPCore::Config::self()->ftpMode(destinationFile.path()));
+ socket()->setConfig("params.data_command", "STOR " + destinationFile.filename());
+
+ currentState = WaitTransfer;
+ chainCommandClass(FtpCommandNegotiateData);
+ break;
+ }
+ case WaitTransfer: {
+ // Transfer has been completed
+ Cache::self()->updateDirectoryEntry(socket(), destinationFile, socket()->getTransferFile()->size());
+ socket()->getTransferFile()->close();
+ markClean();
+
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoPut(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Transfering..."));
+ emitEvent(Event::EventMessage, i18n("Uploading file '%1'...").arg(source.fileName()));
+
+ // Set the source and destination
+ setConfig("params.get.source", source.path());
+ setConfig("params.get.destination", destination.path());
+
+ activateCommandClass(FtpCommandPut);
+}
+
+// *******************************************************************************************
+// **************************************** REMOVE *******************************************
+// *******************************************************************************************
+
+class FtpCommandRemove : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentCwd,
+ SentRemove
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandRemove, FtpSocket, CmdNone)
+
+ QString destinationPath;
+ QString parentDirectory;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ destinationPath = socket()->getConfig("params.remove.path");
+ parentDirectory = socket()->getConfig("params.remove.parent");
+
+ currentState = SentRemove;
+
+ if (socket()->getConfigInt("params.remove.directory")) {
+ if (socket()->getCurrentDirectory() != parentDirectory) {
+ // We should change working directory to parent directory before removing
+ currentState = SentCwd;
+ socket()->sendCommand("CWD " + parentDirectory);
+ } else {
+ socket()->sendCommand("RMD " + destinationPath);
+ }
+ } else {
+ socket()->sendCommand("DELE " + destinationPath);
+ }
+ break;
+ }
+ case SentCwd: {
+ if (socket()->isMultiline())
+ return;
+
+ if (socket()->isResponse("2")) {
+ // CWD was successful
+ socket()->setCurrentDirectory(parentDirectory);
+ }
+
+ currentState = SentRemove;
+ socket()->sendCommand("RMD " + destinationPath);
+ break;
+ }
+ case SentRemove: {
+ if (socket()->isMultiline())
+ return;
+
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(socket(), parentDirectory);
+ Cache::self()->invalidatePath(socket(), destinationPath);
+
+ if (!socket()->isChained())
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ }
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoRemove(const KURL &path)
+{
+ emitEvent(Event::EventState, i18n("Removing..."));
+
+ // Set the file to remove
+ setConfig("params.remove.parent", path.directory());
+ setConfig("params.remove.path", path.path());
+
+ activateCommandClass(FtpCommandRemove);
+}
+
+// *******************************************************************************************
+// **************************************** RENAME *******************************************
+// *******************************************************************************************
+
+class FtpCommandRename : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentRnfr,
+ SentRnto
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandRename, FtpSocket, CmdRename)
+
+ QString sourcePath;
+ QString destinationPath;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ sourcePath = socket()->getConfig("params.rename.source");
+ destinationPath = socket()->getConfig("params.rename.destination");
+
+ currentState = SentRnfr;
+ socket()->sendCommand("RNFR " + sourcePath);
+ break;
+ }
+ case SentRnfr: {
+ if (socket()->isResponse("3")) {
+ currentState = SentRnto;
+ socket()->sendCommand("RNTO " + destinationPath);
+ } else
+ socket()->resetCommandClass(Failed);
+ break;
+ }
+ case SentRnto: {
+ if (socket()->isResponse("2")) {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(socket(), KURL(sourcePath).directory());
+ Cache::self()->invalidateEntry(socket(), KURL(destinationPath).directory());
+
+ Cache::self()->invalidatePath(socket(), sourcePath);
+ Cache::self()->invalidatePath(socket(), destinationPath);
+
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ } else
+ socket()->resetCommandClass(Failed);
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoRename(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Renaming..."));
+
+ // Set rename options
+ setConfig("params.rename.source", source.path());
+ setConfig("params.rename.destination", destination.path());
+
+ activateCommandClass(FtpCommandRename);
+}
+
+// *******************************************************************************************
+// **************************************** CHMOD ********************************************
+// *******************************************************************************************
+
+class FtpCommandChmod : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentChmod
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandChmod, FtpSocket, CmdChmod)
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ currentState = SentChmod;
+
+ QString chmod;
+ chmod.sprintf("SITE CHMOD %.3d %s", socket()->getConfigInt("params.chmod.mode"),
+ socket()->getConfig("params.chmod.path").ascii());
+ socket()->sendCommand(chmod);
+ break;
+ }
+ case SentChmod: {
+ if (!socket()->isResponse("2"))
+ socket()->resetCommandClass(Failed);
+ else {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(socket(), KURL(socket()->getConfig("params.chmod.path")).directory());
+
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ }
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoChmodSingle(const KURL &path, int mode)
+{
+ emitEvent(Event::EventState, i18n("Changing mode..."));
+
+ // Set chmod options
+ setConfig("params.chmod.path", path.path());
+ setConfig("params.chmod.mode", mode);
+
+ activateCommandClass(FtpCommandChmod);
+}
+
+// *******************************************************************************************
+// **************************************** MKDIR ********************************************
+// *******************************************************************************************
+
+class FtpCommandMkdir : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentMkdir
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandMkdir, FtpSocket, CmdMkdir)
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ currentState = SentMkdir;
+ socket()->changeWorkingDirectory(socket()->getConfig("params.mkdir.path"), true);
+ break;
+ }
+ case SentMkdir: {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(socket(), KURL(socket()->getCurrentDirectory()).directory());
+
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoMkdir(const KURL &path)
+{
+ emitEvent(Event::EventState, i18n("Making directory..."));
+
+ setConfig("params.mkdir.path", path.path());
+ activateCommandClass(FtpCommandMkdir);
+}
+
+// *******************************************************************************************
+// ******************************************* RAW *******************************************
+// *******************************************************************************************
+
+class FtpCommandRaw : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentRaw
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandRaw, FtpSocket, CmdRaw)
+
+ QString response;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ currentState = SentRaw;
+ socket()->sendCommand(socket()->getConfig("params.raw.command"));
+ break;
+ }
+ case SentRaw: {
+ response.append(socket()->getResponse());
+
+ if (!socket()->isMultiline()) {
+ socket()->emitEvent(Event::EventRaw, response);
+ socket()->resetCommandClass();
+ }
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoRaw(const QString &raw)
+{
+ setConfig("params.raw.command", raw);
+ activateCommandClass(FtpCommandRaw);
+}
+
+// *******************************************************************************************
+// ******************************************* FXP *******************************************
+// *******************************************************************************************
+
+class FtpCommandFxp : public Commands::Base {
+public:
+ enum State {
+ None,
+
+ // Source socket
+ SourceSentCwd,
+ SourceSentStat,
+ SourceDestVerified,
+ SourceSentType,
+ SourceSentSscn,
+ SourceSentProt,
+ SourceWaitType,
+ SourceSentPret,
+ SourceSentPasv,
+ SourceDoRest,
+ SourceSentRest,
+ SourceDoRetr,
+ SourceSentRetr,
+ SourceWaitTransfer,
+ SourceResetProt,
+
+ // Destination socket
+ DestSentStat,
+ DestWaitCwd,
+ DestDoType,
+ DestSentType,
+ DestSentSscn,
+ DestSentProt,
+ DestDoPort,
+ DestSentPort,
+ DestSentRest,
+ DestDoStor,
+ DestSentStor,
+ DestWaitTransfer,
+ DestResetProt
+ };
+
+ enum ProtectionMode {
+ ProtClear = 0,
+ ProtPrivate = 1,
+ ProtSSCN = 2
+ };
+
+ enum TransferMode {
+ TransferPASV = 0,
+ TransferCPSV = 1
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandFxp, FtpSocket, CmdFxp)
+
+ FtpSocket *companion;
+
+ KURL sourceFile;
+ KURL destinationFile;
+ filesize_t resumeOffset;
+
+ void cleanup()
+ {
+ // We have been interrupted, so we have to abort the companion as well
+ if (!socket()->getConfigInt("params.fxp.abort")) {
+ companion->setConfig("params.fxp.abort", 1);
+ companion->protoAbort();
+ }
+
+ // Unclean upload termination, be sure to erase the cached stat infos
+ if (!socket()->getConfigInt("params.fxp.keep_cache"))
+ Cache::self()->invalidateEntry(socket(), destinationFile.directory());
+ }
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ sourceFile.setPath(socket()->getConfig("params.fxp.source"));
+ destinationFile.setPath(socket()->getConfig("params.fxp.destination"));
+ socket()->setConfig("params.fxp.keep_cache", 0);
+
+ // Who are we ? Where shall we begin ?
+ if (socket()->getConfigInt("params.fxp.companion")) {
+ // We are the companion, so we should check the destination
+ socket()->setConfig("params.fxp.companion", 0);
+
+ currentState = DestSentStat;
+ socket()->protoStat(destinationFile);
+ return;
+ } else {
+ socket()->setConfig("params.transfer.mode", TransferPASV);
+
+ if (socket()->getCurrentDirectory() != sourceFile.directory()) {
+ // Attempt to CWD to the parent directory
+ currentState = SourceSentCwd;
+ socket()->sendCommand("CWD " + sourceFile.directory());
+ return;
+ }
+ }
+ }
+
+ // ***************************************************************************
+ // ***************************** Source socket *******************************
+ // ***************************************************************************
+ case SourceSentCwd: {
+ if (currentState == SourceSentCwd) {
+ if (!socket()->isResponse("250")) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ if (socket()->isMultiline())
+ return;
+ else
+ socket()->setCurrentDirectory(sourceFile.directory());
+ }
+
+ // We are the source socket, let's stat
+ currentState = SourceSentStat;
+ socket()->protoStat(sourceFile);
+ break;
+ }
+ case SourceSentStat: {
+ if (socket()->getStatResponse().filename().isEmpty()) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ } else {
+ // File exists, invoke the companion
+ companion->setConfig("params.fxp.companion", 1);
+ companion->thread()->siteToSite(socket()->thread(), sourceFile, destinationFile);
+ currentState = SourceDestVerified;
+ }
+ break;
+ }
+ case SourceDestVerified: {
+ if (isWakeup()) {
+ // We have been waken up because a decision has been made
+ FileExistsWakeupEvent *event = static_cast<FileExistsWakeupEvent*>(m_wakeupEvent);
+
+ if (!socket()->getConfigInt("feat.rest") && event->action == FileExistsWakeupEvent::Resume)
+ event->action = FileExistsWakeupEvent::Overwrite;
+
+ switch (event->action) {
+ case FileExistsWakeupEvent::Rename: {
+ // Change the destination filename, otherwise it is the same as overwrite
+ destinationFile.setPath(event->newFileName);
+ }
+ case FileExistsWakeupEvent::Overwrite: {
+ companion->setConfig("params.fxp.rest", 0);
+ resumeOffset = 0;
+ break;
+ }
+ case FileExistsWakeupEvent::Resume: {
+ companion->setConfig("params.fxp.rest", companion->getStatResponse().size());
+ resumeOffset = companion->getStatResponse().size();
+ break;
+ }
+ case FileExistsWakeupEvent::Skip: {
+ // Transfer should be aborted
+ companion->setConfig("params.fxp.keep_cache", 1);
+ socket()->setConfig("params.fxp.keep_cache", 1);
+
+ socket()->resetCommandClass(UserAbort);
+ socket()->emitEvent(Event::EventTransferComplete);
+ return;
+ }
+ }
+ } else {
+ companion->setConfig("params.fxp.rest", 0);
+ resumeOffset = 0;
+ }
+
+ // Change type
+ currentState = SourceSentType;
+
+ QString type = "TYPE ";
+ type.append(KFTPCore::Config::self()->ftpMode(sourceFile.path()));
+ socket()->sendCommand(type);
+ break;
+ }
+ case SourceSentType: {
+ if (socket()->getConfigInt("ssl") && socket()->getConfigInt("ssl.prot_mode") != 2 && !socket()->getConfigInt("sscn.activated")) {
+ if (socket()->getConfigInt("ssl.prot_mode") == 0) {
+ if (socket()->getConfigInt("feat.sscn")) {
+ // We support SSCN
+ currentState = SourceSentSscn;
+ socket()->sendCommand("SSCN ON");
+ companion->setConfig("params.ssl.mode", ProtPrivate);
+ } else if (companion->getConfigInt("feat.sscn")) {
+ // Companion supports SSCN
+ currentState = SourceWaitType;
+ companion->setConfig("params.ssl.mode", ProtSSCN);
+ companion->nextCommandAsync();
+ } else if (socket()->getConfigInt("feat.cpsv")) {
+ // We support CPSV
+ currentState = SourceWaitType;
+ socket()->setConfig("params.transfer.mode", TransferCPSV);
+ companion->setConfig("params.ssl.mode", ProtPrivate);
+ companion->nextCommandAsync();
+ } else {
+ // Neither support SSCN, can't do SSL transfer
+ socket()->emitEvent(Event::EventMessage, i18n("Neither server supports SSCN/CPSV but SSL data connection requested, aborting transfer!"));
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+ } else {
+ currentState = SourceSentProt;
+ socket()->sendCommand("PROT C");
+ companion->setConfig("params.ssl.mode", ProtClear);
+ }
+ } else {
+ currentState = SourceWaitType;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case SourceSentSscn: {
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("sscn.activated", 1);
+ socket()->setConfig("params.fxp.changed_prot", 0);
+
+ currentState = SourceWaitType;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case SourceSentProt: {
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("params.fxp.changed_prot", 1);
+
+ currentState = SourceWaitType;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case SourceWaitType: {
+ // We are ready to invoke file transfer, do PASV
+ if (socket()->getConfigInt("feat.pret")) {
+ currentState = SourceSentPret;
+ socket()->sendCommand("PRET RETR " + sourceFile.filename());
+ } else {
+ currentState = SourceSentPasv;
+
+ switch (socket()->getConfigInt("params.transfer.mode")) {
+ case TransferPASV: socket()->sendCommand("PASV"); break;
+ case TransferCPSV: socket()->sendCommand("CPSV"); break;
+ }
+ }
+ break;
+ }
+ case SourceSentPret: {
+ if (!socket()->isResponse("2")) {
+ if (socket()->isResponse("550")) {
+ socket()->emitError(PermissionDenied);
+ socket()->resetCommandClass(Failed);
+ return;
+ } else if (socket()->isResponse("530")) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ }
+
+ socket()->setConfig("feat.pret", 0);
+ }
+
+ currentState = SourceSentPasv;
+
+ switch (socket()->getConfigInt("params.transfer.mode")) {
+ case TransferPASV: socket()->sendCommand("PASV"); break;
+ case TransferCPSV: socket()->sendCommand("CPSV"); break;
+ }
+ break;
+ }
+ case SourceSentPasv: {
+ // Parse the PASV response and get it to the companion to issue PORT
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ QString tmp = socket()->getResponse();
+ int pos = tmp.find('(') + 1;
+ tmp = tmp.mid(pos, tmp.find(')') - pos);
+
+ currentState = SourceDoRest;
+ companion->setConfig("params.fxp.ip", tmp);
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case SourceDoRest: {
+ currentState = SourceSentRest;
+ socket()->sendCommand("REST " + QString::number(resumeOffset));
+ break;
+ }
+ case SourceSentRest: {
+ if (!socket()->isResponse("2") && !socket()->isResponse("3")) {
+ socket()->setConfig("feat.rest", 0);
+ companion->setConfig("params.fxp.rest", 0);
+ } else {
+ // Signal resume
+ socket()->emitEvent(Event::EventResumeOffset, resumeOffset);
+ }
+
+ currentState = SourceDoRetr;
+ companion->nextCommandAsync();
+ break;
+ }
+ case SourceDoRetr: {
+ currentState = SourceSentRetr;
+ socket()->sendCommand("RETR " + sourceFile.filename());
+ break;
+ }
+ case SourceSentRetr: {
+ if (!socket()->isResponse("1")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ currentState = SourceWaitTransfer;
+ }
+ break;
+ }
+ case SourceWaitTransfer: {
+ if (!socket()->isMultiline()) {
+ // Transfer has been completed
+ if (socket()->getConfigInt("params.fxp.changed_prot")) {
+ currentState = SourceResetProt;
+
+ QString prot = "PROT ";
+
+ if (socket()->getConfigInt("ssl.prot_mode") == 0)
+ prot.append('P');
+ else
+ prot.append('C');
+
+ socket()->sendCommand(prot);
+ } else {
+ markClean();
+
+ socket()->emitEvent(Event::EventMessage, i18n("Transfer completed."));
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ }
+ }
+ break;
+ }
+ case SourceResetProt: {
+ markClean();
+
+ socket()->emitEvent(Event::EventMessage, i18n("Transfer completed."));
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ break;
+ }
+
+ // ***************************************************************************
+ // *************************** Destination socket ****************************
+ // ***************************************************************************
+ case DestSentStat: {
+ if (socket()->getStatResponse().filename().isEmpty()) {
+ // Change the working directory
+ currentState = DestWaitCwd;
+ socket()->changeWorkingDirectory(destinationFile.directory(), true);
+ } else {
+ // The file already exists, request action
+ DirectoryListing list;
+ list.addEntry(companion->getStatResponse());
+ list.addEntry(socket()->getStatResponse());
+
+ currentState = DestDoType;
+ socket()->emitEvent(Event::EventFileExists, list);
+ }
+ break;
+ }
+ case DestWaitCwd: {
+ // Directory has been changed/created, call back the companion
+ currentState = DestDoType;
+ companion->nextCommandAsync();
+ break;
+ }
+ case DestDoType: {
+ currentState = DestSentType;
+
+ QString type = "TYPE ";
+ type.append(KFTPCore::Config::self()->ftpMode(sourceFile.path()));
+ socket()->sendCommand(type);
+ break;
+ }
+ case DestSentType: {
+ if (socket()->getConfigInt("ssl")) {
+ // Check what the source socket has instructed us to do
+ switch (socket()->getConfigInt("params.ssl.mode")) {
+ case ProtClear: {
+ // We should use cleartext data channel
+ if (socket()->getConfigInt("ssl.prot_mode") != 2) {
+ currentState = DestSentProt;
+ socket()->sendCommand("PROT C");
+ } else {
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case ProtPrivate: {
+ // We should use private data channel
+ if (socket()->getConfigInt("ssl.prot_mode") != 0) {
+ currentState = DestSentProt;
+ socket()->sendCommand("PROT P");
+ } else {
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case ProtSSCN: {
+ // We should initialize SSCN mode
+ if (!socket()->getConfigInt("sscn.activated")) {
+ currentState = DestSentSscn;
+ socket()->sendCommand("SSCN ON");
+ } else {
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ }
+ } else {
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case DestSentSscn: {
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("sscn.activated", 1);
+ socket()->setConfig("params.fxp.changed_prot", 0);
+
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case DestSentProt: {
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ socket()->setConfig("params.fxp.changed_prot", 1);
+
+ currentState = DestDoPort;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case DestDoPort: {
+ currentState = DestSentPort;
+ socket()->sendCommand("PORT " + socket()->getConfig("params.fxp.ip"));
+ break;
+ }
+ case DestSentPort: {
+ if (!socket()->isResponse("2")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ currentState = DestSentRest;
+ socket()->sendCommand("REST " + socket()->getConfig("params.fxp.rest"));
+ }
+ break;
+ }
+ case DestSentRest: {
+ // We are ready for file transfer
+ currentState = DestDoStor;
+ companion->nextCommandAsync();
+ break;
+ }
+ case DestDoStor: {
+ currentState = DestSentStor;
+ socket()->sendCommand("STOR " + destinationFile.filename());
+ break;
+ }
+ case DestSentStor: {
+ if (!socket()->isResponse("1")) {
+ socket()->resetCommandClass(Failed);
+ } else {
+ currentState = DestWaitTransfer;
+ companion->nextCommandAsync();
+ }
+ break;
+ }
+ case DestWaitTransfer: {
+ if (!socket()->isMultiline()) {
+ // Transfer has been completed
+ if (socket()->getConfigInt("params.fxp.changed_prot")) {
+ currentState = DestResetProt;
+
+ QString prot = "PROT ";
+
+ if (socket()->getConfigInt("ssl.prot_mode") == 0)
+ prot.append('P');
+ else
+ prot.append('C');
+
+ socket()->sendCommand(prot);
+ } else {
+ markClean();
+
+ socket()->emitEvent(Event::EventMessage, i18n("Transfer completed."));
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ }
+ }
+ break;
+ }
+ case DestResetProt: {
+ markClean();
+
+ socket()->emitEvent(Event::EventMessage, i18n("Transfer completed."));
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoSiteToSite(Socket *socket, const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Transfering..."));
+ emitEvent(Event::EventMessage, i18n("Transfering file '%1'...").arg(source.fileName()));
+
+ // Set the source and destination
+ setConfig("params.fxp.abort", 0);
+ setConfig("params.fxp.source", source.path());
+ setConfig("params.fxp.destination", destination.path());
+
+ FtpCommandFxp *fxp = new FtpCommandFxp(this);
+ fxp->companion = static_cast<FtpSocket*>(socket);
+ m_cmdData = fxp;
+ m_cmdData->process();
+}
+
+// *******************************************************************************************
+// ******************************************* NOOP ******************************************
+// *******************************************************************************************
+
+class FtpCommandKeepAlive : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentNoop
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandKeepAlive, FtpSocket, CmdKeepAlive)
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ currentState = SentNoop;
+ socket()->sendCommand("NOOP");
+ break;
+ }
+ case SentNoop: {
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void FtpSocket::protoKeepAlive()
+{
+ emitEvent(Event::EventState, i18n("Transmitting keep-alive..."));
+ setCurrentCommand(Commands::CmdKeepAlive);
+ activateCommandClass(FtpCommandKeepAlive);
+}
+
+}
diff --git a/kftpgrabber/src/engine/ftpsocket.h b/kftpgrabber/src/engine/ftpsocket.h
new file mode 100644
index 0000000..1762bfd
--- /dev/null
+++ b/kftpgrabber/src/engine/ftpsocket.h
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPENGINEFTPSOCKET_H
+#define KFTPENGINEFTPSOCKET_H
+
+#include <kstreamsocket.h>
+#include <kserversocket.h>
+#include <kssl.h>
+
+#include <qguardedptr.h>
+#include <qfile.h>
+
+#include "speedlimiter.h"
+#include "socket.h"
+
+namespace KFTPEngine {
+
+class FtpDirectoryParser;
+class Ssl;
+
+/**
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class FtpSocket : public KNetwork::KStreamSocket, public Socket, public SpeedLimiterItem
+{
+Q_OBJECT
+friend class Commands::Base;
+friend class FtpCommandConnect;
+friend class FtpCommandNegotiateData;
+friend class FtpCommandList;
+public:
+ FtpSocket(Thread *thread);
+ ~FtpSocket();
+
+ void protoConnect(const KURL &url);
+ void protoDisconnect();
+ void protoAbort();
+ void protoGet(const KURL &source, const KURL &destination);
+ void protoPut(const KURL &source, const KURL &destination);
+ void protoRemove(const KURL &path);
+ void protoRename(const KURL &source, const KURL &destination);
+ void protoChmodSingle(const KURL &path, int mode);
+ void protoMkdir(const KURL &path);
+ void protoList(const KURL &path);
+ void protoRaw(const QString &raw);
+ void protoSiteToSite(Socket *socket, const KURL &source, const KURL &destination);
+ void protoKeepAlive();
+
+ void changeWorkingDirectory(const QString &path, bool shouldCreate = false);
+
+ void poll();
+
+ int features() { return SF_FXP_TRANSFER | SF_RAW_COMMAND; }
+
+ bool isConnected() { return m_login; }
+ bool isEncrypted() { return isConnected() && getConfigInt("ssl"); }
+
+ void setSslClientCertificate(KSSLPKCS12 *certificate) { m_clientCert = certificate; }
+
+ bool isResponse(const QString &code);
+ QString getResponse() { return m_response; }
+ bool isMultiline() { return !m_multiLineCode.isEmpty(); }
+
+ void sendCommand(const QString &command);
+ void resetCommandClass(ResetCode code = Ok);
+
+ void setupPassiveTransferSocket(const QString &host, int port);
+ KNetwork::KSocketAddress setupActiveTransferSocket();
+
+ QFile *getTransferFile() { return &m_transferFile; }
+
+ void checkTransferEnd();
+ void checkTransferStart();
+ void resetTransferStart() { m_transferStart = 0; }
+protected:
+ void processBuffer();
+ void parseLine(const QString &line);
+ void variableBufferUpdate(Q_LONG size);
+ void closeDataTransferSocket();
+ void initializeTransferSocket();
+ void transferCompleted();
+private:
+ bool m_login;
+
+ QString m_buffer;
+ QString m_multiLineCode;
+ QString m_response;
+
+ QGuardedPtr<KNetwork::KStreamSocket> m_transferSocket;
+ QGuardedPtr<KNetwork::KServerSocket> m_serverSocket;
+ FtpDirectoryParser *m_directoryParser;
+
+ char m_controlBuffer[1024];
+
+ QFile m_transferFile;
+ char *m_transferBuffer;
+ int m_transferBufferSize;
+ int m_transferStart;
+ int m_transferEnd;
+
+ bool m_transferConnecting;
+ bool m_controlConnecting;
+
+ Ssl *m_controlSsl;
+ Ssl *m_dataSsl;
+ KSSLPKCS12 *m_clientCert;
+protected slots:
+ void slotConnected();
+ void slotControlTryRead();
+ void slotError();
+
+ void slotDataAccept(KNetwork::KStreamSocket *socket);
+ void slotDataConnected();
+ void slotDataTryRead();
+ void slotDataTryWrite();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/sftpsocket.cpp b/kftpgrabber/src/engine/sftpsocket.cpp
new file mode 100644
index 0000000..a683723
--- /dev/null
+++ b/kftpgrabber/src/engine/sftpsocket.cpp
@@ -0,0 +1,775 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "sftpsocket.h"
+#include "cache.h"
+#include "misc/config.h"
+
+#include <qdir.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kio/job.h>
+#include <kio/renamedlg.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace KFTPEngine {
+
+SftpSocket::SftpSocket(Thread *thread)
+ : Socket(thread, "sftp"),
+ m_login(false)
+{
+}
+
+SftpSocket::~SftpSocket()
+{
+}
+
+int addPermInt(int &x, int n, int add)
+{
+ if (x >= n) {
+ x -= n;
+ return add;
+ } else {
+ return 0;
+ }
+}
+
+int SftpSocket::intToPosix(int permissions)
+{
+ int posix = 0;
+ QString str = QString::number(permissions);
+
+ int user = str.mid(0, 1).toInt();
+ int group = str.mid(1, 1).toInt();
+ int other = str.mid(2, 1).toInt();
+
+ posix |= addPermInt(user, 4, S_IRUSR);
+ posix |= addPermInt(user, 2, S_IWUSR);
+ posix |= addPermInt(user, 1, S_IXUSR);
+
+ posix |= addPermInt(group, 4, S_IRGRP);
+ posix |= addPermInt(group, 2, S_IWGRP);
+ posix |= addPermInt(group, 1, S_IXGRP);
+
+ posix |= addPermInt(other, 4, S_IROTH);
+ posix |= addPermInt(other, 2, S_IWOTH);
+ posix |= addPermInt(other, 1, S_IXOTH);
+
+ return posix;
+}
+
+
+// *******************************************************************************************
+// ***************************************** CONNECT *****************************************
+// *******************************************************************************************
+
+class SftpCommandConnect : public Commands::Base {
+public:
+ enum State {
+ None,
+ ConnectComplete,
+ LoginComplete
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(SftpCommandConnect, SftpSocket, CmdConnect)
+
+ void process()
+ {
+ KURL url = socket()->getCurrentUrl();
+
+ switch (currentState) {
+ case None: {
+ // Set connection info
+ SSH_OPTIONS *sshOptions = options_new();
+ options_set_username(sshOptions, (char*) url.user().ascii());
+ options_set_host(sshOptions, url.host().ascii());
+ options_set_port(sshOptions, url.port());
+ options_set_timeout(sshOptions, 10, 0);
+
+ socket()->m_sftpSession = 0;
+ socket()->m_sshSession = ssh_connect(sshOptions);
+
+ if (!socket()->sshSession()) {
+ socket()->emitEvent(Event::EventMessage, i18n("Unable to establish SSH connection (%1)").arg(ssh_get_error(0)));
+ socket()->emitError(ConnectFailed);
+ return;
+ }
+
+ socket()->emitEvent(Event::EventState, i18n("Logging in..."));
+ socket()->emitEvent(Event::EventMessage, i18n("Connected with server, attempting to login..."));
+
+ currentState = ConnectComplete;
+ }
+ case ConnectComplete: {
+ SSH_SESSION *sshSession = socket()->sshSession();
+ QString password;
+
+ // Check if a public key password was supplied using the wakeup event
+ if (isWakeup()) {
+ PubkeyWakeupEvent *event = static_cast<PubkeyWakeupEvent*>(m_wakeupEvent);
+ password = event->password;
+ }
+
+ // Try the public key auth with the set password (if any)
+ int pkey_ret = ssh_userauth_autopubkey(sshSession, (char*) password.ascii());
+ if (pkey_ret == -666) {
+ // Make a password request
+ socket()->emitEvent(Event::EventPubkeyPassword);
+ return;
+ } else if (pkey_ret != SSH_AUTH_SUCCESS) {
+ // First let's try the keyboard-interactive authentification
+ if (keyboardInteractiveLogin() != SSH_AUTH_SUCCESS) {
+ // If this fails, let's try the password authentification
+ if (ssh_userauth_password(sshSession, NULL, (char*) url.pass().ascii()) != SSH_AUTH_SUCCESS) {
+ socket()->emitEvent(Event::EventMessage, i18n("Login has failed."));
+ socket()->emitError(LoginFailed);
+
+ socket()->protoAbort();
+ return;
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Keyboard-interactive authentication succeeded."));
+ }
+ } else {
+ socket()->emitEvent(Event::EventMessage, i18n("Public key authentication succeeded."));
+ }
+
+ currentState = LoginComplete;
+ }
+ case LoginComplete: {
+ socket()->m_sftpSession = sftp_new(socket()->sshSession());
+
+ if (!socket()->sftpSession()) {
+ socket()->emitEvent(Event::EventMessage, i18n("Unable to initialize SFTP channel."));
+ socket()->emitError(LoginFailed);
+
+ socket()->protoAbort();
+ return;
+ }
+
+ if (sftp_init(socket()->sftpSession())) {
+ socket()->emitEvent(Event::EventMessage, i18n("Unable to initialize SFTP."));
+ socket()->emitError(LoginFailed);
+
+ socket()->protoAbort();
+ return;
+ }
+
+ // Get the current directory
+ char *cwd = sftp_canonicalize_path(socket()->sftpSession(), "./");
+ socket()->setDefaultDirectory(socket()->remoteEncoding()->decode(cwd));
+ socket()->setCurrentDirectory(socket()->remoteEncoding()->decode(cwd));
+ delete cwd;
+
+ socket()->emitEvent(Event::EventMessage, i18n("Connected."));
+ socket()->emitEvent(Event::EventConnect);
+ socket()->m_login = true;
+
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+
+ int keyboardInteractiveLogin()
+ {
+ int err = ssh_userauth_kbdint(socket()->sshSession(), NULL, NULL);
+ char *name, *instruction, *prompt;
+ int i, n;
+ char echo;
+
+ while (err == SSH_AUTH_INFO) {
+ name = ssh_userauth_kbdint_getname(socket()->sshSession());
+ instruction = ssh_userauth_kbdint_getinstruction(socket()->sshSession());
+ n = ssh_userauth_kbdint_getnprompts(socket()->sshSession());
+
+ // FIXME Name and instruction are currently ignored. The libssh API reference
+ // suggests displaying an interactive dialog box for the user to supply the
+ // information requested from the server.
+
+ for(i = 0; i < n; ++i) {
+ prompt = ssh_userauth_kbdint_getprompt(socket()->sshSession(), i, &echo);
+
+ if (!echo) {
+ // We should send the password (since only the password should be masked)
+ ssh_userauth_kbdint_setanswer(socket()->sshSession(), i, (char*) socket()->getCurrentUrl().pass().ascii());
+ } else {
+ // FIXME Server requests something else ?
+ }
+ }
+
+ err = ssh_userauth_kbdint(socket()->sshSession(), NULL, NULL);
+ }
+
+ return err;
+ }
+};
+
+void SftpSocket::protoConnect(const KURL &url)
+{
+ emitEvent(Event::EventState, i18n("Connecting..."));
+ emitEvent(Event::EventMessage, i18n("Connecting to %1:%2...").arg(url.host()).arg(url.port()));
+
+ if (!getConfig("encoding").isEmpty())
+ changeEncoding(getConfig("encoding"));
+
+ // Connect to the remote host
+ setCurrentUrl(url);
+ activateCommandClass(SftpCommandConnect);
+}
+
+// *******************************************************************************************
+// **************************************** DISCONNECT ***************************************
+// *******************************************************************************************
+
+void SftpSocket::protoDisconnect()
+{
+ Socket::protoDisconnect();
+
+ if (m_sftpSession)
+ sftp_free(m_sftpSession);
+
+ ssh_disconnect(m_sshSession);
+ m_sshSession = 0;
+
+ m_login = false;
+}
+
+void SftpSocket::protoAbort()
+{
+ Socket::protoAbort();
+
+ if (getCurrentCommand() == Commands::CmdGet || getCurrentCommand() == Commands::CmdPut) {
+ // Abort current command
+ resetCommandClass(UserAbort);
+ emitEvent(Event::EventMessage, i18n("Aborted."));
+ }
+}
+
+// *******************************************************************************************
+// ******************************************* LIST ******************************************
+// *******************************************************************************************
+
+class SftpCommandList : public Commands::Base {
+public:
+ enum State {
+ None
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(SftpCommandList, SftpSocket, CmdList)
+
+ void process()
+ {
+ // Check the directory listing cache
+ DirectoryListing cached = Cache::self()->findCached(socket(), socket()->getCurrentDirectory());
+ if (cached.isValid()) {
+ socket()->emitEvent(Event::EventMessage, i18n("Using cached directory listing."));
+
+ if (socket()->isChained()) {
+ // We don't emit an event, because this list has been called from another
+ // command. Just save the listing.
+ socket()->m_lastDirectoryListing = cached;
+ } else
+ socket()->emitEvent(Event::EventDirectoryListing, cached);
+
+ socket()->resetCommandClass();
+ return;
+ }
+
+ socket()->m_lastDirectoryListing = DirectoryListing(socket()->getCurrentDirectory());
+
+ SFTP_DIR *m_dir = sftp_opendir(socket()->sftpSession(), socket()->remoteEncoding()->encode(socket()->getCurrentDirectory()).data());
+ if (!m_dir) {
+ if (socket()->errorReporting()) {
+ socket()->emitError(ListFailed);
+ socket()->resetCommandClass(Failed);
+ } else
+ socket()->resetCommandClass();
+ return;
+ }
+
+ // Read the specified directory
+ SFTP_ATTRIBUTES *file;
+ DirectoryEntry entry;
+
+ while ((file = sftp_readdir(socket()->sftpSession(), m_dir))) {
+ entry.setFilename(file->name);
+
+ if (entry.filename() != "." && entry.filename() != "..") {
+ entry.setFilename(socket()->remoteEncoding()->decode(entry.filename().ascii()));
+ entry.setOwner(file->owner);
+ entry.setGroup(file->group);
+ entry.setTime(file->mtime);
+ entry.setSize(file->size);
+ entry.setPermissions(file->permissions);
+
+ if (file->permissions & S_IFDIR)
+ entry.setType('d');
+ else
+ entry.setType('f');
+
+ socket()->m_lastDirectoryListing.addEntry(entry);
+ }
+
+ sftp_attributes_free(file);
+ }
+
+ sftp_dir_close(m_dir);
+
+ // Cache the directory listing
+ Cache::self()->addDirectory(socket(), socket()->m_lastDirectoryListing);
+
+ if (!socket()->isChained())
+ socket()->emitEvent(Event::EventDirectoryListing, socket()->m_lastDirectoryListing);
+ socket()->resetCommandClass();
+ }
+};
+
+void SftpSocket::protoList(const KURL &path)
+{
+ emitEvent(Event::EventState, i18n("Fetching directory listing..."));
+ emitEvent(Event::EventMessage, i18n("Fetching directory listing..."));
+
+ // Set the directory that should be listed
+ setCurrentDirectory(path.path());
+
+ activateCommandClass(SftpCommandList);
+}
+
+// *******************************************************************************************
+// ******************************************* GET *******************************************
+// *******************************************************************************************
+
+class SftpCommandGet : public Commands::Base {
+public:
+ enum State {
+ None,
+ WaitStat,
+ DestChecked
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(SftpCommandGet, SftpSocket, CmdGet)
+
+ KURL sourceFile;
+ KURL destinationFile;
+ filesize_t resumeOffset;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ // Stat source file
+ resumeOffset = 0;
+ sourceFile.setPath(socket()->getConfig("params.get.source"));
+ destinationFile.setPath(socket()->getConfig("params.get.destination"));
+
+ currentState = WaitStat;
+ socket()->protoStat(sourceFile);
+ break;
+ }
+ case WaitStat: {
+ socket()->emitEvent(Event::EventState, i18n("Transfering..."));
+
+ if (socket()->getStatResponse().filename().isEmpty()) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ if (QDir::root().exists(destinationFile.path())) {
+ DirectoryListing list;
+ list.addEntry(socket()->getStatResponse());
+
+ currentState = DestChecked;
+ socket()->emitEvent(Event::EventFileExists, list);
+ return;
+ } else
+ KStandardDirs::makeDir(destinationFile.directory());
+ }
+ case DestChecked: {
+ QFile file;
+
+ if (isWakeup()) {
+ // We have been waken up because a decision has been made
+ FileExistsWakeupEvent *event = static_cast<FileExistsWakeupEvent*>(m_wakeupEvent);
+
+ switch (event->action) {
+ case FileExistsWakeupEvent::Rename: {
+ // Change the destination filename, otherwise it is the same as overwrite
+ destinationFile.setPath(event->newFileName);
+ }
+ case FileExistsWakeupEvent::Overwrite: {
+ file.setName(destinationFile.path());
+ file.open(IO_WriteOnly | IO_Truncate);
+ break;
+ }
+ case FileExistsWakeupEvent::Resume: {
+ file.setName(destinationFile.path());
+ file.open(IO_WriteOnly | IO_Append);
+
+ // Signal resume
+ resumeOffset = file.size();
+ socket()->emitEvent(Event::EventResumeOffset, resumeOffset);
+ break;
+ }
+ case FileExistsWakeupEvent::Skip: {
+ // Transfer should be aborted
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ return;
+ }
+ }
+ } else {
+ // The file doesn't exist so we are free to overwrite
+ file.setName(destinationFile.path());
+ file.open(IO_WriteOnly | IO_Truncate);
+ }
+
+ // Download the file
+ SFTP_FILE *rfile = sftp_open(socket()->sftpSession(), socket()->remoteEncoding()->encode(sourceFile.path()).data(), O_RDONLY, 0);
+ if (!rfile) {
+ file.close();
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ if (resumeOffset > 0)
+ sftp_seek(rfile, resumeOffset);
+
+ char buffer[16384];
+ int size;
+
+ do {
+ size = sftp_read(rfile, buffer, sizeof(buffer));
+
+ if (size > 0) {
+ file.writeBlock(buffer, size);
+ socket()->m_transferBytes += size;
+ }
+
+ if (socket()->shouldAbort())
+ break;
+ } while (size);
+
+ sftp_file_close(rfile);
+ file.close();
+
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void SftpSocket::protoGet(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Transfering..."));
+ emitEvent(Event::EventMessage, i18n("Downloading file '%1'...").arg(source.fileName()));
+
+ // Set the source and destination
+ setConfig("params.get.source", source.path());
+ setConfig("params.get.destination", destination.path());
+
+ m_transferBytes = 0;
+
+ m_speedLastTime = time(0);
+ m_speedLastBytes = 0;
+
+ activateCommandClass(SftpCommandGet);
+}
+
+// *******************************************************************************************
+// ******************************************* PUT *******************************************
+// *******************************************************************************************
+
+class SftpCommandPut : public Commands::Base {
+public:
+ enum State {
+ None,
+ WaitStat,
+ DestChecked
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(SftpCommandPut, SftpSocket, CmdPut)
+
+ KURL sourceFile;
+ KURL destinationFile;
+ filesize_t resumeOffset;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ // Stat source file
+ resumeOffset = 0;
+ sourceFile.setPath(socket()->getConfig("params.get.source"));
+ destinationFile.setPath(socket()->getConfig("params.get.destination"));
+
+ if (!QDir::root().exists(sourceFile.path())) {
+ socket()->emitError(FileNotFound);
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ currentState = WaitStat;
+ socket()->protoStat(destinationFile);
+ break;
+ }
+ case WaitStat: {
+ socket()->emitEvent(Event::EventState, i18n("Transfering..."));
+
+ if (!socket()->getStatResponse().filename().isEmpty()) {
+ DirectoryListing list;
+ list.addEntry(socket()->getStatResponse());
+
+ currentState = DestChecked;
+ socket()->emitEvent(Event::EventFileExists, list);
+ return;
+ } else {
+ // Create destination directories
+ socket()->setErrorReporting(false);
+
+ QString destinationDir = destinationFile.directory();
+ QString fullPath;
+
+ for (register int i = 1; i <= destinationDir.contains('/'); i++) {
+ fullPath += "/" + destinationDir.section('/', i, i);
+
+ // Create the directory
+ socket()->protoMkdir(fullPath);
+ }
+ }
+ }
+ case DestChecked: {
+ QFile file;
+
+ if (isWakeup()) {
+ // We have been waken up because a decision has been made
+ FileExistsWakeupEvent *event = static_cast<FileExistsWakeupEvent*>(m_wakeupEvent);
+
+ switch (event->action) {
+ case FileExistsWakeupEvent::Rename: {
+ // Change the destination filename, otherwise it is the same as overwrite
+ destinationFile.setPath(event->newFileName);
+ }
+ case FileExistsWakeupEvent::Overwrite: {
+ file.setName(sourceFile.path());
+ file.open(IO_ReadOnly);
+ break;
+ }
+ case FileExistsWakeupEvent::Resume: {
+ resumeOffset = socket()->getStatResponse().size();
+
+ file.setName(sourceFile.path());
+ file.open(IO_ReadOnly);
+ file.at(resumeOffset);
+
+ // Signal resume
+ socket()->emitEvent(Event::EventResumeOffset, resumeOffset);
+ break;
+ }
+ case FileExistsWakeupEvent::Skip: {
+ // Transfer should be aborted
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ return;
+ }
+ }
+ } else {
+ // The file doesn't exist so we are free to overwrite
+ file.setName(sourceFile.path());
+ file.open(IO_ReadOnly);
+ }
+
+ // Download the file
+ SFTP_FILE *rfile;
+
+ if (resumeOffset > 0) {
+ rfile = sftp_open(socket()->sftpSession(), socket()->remoteEncoding()->encode(destinationFile.path()).data(), O_WRONLY | O_APPEND, 0);
+ sftp_seek(rfile, resumeOffset);
+ } else
+ rfile = sftp_open(socket()->sftpSession(), socket()->remoteEncoding()->encode(destinationFile.path()).data(), O_WRONLY | O_CREAT, 0);
+
+ if (!rfile) {
+ file.close();
+ socket()->resetCommandClass(Failed);
+ return;
+ }
+
+ char buffer[16384];
+ int size;
+
+ do {
+ size = file.readBlock(buffer, sizeof(buffer));
+
+ if (size > 0) {
+ sftp_write(rfile, buffer, size);
+ socket()->m_transferBytes += size;
+ }
+
+ if (socket()->shouldAbort())
+ break;
+ } while (size);
+
+ sftp_file_close(rfile);
+ file.close();
+
+ socket()->emitEvent(Event::EventTransferComplete);
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void SftpSocket::protoPut(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Transfering..."));
+ emitEvent(Event::EventMessage, i18n("Uploading file '%1'...").arg(source.fileName()));
+
+ // Set the source and destination
+ setConfig("params.get.source", source.path());
+ setConfig("params.get.destination", destination.path());
+
+ m_transferBytes = 0;
+
+ m_speedLastTime = time(0);
+ m_speedLastBytes = 0;
+
+ activateCommandClass(SftpCommandPut);
+}
+
+// *******************************************************************************************
+// **************************************** REMOVE *******************************************
+// *******************************************************************************************
+
+void SftpSocket::protoRemove(const KURL &path)
+{
+ emitEvent(Event::EventState, i18n("Removing..."));
+
+ // Remove a file or directory
+ int result = 0;
+
+ if (getConfigInt("params.remove.directory"))
+ result = sftp_rmdir(m_sftpSession, remoteEncoding()->encode(path.path()).data());
+ else
+ result = sftp_rm(m_sftpSession, remoteEncoding()->encode(path.path()).data());
+
+ if (result < 0) {
+ resetCommandClass(Failed);
+ } else {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(this, path.directory());
+
+ emitEvent(Event::EventReloadNeeded);
+ resetCommandClass();
+ }
+}
+
+// *******************************************************************************************
+// **************************************** RENAME *******************************************
+// *******************************************************************************************
+
+void SftpSocket::protoRename(const KURL &source, const KURL &destination)
+{
+ emitEvent(Event::EventState, i18n("Renaming..."));
+
+ if (sftp_rename(m_sftpSession, remoteEncoding()->encode(source.path()).data(), remoteEncoding()->encode(destination.path()).data()) < 0) {
+ resetCommandClass(Failed);
+ } else {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(this, source.directory());
+ Cache::self()->invalidateEntry(this, destination.directory());
+
+ emitEvent(Event::EventReloadNeeded);
+ resetCommandClass();
+ }
+}
+
+// *******************************************************************************************
+// **************************************** CHMOD ********************************************
+// *******************************************************************************************
+
+void SftpSocket::protoChmodSingle(const KURL &path, int mode)
+{
+ emitEvent(Event::EventState, i18n("Changing mode..."));
+
+ SFTP_ATTRIBUTES *attrs = static_cast<SFTP_ATTRIBUTES*>(new SFTP_ATTRIBUTES);
+ memset(attrs, 0, sizeof(*attrs));
+
+ attrs->permissions = intToPosix(mode);
+ attrs->flags = SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ sftp_setstat(m_sftpSession, remoteEncoding()->encode(path.path()).data(), attrs);
+ sftp_attributes_free(attrs);
+
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(this, path.directory());
+
+ emitEvent(Event::EventReloadNeeded);
+ resetCommandClass();
+}
+
+// *******************************************************************************************
+// **************************************** MKDIR ********************************************
+// *******************************************************************************************
+
+void SftpSocket::protoMkdir(const KURL &path)
+{
+ SFTP_ATTRIBUTES *attrs = static_cast<SFTP_ATTRIBUTES*>(new SFTP_ATTRIBUTES);
+ memset(attrs, 0, sizeof(*attrs));
+
+ if (sftp_mkdir(m_sftpSession, remoteEncoding()->encode(path.path()).data(), attrs) < 0) {
+ if (errorReporting())
+ resetCommandClass(Failed);
+ } else {
+ // Invalidate cached parent entry (if any)
+ Cache::self()->invalidateEntry(this, path.directory());
+
+ if (errorReporting()) {
+ emitEvent(Event::EventReloadNeeded);
+ resetCommandClass();
+ }
+ }
+
+ delete attrs;
+}
+
+}
diff --git a/kftpgrabber/src/engine/sftpsocket.h b/kftpgrabber/src/engine/sftpsocket.h
new file mode 100644
index 0000000..f34a896
--- /dev/null
+++ b/kftpgrabber/src/engine/sftpsocket.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPENGINESFTPSOCKET_H
+#define KFTPENGINESFTPSOCKET_H
+
+// LibSSH includes
+#include "misc/libs/ssh/libssh.h"
+#include "misc/libs/ssh/sftp.h"
+
+#include "socket.h"
+
+namespace KFTPEngine {
+
+/**
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class SftpSocket : public Socket {
+friend class SftpCommandConnect;
+friend class SftpCommandList;
+friend class SftpCommandGet;
+friend class SftpCommandPut;
+public:
+ SftpSocket(Thread *thread);
+ ~SftpSocket();
+
+ void protoConnect(const KURL &url);
+ void protoDisconnect();
+ void protoAbort();
+ void protoGet(const KURL &source, const KURL &destination);
+ void protoPut(const KURL &source, const KURL &destination);
+ void protoRemove(const KURL &path);
+ void protoRename(const KURL &source, const KURL &destination);
+ void protoChmodSingle(const KURL &path, int mode);
+ void protoMkdir(const KURL &path);
+ void protoList(const KURL &path);
+
+ void poll() {};
+
+ int features() { return 0; }
+
+ bool isConnected() { return m_login; }
+ bool isEncrypted() { return true; }
+
+ SSH_SESSION *sshSession() { return m_sshSession; }
+ SFTP_SESSION *sftpSession() { return m_sftpSession; }
+private:
+ QString posixToString(int permissions);
+ int intToPosix(int permissions);
+private:
+ SSH_SESSION *m_sshSession;
+ SFTP_SESSION *m_sftpSession;
+
+ bool m_login;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/socket.cpp b/kftpgrabber/src/engine/socket.cpp
new file mode 100644
index 0000000..370de1b
--- /dev/null
+++ b/kftpgrabber/src/engine/socket.cpp
@@ -0,0 +1,866 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "socket.h"
+#include "thread.h"
+#include "connectionretry.h"
+#include "speedlimiter.h"
+#include "cache.h"
+
+#include "misc/config.h"
+
+#include <klocale.h>
+
+namespace KFTPEngine {
+
+Socket::Socket(Thread *thread, const QString &protocol)
+ : m_remoteEncoding(new KRemoteEncoding()),
+ m_cmdData(0),
+ m_thread(thread),
+ m_transferBytes(0),
+ m_speedLastTime(0),
+ m_speedLastBytes(0),
+ m_protocol(protocol),
+ m_currentCommand(Commands::CmdNone),
+ m_errorReporting(true),
+ m_shouldAbort(false)
+{
+ m_commandChain.setAutoDelete(true);
+}
+
+Socket::~Socket()
+{
+ delete m_remoteEncoding;
+
+ if (m_connectionRetry)
+ delete m_connectionRetry;
+}
+
+void Socket::initConfig()
+{
+ m_config.clear();
+
+ // Fill in some default values
+ setConfig("feat.epsv", 1);
+ setConfig("feat.eprt", 1);
+ setConfig("feat.pasv", 1);
+ setConfig("feat.size", 1);
+ setConfig("ssl.prot_mode", 2);
+ setConfig("keepalive.enabled", 1);
+ setConfig("keepalive.timeout", 60);
+}
+
+void Socket::emitError(ErrorCode code, const QString &param1)
+{
+ // Intercept connect and login errors and pass them on to the ConnectionRetry class (if enabled)
+ if (getConfigInt("retry") && (code == ConnectFailed || code == LoginFailed)) {
+ if (!m_connectionRetry)
+ m_connectionRetry = new ConnectionRetry(this);
+
+ m_connectionRetry->startRetry();
+ return;
+ }
+
+ QValueList<EventParameter> params;
+ params.append(EventParameter(code));
+ params.append(EventParameter(param1));
+
+ // Dispatch the event via socket thread
+ m_thread->event(Event::EventError, params);
+}
+
+void Socket::emitEvent(Event::Type type, const QString &param1, const QString &param2)
+{
+ QValueList<EventParameter> params;
+ params.append(EventParameter(param1));
+ params.append(EventParameter(param2));
+
+ // Dispatch the event via socket thread
+ m_thread->event(type, params);
+}
+
+void Socket::emitEvent(Event::Type type, DirectoryListing param1)
+{
+ QValueList<EventParameter> params;
+ params.append(EventParameter(param1));
+
+ // Dispatch the event via socket thread
+ m_thread->event(type, params);
+}
+
+void Socket::emitEvent(Event::Type type, filesize_t param1)
+{
+ QValueList<EventParameter> params;
+ params.append(EventParameter(param1));
+
+ // Dispatch the event via socket thread
+ m_thread->event(type, params);
+}
+
+void Socket::emitEvent(Event::Type type, void *param1)
+{
+ QValueList<EventParameter> params;
+ params.append(EventParameter(param1));
+
+ // Dispatch the event via socket thread
+ m_thread->event(type, params);
+}
+
+void Socket::changeEncoding(const QString &encoding)
+{
+ // Alter encoding and change socket config
+ m_remoteEncoding->setEncoding(encoding.ascii());
+ setConfig("encoding", encoding);
+}
+
+void Socket::protoDisconnect()
+{
+ resetCommandClass(UserAbort);
+
+ emitEvent(Event::EventMessage, i18n("Disconnected."));
+ emitEvent(Event::EventDisconnect);
+}
+
+void Socket::timeoutWait(bool start)
+{
+ if (start) {
+ m_timeoutCounter.start();
+ } else {
+ m_timeoutCounter = QTime();
+ }
+}
+
+void Socket::timeoutPing()
+{
+ m_timeoutCounter.restart();
+}
+
+void Socket::timeoutCheck()
+{
+ if (!isConnected())
+ return;
+
+ if (!m_timeoutCounter.isNull()) {
+ Commands::Type command = getCurrentCommand();
+ int timeout = 0;
+
+ // Ignore timeouts for FXP transfers, since there is no way to do pings
+ if (command == Commands::CmdFxp)
+ return;
+
+ if (command == Commands::CmdGet || command == Commands::CmdPut)
+ timeout = KFTPCore::Config::dataTimeout();
+ else
+ timeout = KFTPCore::Config::controlTimeout();
+
+ if (timeout > 0 && m_timeoutCounter.elapsed() > (timeout * 1000)) {
+ timeoutWait(false);
+
+ // We have a timeout, let's abort
+ emitEvent(Event::EventMessage, i18n("Connection timed out."));
+ protoDisconnect();
+ }
+ }
+}
+
+void Socket::keepaliveStart()
+{
+ m_keepaliveCounter.start();
+}
+
+void Socket::keepaliveCheck()
+{
+ // Ignore keepalive if the socket is busy
+ if (isBusy() || !isConnected()) {
+ m_keepaliveCounter.restart();
+ return;
+ }
+
+ if (getConfigInt("keepalive.enabled") && m_keepaliveCounter.elapsed() > getConfigInt("keepalive.timeout") * 1000) {
+ protoKeepAlive();
+
+ // Reset the counter
+ m_keepaliveCounter.restart();
+ }
+}
+
+Commands::Type Socket::getCurrentCommand()
+{
+ if (m_commandChain.count() > 0) {
+ QPtrList<Commands::Base>::iterator chainEnd = m_commandChain.end();
+ for (QPtrList<Commands::Base>::iterator i = m_commandChain.begin(); i != chainEnd; i++) {
+ if ((*i)->command() != Commands::CmdNone)
+ return (*i)->command();
+ }
+ }
+
+ return m_currentCommand;
+}
+
+Commands::Type Socket::getToplevelCommand()
+{
+ return m_currentCommand;
+}
+
+Commands::Type Socket::getPreviousCommand()
+{
+ if (!isChained())
+ return Commands::CmdNone;
+
+ if (m_commandChain.count() > 1) {
+ Commands::Base *previous = m_commandChain.prev();
+ m_commandChain.next();
+
+ return previous->command();
+ } else {
+ return m_currentCommand;
+ }
+}
+
+void Socket::resetCommandClass(ResetCode code)
+{
+ if (m_commandChain.count() > 0) {
+ Commands::Base *current = m_commandChain.current();
+
+ if (current->isProcessing()) {
+ current->autoDestruct(code);
+ return;
+ } else {
+ if (!current->isClean())
+ current->cleanup();
+
+ m_commandChain.remove();
+ }
+
+ if (code == Ok) {
+ nextCommandAsync();
+ } else {
+ // Command has completed with an error code. We should abort the
+ // complete chain.
+ resetCommandClass(code);
+ }
+ } else {
+ if (m_cmdData) {
+ if (m_cmdData->isProcessing()) {
+ m_cmdData->autoDestruct(code);
+ return;
+ } else {
+ if (!m_cmdData->isClean())
+ m_cmdData->cleanup();
+
+ delete m_cmdData;
+ m_cmdData = 0;
+ }
+ }
+
+ if (code == Failed)
+ emitError(OperationFailed);
+
+ // Reset current command and emit a ready event
+ if (getCurrentCommand() != Commands::CmdConnectRetry) {
+ setCurrentCommand(Commands::CmdNone);
+ emitEvent(Event::EventReady);
+ emitEvent(Event::EventState, i18n("Idle."));
+ }
+
+ setErrorReporting(true);
+ m_shouldAbort = false;
+ }
+}
+
+void Socket::nextCommand()
+{
+ if (m_commandChain.count() > 0) {
+ Commands::Base *current = m_commandChain.current();
+
+ current->setProcessing(true);
+ current->process();
+ current->setProcessing(false);
+
+ if (current->isDestructable())
+ resetCommandClass(current->resetCode());
+ } else if (m_cmdData) {
+ m_cmdData->setProcessing(true);
+ m_cmdData->process();
+ m_cmdData->setProcessing(false);
+
+ if (m_cmdData->isDestructable())
+ resetCommandClass(m_cmdData->resetCode());
+ }
+}
+
+void Socket::nextCommandAsync()
+{
+ m_thread->m_commandQueue.append(Commands::CmdNext);
+}
+
+void Socket::wakeup(WakeupEvent *event)
+{
+ if (m_commandChain.count() > 0) {
+ Commands::Base *current = m_commandChain.current();
+
+ if (current->isProcessing()) {
+ qDebug("WARNING: Attempted to wakeup a processing socket!");
+ return;
+ }
+
+ current->setProcessing(true);
+ current->wakeup(event);
+ current->setProcessing(false);
+
+ if (current->isDestructable())
+ resetCommandClass(current->resetCode());
+ } else if (m_cmdData) {
+ if (m_cmdData->isProcessing()) {
+ qDebug("WARNING: Attempted to wakeup a processing socket!");
+ return;
+ }
+
+ m_cmdData->setProcessing(true);
+ m_cmdData->wakeup(event);
+ m_cmdData->setProcessing(false);
+
+ if (m_cmdData->isDestructable())
+ resetCommandClass(m_cmdData->resetCode());
+ }
+}
+
+filesize_t Socket::getTransferSpeed()
+{
+ time_t timeDelta = time(0) - m_speedLastTime;
+
+ if (timeDelta == 0)
+ return 0;
+
+ if (m_speedLastBytes > m_transferBytes)
+ m_speedLastBytes = 0;
+
+ filesize_t speed = (m_transferBytes - m_speedLastBytes)/(time(0) - m_speedLastTime);
+
+ m_speedLastBytes = m_transferBytes;
+ m_speedLastTime = time(0);
+
+ return speed;
+}
+
+void Socket::protoAbort()
+{
+ m_shouldAbort = true;
+
+ if (m_connectionRetry && !m_cmdData)
+ m_connectionRetry->abortRetry();
+}
+
+// *******************************************************************************************
+// ******************************************* STAT ******************************************
+// *******************************************************************************************
+
+class FtpCommandStat : public Commands::Base {
+public:
+ enum State {
+ None,
+ WaitList
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandStat, Socket, CmdNone)
+
+ KURL path;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ // Issue a list of the parent directory
+ currentState = WaitList;
+ socket()->setErrorReporting(false);
+ socket()->protoList(path.directory());
+ break;
+ }
+ case WaitList: {
+ // Now just extract what we need
+ QValueList<DirectoryEntry> list = socket()->getLastDirectoryListing().list();
+ QValueList<DirectoryEntry>::iterator listEnd = list.end();
+ for (QValueList<DirectoryEntry>::iterator i = list.begin(); i != listEnd; i++) {
+ if ((*i).filename() == path.fileName()) {
+ socket()->m_lastStatResponse = *i;
+ socket()->resetCommandClass();
+ return;
+ }
+ }
+
+ // We found no such file
+ socket()->m_lastStatResponse = DirectoryEntry();
+ socket()->resetCommandClass();
+ break;
+ }
+ }
+ }
+};
+
+void Socket::protoStat(const KURL &path)
+{
+ // Lookup the cache first and don't even try to list if cached
+ DirectoryListing cached = Cache::self()->findCached(this, path.directory());
+ if (cached.isValid()) {
+ QValueList<DirectoryEntry> list = cached.list();
+ QValueList<DirectoryEntry>::iterator listEnd = list.end();
+ for (QValueList<DirectoryEntry>::iterator i = list.begin(); i != listEnd; i++) {
+ if ((*i).filename() == path.fileName()) {
+ m_lastStatResponse = *i;
+ nextCommandAsync();
+ return;
+ }
+ }
+
+ // Cached is valid but file can't be found
+ m_lastStatResponse = DirectoryEntry();
+ nextCommandAsync();
+ return;
+ }
+
+ // Not cached, let's do a real listing
+ FtpCommandStat *stat = new FtpCommandStat(this);
+ stat->path = path;
+ addToCommandChain(stat);
+ nextCommand();
+}
+
+// *******************************************************************************************
+// ****************************************** SCAN *******************************************
+// *******************************************************************************************
+
+class FtpCommandScan : public Commands::Base {
+public:
+ enum State {
+ None,
+ SentList,
+ ProcessList,
+ ScannedDir
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandScan, Socket, CmdNone)
+
+ QValueList<DirectoryEntry> currentList;
+ QValueList<DirectoryEntry>::const_iterator currentEntry;
+
+ QString currentDirectory;
+ DirectoryTree *currentTree;
+
+ void cleanup()
+ {
+ // We didn't emit the tree, so we should free it
+ if (!socket()->isChained())
+ delete currentTree;
+ }
+
+ void process()
+ {
+ // NOTE: The missing breaks are mising for a purpuse! Do not dare to add them ;)
+ switch (currentState) {
+ case None: {
+ // We would like to disable error reporting
+ socket()->setErrorReporting(false);
+
+ // Issue a directory listing on the given URL
+ currentState = SentList;
+ socket()->protoList(currentDirectory);
+ break;
+ }
+ case SentList: {
+ currentList = socket()->getLastDirectoryListing().list();
+ qHeapSort(currentList);
+
+ currentEntry = currentList.begin();
+ currentState = ProcessList;
+
+ // Empty listing, we are done
+ if (currentEntry == currentList.end()) {
+ if (socket()->isChained()) {
+ socket()->resetCommandClass();
+ } else {
+ // We are the toplevel scan command
+ markClean();
+
+ socket()->emitEvent(Event::EventScanComplete, currentTree);
+ socket()->emitEvent(Event::EventMessage, i18n("Scan complete."));
+ socket()->resetCommandClass();
+ }
+
+ return;
+ }
+ }
+ case ProcessList: {
+ if ((*currentEntry).isDirectory()) {
+ // A directory entry
+ DirectoryTree *tree = currentTree->addDirectory(*currentEntry);
+ currentState = ScannedDir;
+
+ FtpCommandScan *scan = new FtpCommandScan(socket());
+ scan->currentDirectory = currentDirectory + "/" + (*currentEntry).filename();
+ scan->currentTree = tree;
+ socket()->addToCommandChain(scan);
+ socket()->nextCommandAsync();
+ return;
+ } else {
+ // A file entry
+ currentTree->addFile(*currentEntry);
+ }
+ }
+ case ScannedDir: {
+ currentState = ProcessList;
+
+ if (++currentEntry == currentList.end()) {
+ // We are done
+ if (socket()->isChained()) {
+ socket()->resetCommandClass();
+ } else {
+ // We are the toplevel scan command
+ markClean();
+
+ socket()->emitEvent(Event::EventScanComplete, currentTree);
+ socket()->emitEvent(Event::EventMessage, i18n("Scan complete."));
+ socket()->resetCommandClass();
+ }
+ } else {
+ socket()->nextCommandAsync();
+ }
+ break;
+ }
+ }
+ }
+};
+
+void Socket::protoScan(const KURL &path)
+{
+ emitEvent(Event::EventMessage, i18n("Starting recursive directory scan..."));
+
+ // We have to create a new command class manually, since we need to set the
+ // currentTree parameter
+ FtpCommandScan *scan = new FtpCommandScan(this);
+ scan->currentDirectory = path.path();
+ scan->currentTree = new DirectoryTree(DirectoryEntry());
+ m_cmdData = scan;
+ m_cmdData->process();
+}
+
+// *******************************************************************************************
+// ***************************************** DELETE ******************************************
+// *******************************************************************************************
+
+class FtpCommandDelete : public Commands::Base {
+public:
+ enum State {
+ None,
+ VerifyDir,
+ SimpleRemove,
+ SentList,
+ ProcessList,
+ DeletedDir,
+ DeletedFile
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandDelete, Socket, CmdDelete)
+
+ QValueList<DirectoryEntry> currentList;
+ QValueList<DirectoryEntry>::const_iterator currentEntry;
+
+ KURL destinationPath;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ // We have to determine if the destination is a file or a directory
+ // TODO use cached information
+ if (socket()->isChained()) {
+ // We know that it is a directory
+ currentState = SentList;
+ socket()->protoList(destinationPath);
+ } else {
+ currentState = VerifyDir;
+ socket()->protoStat(destinationPath);
+ }
+ break;
+ }
+ case VerifyDir: {
+ DirectoryEntry entry = socket()->getStatResponse();
+
+ if (entry.filename().isEmpty()) {
+ // The file doesn't exist, abort
+ socket()->resetCommandClass(Failed);
+ } else {
+ if (entry.isDirectory()) {
+ // It is a directory, remove recursively
+ currentState = SentList;
+ socket()->protoList(destinationPath);
+ } else {
+ // A single file, a simple remove
+ currentState = SimpleRemove;
+ socket()->setConfig("params.remove.directory", 0);
+ socket()->protoRemove(destinationPath);
+ }
+ }
+ break;
+ }
+ case SimpleRemove: {
+ if (!socket()->isChained())
+ socket()->emitEvent(Event::EventReloadNeeded);
+ socket()->resetCommandClass();
+ break;
+ }
+ case SentList: {
+ currentList = socket()->getLastDirectoryListing().list();
+ currentEntry = currentList.begin();
+ currentState = ProcessList;
+
+ // Empty listing, we are done
+ if (currentEntry == currentList.end()) {
+ if (socket()->isChained())
+ socket()->resetCommandClass();
+ else {
+ // We are the top level command class, remove the destination dir
+ currentState = SimpleRemove;
+ socket()->setConfig("params.remove.directory", 1);
+ socket()->protoRemove(destinationPath);
+ }
+
+ return;
+ }
+ }
+ case ProcessList: {
+ KURL childPath = destinationPath;
+ childPath.addPath((*currentEntry).filename());
+
+ if ((*currentEntry).isDirectory()) {
+ // A directory, chain another delete command
+ currentState = DeletedDir;
+
+ // Chain manually, since we need to set some parameters
+ FtpCommandDelete *del = new FtpCommandDelete(socket());
+ del->destinationPath = childPath;
+ socket()->addToCommandChain(del);
+ socket()->nextCommand();
+ } else {
+ // A file entry - remove
+ currentState = DeletedFile;
+ socket()->setConfig("params.remove.directory", 0);
+ socket()->protoRemove(childPath);
+ }
+ break;
+ }
+ case DeletedDir: {
+ // We have to remove the empty directory
+ KURL childPath = destinationPath;
+ childPath.addPath((*currentEntry).filename());
+
+ currentState = DeletedFile;
+ socket()->setConfig("params.remove.directory", 1);
+ socket()->protoRemove(childPath);
+ break;
+ }
+ case DeletedFile: {
+ currentState = ProcessList;
+
+ if (++currentEntry == currentList.end()) {
+ if (socket()->isChained())
+ socket()->resetCommandClass();
+ else {
+ // We are the top level command class, remove the destination dir
+ currentState = SimpleRemove;
+ socket()->setConfig("params.remove.directory", 1);
+ socket()->protoRemove(destinationPath);
+ }
+ } else
+ socket()->nextCommand();
+ break;
+ }
+ }
+ }
+};
+
+void Socket::protoDelete(const KURL &path)
+{
+ // We have to create a new command class manually to set some parameter
+ FtpCommandDelete *del = new FtpCommandDelete(this);
+ del->destinationPath = path;
+ m_cmdData = del;
+ m_cmdData->process();
+}
+
+// *******************************************************************************************
+// ***************************************** CHMOD *******************************************
+// *******************************************************************************************
+
+class FtpCommandRecursiveChmod : public Commands::Base {
+public:
+ enum State {
+ None,
+ VerifyDir,
+ SimpleChmod,
+ SentList,
+ ProcessList,
+ ChmodedDir,
+ ChmodedFile
+ };
+
+ ENGINE_STANDARD_COMMAND_CONSTRUCTOR(FtpCommandRecursiveChmod, Socket, CmdChmod)
+
+ QValueList<DirectoryEntry> currentList;
+ QValueList<DirectoryEntry>::const_iterator currentEntry;
+
+ KURL destinationPath;
+ int mode;
+
+ void process()
+ {
+ switch (currentState) {
+ case None: {
+ // We have to determine if the destination is a file or a directory
+ if (socket()->isChained()) {
+ // We know that it is a directory
+ currentState = SentList;
+ socket()->protoList(destinationPath);
+ } else {
+ currentState = VerifyDir;
+ socket()->protoStat(destinationPath);
+ }
+ break;
+ }
+ case VerifyDir: {
+ DirectoryEntry entry = socket()->getStatResponse();
+
+ if (entry.filename().isEmpty()) {
+ // The file doesn't exist, abort
+ socket()->resetCommandClass(Failed);
+ } else {
+ if (entry.isDirectory()) {
+ // It is a directory, chmod recursively
+ currentState = SentList;
+ socket()->protoList(destinationPath);
+ } else {
+ // A single file, a simple chmod
+ currentState = SimpleChmod;
+ socket()->protoChmodSingle(destinationPath, mode);
+ }
+ }
+ break;
+ }
+ case SimpleChmod: {
+ socket()->resetCommandClass();
+ break;
+ }
+ case SentList: {
+ currentList = socket()->getLastDirectoryListing().list();
+ currentEntry = currentList.begin();
+ currentState = ProcessList;
+
+ // Empty listing, we are done
+ if (currentEntry == currentList.end()) {
+ if (socket()->isChained())
+ socket()->resetCommandClass();
+ else {
+ // We are the top level command class, chmod the destination dir
+ currentState = SimpleChmod;
+ socket()->protoChmodSingle(destinationPath, mode);
+ }
+
+ return;
+ }
+ }
+ case ProcessList: {
+ KURL childPath = destinationPath;
+ childPath.addPath((*currentEntry).filename());
+
+ if ((*currentEntry).isDirectory()) {
+ // A directory, chain another recursive chmod command
+ currentState = ChmodedDir;
+
+ // Chain manually, since we need to set some parameters
+ FtpCommandRecursiveChmod *cm = new FtpCommandRecursiveChmod(socket());
+ cm->destinationPath = childPath;
+ cm->mode = mode;
+ socket()->addToCommandChain(cm);
+ socket()->nextCommand();
+ } else {
+ // A file entry - remove
+ currentState = ChmodedFile;
+ socket()->protoChmodSingle(childPath, mode);
+ }
+ break;
+ }
+ case ChmodedDir: {
+ // We have to chmod the directory
+ KURL childPath = destinationPath;
+ childPath.addPath((*currentEntry).filename());
+
+ currentState = ChmodedFile;
+ socket()->protoChmodSingle(childPath, mode);
+ break;
+ }
+ case ChmodedFile: {
+ currentState = ProcessList;
+
+ if (++currentEntry == currentList.end()) {
+ if (socket()->isChained())
+ socket()->resetCommandClass();
+ else {
+ // We are the top level command class, chmod the destination dir
+ currentState = SimpleChmod;
+ socket()->protoChmodSingle(destinationPath, mode);
+ }
+ } else
+ socket()->nextCommand();
+ break;
+ }
+ }
+ }
+};
+
+void Socket::protoChmod(const KURL &path, int mode, bool recursive)
+{
+ if (recursive) {
+ // We have to create a new command class manually to set some parameters
+ FtpCommandRecursiveChmod *cm = new FtpCommandRecursiveChmod(this);
+ cm->destinationPath = path;
+ cm->mode = mode;
+ m_cmdData = cm;
+ m_cmdData->process();
+ } else {
+ // No recursive, just chmod a single file
+ protoChmodSingle(path, mode);
+ }
+}
+
+}
diff --git a/kftpgrabber/src/engine/socket.h b/kftpgrabber/src/engine/socket.h
new file mode 100644
index 0000000..3c2296a
--- /dev/null
+++ b/kftpgrabber/src/engine/socket.h
@@ -0,0 +1,605 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPENGINESOCKET_H
+#define KFTPENGINESOCKET_H
+
+#include <kurl.h>
+#include <kremoteencoding.h>
+
+#include <qptrlist.h>
+#include <qguardedptr.h>
+#include <qdatetime.h>
+
+#include "commands.h"
+
+namespace KFTPEngine {
+
+class ConnectionRetry;
+
+enum SocketFeatures {
+ SF_FXP_TRANSFER = 1,
+ SF_RAW_COMMAND = 2
+};
+
+/**
+ * The socket class provides an abstract class for all implemented protocols. It
+ * provides basic methods and also some remote operations (recursive scan,
+ * recursive removal).
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class Socket
+{
+friend class Thread;
+friend class FtpCommandStat;
+public:
+ /**
+ * Constructs a new socket.
+ *
+ * @param thread The thread that created this socket
+ * @param protocol The protocol name
+ */
+ Socket(Thread *thread, const QString &protocol);
+ ~Socket();
+
+ /**
+ * Set an internal config value.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ void setConfig(const QString &key, const QString &value) { m_config[key] = value; }
+
+ /**
+ * Set an internal config value.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ void setConfig(const QString &key, int value) { m_config[key] = QString::number(value); }
+
+ /**
+ * Set an internal config value.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ void setConfig(const QString &key, filesize_t value) { m_config[key] = QString::number(value); }
+
+ /**
+ * Get an internal config value as string.
+ *
+ * @param key Key
+ * @return The key's value or an empty string if the key doesn't exist
+ */
+ QString getConfig(const QString &key) { return m_config[key]; }
+
+ /**
+ * Get an internal config value as an integer.
+ *
+ * @param key Key
+ * @return The key's value or 0 if the key doesn't exist
+ */
+ int getConfigInt(const QString &key) { return m_config[key].toInt(); }
+
+ /**
+ * Get an internal config value as filesize.
+ *
+ * @param key Key
+ * @return The key's value or 0 if the key doesn't exist
+ */
+ filesize_t getConfigFs(const QString &key) { return m_config[key].toULongLong(); }
+
+ /**
+ * This method should initialize the configuration map.
+ */
+ virtual void initConfig();
+
+ /**
+ * This method should trigger the connection process.
+ *
+ * @param url Remote url to connect to
+ */
+ virtual void protoConnect(const KURL &url) = 0;
+
+ /**
+ * This method should disconnect from the remote host.
+ */
+ virtual void protoDisconnect();
+
+ /**
+ * This method should abort any ongoing action.
+ */
+ virtual void protoAbort();
+
+ /**
+ * This method should download a remote file and save it localy.
+ *
+ * @param source The source url
+ * @param destination The destination url
+ */
+ virtual void protoGet(const KURL &source, const KURL &destination) = 0;
+
+ /**
+ * This method should upload a local file and save it remotely.
+ *
+ * @param source The source url
+ * @param destination The destination url
+ */
+ virtual void protoPut(const KURL &source, const KURL &destination) = 0;
+
+ /**
+ * Each protocol should implement this method. It should remove just one
+ * single entry. A config variable "params.remove.directory" will be set
+ * to 1 if the entry to remove is a directory and to 0 if it should expect
+ * a file.
+ *
+ * @warning You should NOT use this method directly! Use @ref protoDelete
+ * instead!
+ * @param path The path to the entry to remove
+ */
+ virtual void protoRemove(const KURL &path) = 0;
+
+ /**
+ * This method should rename/move a remote file.
+ *
+ * @param source The source file path
+ * @param destination The destination file path
+ */
+ virtual void protoRename(const KURL &source, const KURL &destination) = 0;
+
+ /**
+ * This method should change file's mode.
+ *
+ * * @warning You should NOT use this method directly! Use @ref protoChmod
+ * instead!
+ * @param path The file's path
+ * @param mode The new file mode
+ */
+ virtual void protoChmodSingle(const KURL &path, int mode) = 0;
+
+ /**
+ * This method should create a new remote directory.
+ *
+ * @param path Path of the newly created remote directory
+ */
+ virtual void protoMkdir(const KURL &path) = 0;
+
+ /**
+ * This method should fetch the remote directory listing for a specified
+ * directory. Note that this method could be called as a chained command,
+ * so it MUST NOT emit an EventDirectoryListing event if isChained returns
+ * true! In this case it should save the directory listing to the
+ * m_lastDirectoryListing member variable.
+ *
+ * @param path The path to list
+ */
+ virtual void protoList(const KURL &path) = 0;
+
+ /**
+ * This method should fetch the information about the given path. It is
+ * usualy called as a chained command.
+ *
+ * @param path The path to stat
+ */
+ virtual void protoStat(const KURL &path);
+
+ /**
+ * This method should send a raw command in case the protocol supports it
+ * (the SF_RAW_COMMAND is among features).
+ *
+ * @param command The command to send
+ */
+ virtual void protoRaw(const QString&) {}
+
+ /**
+ * This method should initiate a site to site transfer in case the protocol
+ * supports it (the SF_FXP_TRANSFER is among features).
+ *
+ * @param socket The destination socket
+ * @param source The source url
+ * @param destination The destination url
+ */
+ virtual void protoSiteToSite(Socket*, const KURL&, const KURL&) {}
+
+ /**
+ * Send a packet to keep the connection alive.
+ */
+ virtual void protoKeepAlive() {}
+
+ /**
+ * Recursively scan a directory and emit a DirectoryTree that can be used to
+ * create new transfers for addition to the queue.
+ *
+ * @param path The path to recursively scan
+ */
+ void protoScan(const KURL &path);
+
+ /**
+ * Identify if the remote path is a file or a directory and recursively remove
+ * it if so. The difference between this command and @ref protoRemove is, that
+ * protoRemove removes just one entry, and doesn't identify file type.
+ *
+ * @param path The path to remove
+ */
+ void protoDelete(const KURL &path);
+
+ /**
+ * Change file or directory mode. Also supports recursive mode changes.
+ *
+ * @param path The file's path
+ * @param mode The new file mode
+ * @param recursive Should the mode be recursively changed
+ */
+ void protoChmod(const KURL &path, int mode, bool recursive);
+
+ /**
+ * Returns this socket's parent thread.
+ *
+ * @return Socket's parent thread
+ */
+ Thread *thread() { return m_thread; }
+
+ /**
+ * Returns the protocol name of this socket.
+ *
+ * @return This socket's protocol name
+ */
+ QString protocolName() { return m_protocol; }
+
+ /**
+ * This method should return the socket's features by or-ing the values in
+ * SocketFeatures enum.
+ *
+ * @return Socket's features
+ */
+ virtual int features() = 0;
+
+ /**
+ * This method should return true if this socket is connected.
+ *
+ * @return True if the socket has successfully connected
+ */
+ virtual bool isConnected() = 0;
+
+ /**
+ * This method should return true if the connection is encrypted by some method.
+ *
+ * @return True if the connection is encrypted
+ */
+ virtual bool isEncrypted() = 0;
+
+ /**
+ * Returns true if the socket is currently busy performing an action.
+ *
+ * @return True if the socket is busy
+ */
+ virtual bool isBusy() { return m_currentCommand != Commands::CmdNone; }
+
+ /**
+ * Emit an engine error code.
+ *
+ * @param code The error code
+ * @param param1 Optional string parameter
+ */
+ void emitError(ErrorCode code, const QString &param1 = 0);
+
+ /**
+ * Emit an engine event.
+ *
+ * @param type Event type
+ * @param param1 Optional string parameter
+ * @param param2 Optional string parameter
+ */
+ void emitEvent(Event::Type type, const QString &param1 = 0, const QString &param2 = 0);
+
+ /**
+ * Emit an engine event containing a directory listing.
+ *
+ * @param type Event type
+ * @param param1 The DirectoryListing parameter
+ */
+ void emitEvent(Event::Type type, DirectoryListing param1);
+
+ /**
+ * Emit an engine event containing a filesize.
+ *
+ * @param type Event type
+ * @param param1 The filesize parameter
+ */
+ void emitEvent(Event::Type type, filesize_t param1);
+
+ /**
+ * Emit an engine event containing a custom pointer.
+ *
+ * @param type Event type
+ * @param param1 The custom pointer parameter
+ */
+ void emitEvent(Event::Type type, void *param1);
+
+ /**
+ * This method will set the socket's remote encoding which will be used when
+ * converting filenames into UTF-8 and back.
+ *
+ * @param encoding A valid encoding name
+ */
+ virtual void changeEncoding(const QString &encoding);
+
+ /**
+ * Retrieve the KRemoteEncoding object for this socket set to the appropriate
+ * encoding.
+ *
+ * @return The KRemoteEncoding object
+ */
+ KRemoteEncoding *remoteEncoding() { return m_remoteEncoding; }
+
+ /**
+ * Sets the current directory path.
+ *
+ * @param path The current directory path
+ */
+ void setCurrentDirectory(const QString &path) { m_currentDirectory = path; }
+
+ /**
+ * Get the current directory path.
+ *
+ * @return The current directory path.
+ */
+ virtual QString getCurrentDirectory() { return m_currentDirectory; }
+
+ /**
+ * Sets the default directory path (like a remote home directory).
+ *
+ * @param path The default directory path
+ */
+ void setDefaultDirectory(const QString &path) { m_defaultDirectory = path; }
+
+ /**
+ * Get the default directory path.
+ *
+ * @return The default directory path
+ */
+ virtual QString getDefaultDirectory() { return m_defaultDirectory; }
+
+ /**
+ * Sets the url this socket is connected to.
+ *
+ * @param url The url this socket is connected to
+ */
+ void setCurrentUrl(const KURL &url) { m_currentUrl = url; }
+
+ /**
+ * Get the url this socket is connected to.
+ *
+ * @return The url this socket is currently connected to
+ */
+ KURL getCurrentUrl() { return m_currentUrl; }
+
+ /**
+ * Sets the command the socket is currently executing.
+ *
+ * @param type Command type
+ */
+ void setCurrentCommand(Commands::Type type) { m_currentCommand = type; }
+
+ /**
+ * Get the current socket command.
+ *
+ * @return The current socket command
+ */
+ Commands::Type getCurrentCommand();
+
+ /**
+ * Get the toplevel socket command in the command chain.
+ *
+ * @return The toplevel socket command
+ */
+ Commands::Type getToplevelCommand();
+
+ /**
+ * Get the command that executed the current command. Note that this
+ * is valid only if the current command is chained. Otherwise this
+ * method returns Commands::CmdNone.
+ *
+ * @return The previous command
+ */
+ Commands::Type getPreviousCommand();
+
+ /**
+ * Get the last directory listing made by protoList.
+ *
+ * @return The last directory listing
+ */
+ DirectoryListing getLastDirectoryListing() { return m_lastDirectoryListing; }
+
+ /**
+ * Get the last stat response made by protoStat.
+ *
+ * @return The last stat response
+ */
+ DirectoryEntry getStatResponse() { return m_lastStatResponse; }
+
+ /**
+ * Get the number of bytes transfered from the beginning of the transfer.
+ *
+ * @return The number of bytes transfered
+ */
+ filesize_t getTransferBytes() { return m_transferBytes; }
+
+ /**
+ * Get the current transfer speed.
+ *
+ * @return The current transfer speed.
+ */
+ filesize_t getTransferSpeed();
+
+ /**
+ * This method will be called every cycle. It should be usually used to
+ * poll the data transfer socket.
+ */
+ virtual void poll() = 0;
+
+ /**
+ * Wakeup the last command processor with a specific wakeup event. This
+ * is used for async two-way communication between the engine and the
+ * GUI (wakeup event is a reply from the GUI).
+ *
+ * By default this method just passes the event to the currently active
+ * command processor.
+ *
+ * @param event The wakeup event that should be passed to the command class
+ */
+ virtual void wakeup(WakeupEvent *event);
+
+ /**
+ * Reset the current command class, possibly invoking the calling chained
+ * command class or completing the operation.
+ *
+ * @param code The result code
+ */
+ virtual void resetCommandClass(ResetCode code = Ok);
+
+ /**
+ * Add a command class to the command chain so that it will be executed next.
+ *
+ * @param cmd The command class to add
+ */
+ void addToCommandChain(Commands::Base *cmd) { m_commandChain.append(cmd); }
+
+ /**
+ * Execute the next command.
+ */
+ void nextCommand();
+
+ /**
+ * Schedule the execution of the next command in the next thread loop.
+ */
+ void nextCommandAsync();
+
+ /**
+ * Returns true if the current command has been chained from another command class.
+ *
+ * @return True if the current command has been chained
+ */
+ bool isChained() { return m_commandChain.count() > 0; }
+
+ /**
+ * Set the error reporting on or off. This variable is then used by some
+ * command classes do determine if they should emit errors and reset with
+ * failure or if they should just silently ignore the error and reset
+ * the command class with an Ok code.
+ *
+ * @param value Error reporting value
+ */
+ void setErrorReporting(bool value) { m_errorReporting = value; }
+
+ /**
+ * Get the current error reporting setting. This only makes sense if the
+ * class is chained - otherwise this allways returns true.
+ *
+ * @return The current error reporting setting
+ */
+ bool errorReporting() { return m_errorReporting || !isChained(); }
+
+ /**
+ * Returns true if the current operation should abort. This method should be
+ * used when the underlying socket implementation is doing blocking operations.
+ *
+ * @return True if the operation should be aborted
+ */
+ bool shouldAbort() { return m_shouldAbort; }
+protected:
+ /**
+ * Call this method when a long wait period has started or ended. If the wait
+ * isn't nulled before the timeout is reached the current action will be aborted
+ * and the socket will be disconnected.
+ *
+ * @param start True if the wait period should start, false if it should end
+ */
+ void timeoutWait(bool start);
+
+ /**
+ * Reset the timeout counter. Call this once in a while during long wait periods
+ * to notify the engine that the socket is still responsive.
+ */
+ void timeoutPing();
+
+ /**
+ * Check if we should timeout. This method might cause a disconnect if the timeout
+ * value is reached.
+ */
+ void timeoutCheck();
+
+ /**
+ * Enable the issue of keepalive packets.
+ */
+ void keepaliveStart();
+
+ /**
+ * Check if we should transmit a new keepalive packet.
+ */
+ void keepaliveCheck();
+protected:
+ KRemoteEncoding *m_remoteEncoding;
+
+ Commands::Base *m_cmdData;
+ QPtrList<Commands::Base> m_commandChain;
+
+ Thread *m_thread;
+ DirectoryListing m_lastDirectoryListing;
+ DirectoryEntry m_lastStatResponse;
+
+ filesize_t m_transferBytes;
+ time_t m_speedLastTime;
+ filesize_t m_speedLastBytes;
+
+ QTime m_timeoutCounter;
+ QTime m_keepaliveCounter;
+private:
+ QMap<QString, QString> m_config;
+ QString m_currentDirectory;
+ QString m_defaultDirectory;
+ KURL m_currentUrl;
+ QString m_protocol;
+ Commands::Type m_currentCommand;
+ bool m_errorReporting;
+ bool m_shouldAbort;
+ QGuardedPtr<ConnectionRetry> m_connectionRetry;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/speedlimiter.cpp b/kftpgrabber/src/engine/speedlimiter.cpp
new file mode 100644
index 0000000..85f2b72
--- /dev/null
+++ b/kftpgrabber/src/engine/speedlimiter.cpp
@@ -0,0 +1,240 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2007 by the KFTPGrabber developers
+ * Copyright (C) 2003-2007 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "speedlimiter.h"
+#include "misc/config.h"
+
+#include <kstaticdeleter.h>
+
+using namespace KFTPCore;
+
+namespace KFTPEngine {
+
+static const int tickDelay = 250;
+static int bucketSize = 1000 / tickDelay;
+
+SpeedLimiter *SpeedLimiter::m_self = 0;
+static KStaticDeleter<SpeedLimiter> staticSpeedLimiterDeleter;
+
+SpeedLimiter *SpeedLimiter::self()
+{
+ if (!m_self) {
+ staticSpeedLimiterDeleter.setObject(m_self, new SpeedLimiter());
+ }
+
+ return m_self;
+}
+
+SpeedLimiter::SpeedLimiter()
+ : m_timer(false)
+{
+ // Reset limits and token debts
+ m_limits[0] = 0;
+ m_limits[1] = 0;
+
+ m_tokenDebt[0] = 0;
+ m_tokenDebt[1] = 0;
+
+ // Subscribe to config updates and update the limits
+ connect(Config::self(), SIGNAL(configChanged()), this, SLOT(updateLimits()));
+ updateLimits();
+}
+
+SpeedLimiter::~SpeedLimiter()
+{
+ if (m_self == this)
+ staticSpeedLimiterDeleter.setObject(m_self, 0, false);
+}
+
+void SpeedLimiter::updateLimits()
+{
+ setLimit(SpeedLimiter::Download, Config::downloadSpeedLimit() * 1024);
+ setLimit(SpeedLimiter::Upload, Config::uploadSpeedLimit() * 1024);
+}
+
+void SpeedLimiter::setLimit(Type type, int limit)
+{
+ m_limits[type] = limit;
+}
+
+void SpeedLimiter::append(SpeedLimiterItem *item, Type type)
+{
+ m_objects[type].append(item);
+
+ int limit = m_limits[type];
+ if (limit > 0) {
+ int tokens = limit * tickDelay / 1000;
+ tokens /= m_objects[type].count();
+
+ if (m_tokenDebt[type] > 0) {
+ if (tokens >= m_tokenDebt[type]) {
+ tokens -= m_tokenDebt[type];
+ m_tokenDebt[type] = 0;
+ } else {
+ tokens = 0;
+ }
+ }
+
+ item->m_availableBytes = tokens;
+ } else {
+ item->m_availableBytes = -1;
+ }
+
+ // Fire the timer if not running
+ if (!m_timer) {
+ startTimer(tickDelay);
+ m_timer = true;
+ }
+}
+
+void SpeedLimiter::remove(SpeedLimiterItem *item)
+{
+ remove(item, Download);
+ remove(item, Upload);
+}
+
+void SpeedLimiter::remove(SpeedLimiterItem *item, Type type)
+{
+ if (m_objects[type].containsRef(item)) {
+ int tokens = m_limits[type] * tickDelay / 1000;
+ tokens /= m_objects[type].count();
+
+ if (item->m_availableBytes < tokens)
+ m_tokenDebt[type] += tokens - item->m_availableBytes;
+
+ m_objects[type].removeRef(item);
+ }
+
+ item->m_availableBytes = -1;
+}
+
+void SpeedLimiter::timerEvent(QTimerEvent*)
+{
+ QPtrList<SpeedLimiterItem> pendingWakeup;
+
+ for (int i = 0; i < 2; i++) {
+ m_tokenDebt[i] = 0;
+
+ int limit = m_limits[i];
+ if (!limit) {
+ // There is no limit, reset all items
+ for (SpeedLimiterItem *item = m_objects[i].first(); item; item = m_objects[i].next()) {
+ item->m_availableBytes = -1;
+ }
+
+ continue;
+ }
+
+ // If there are no objects, just skip it
+ if (m_objects[i].isEmpty())
+ continue;
+
+ int tokens = limit * tickDelay / 1000;
+ if (!tokens)
+ tokens = 1;
+
+ int maxTokens = tokens * bucketSize;
+
+ // Get amount of tokens for each object
+ int tokensPerObject = tokens / m_objects[i].count();
+ if (!tokensPerObject)
+ tokensPerObject = 1;
+
+ tokens = 0;
+
+ QPtrList<SpeedLimiterItem> unsaturatedObjects;
+
+ for (SpeedLimiterItem *item = m_objects[i].first(); item; item = m_objects[i].next()) {
+ if (item->m_availableBytes == -1) {
+ item->m_availableBytes = tokensPerObject;
+ unsaturatedObjects.append(item);
+ } else {
+ item->m_availableBytes += tokensPerObject;
+
+ if (item->m_availableBytes > maxTokens) {
+ tokens += item->m_availableBytes - maxTokens;
+ item->m_availableBytes = maxTokens;
+ } else {
+ unsaturatedObjects.append(item);
+ }
+ }
+ }
+
+ // Assign any left-overs to unsaturated sources
+ while (tokens && !unsaturatedObjects.isEmpty()) {
+ tokensPerObject = tokens / unsaturatedObjects.count();
+ if (!tokensPerObject)
+ break;
+
+ tokens = 0;
+
+ for (SpeedLimiterItem *item = unsaturatedObjects.first(); item; item = unsaturatedObjects.next()) {
+ item->m_availableBytes += tokensPerObject;
+
+ if (item->m_availableBytes > maxTokens) {
+ tokens += item->m_availableBytes - maxTokens;
+ item->m_availableBytes = maxTokens;
+ unsaturatedObjects.removeRef(item);
+ }
+ }
+ }
+ }
+
+ if (m_objects[0].isEmpty() && m_objects[1].isEmpty()) {
+ killTimers();
+ m_timer = false;
+ }
+}
+
+SpeedLimiterItem::SpeedLimiterItem()
+ : m_availableBytes(-1)
+{
+}
+
+void SpeedLimiterItem::updateUsage(int bytes)
+{
+ // Ignore if there are no limits
+ if (m_availableBytes == -1)
+ return;
+
+ if (bytes > m_availableBytes)
+ m_availableBytes = 0;
+ else
+ m_availableBytes -= bytes;
+}
+
+}
+
+#include "speedlimiter.moc"
diff --git a/kftpgrabber/src/engine/speedlimiter.h b/kftpgrabber/src/engine/speedlimiter.h
new file mode 100644
index 0000000..789cb19
--- /dev/null
+++ b/kftpgrabber/src/engine/speedlimiter.h
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2007 by the KFTPGrabber developers
+ * Copyright (C) 2003-2007 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPENGINESPEEDLIMITER_H
+#define KFTPENGINESPEEDLIMITER_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+namespace KFTPEngine {
+
+class SpeedLimiterItem;
+
+/**
+ * This class is used by Socket implementations to enforce speed limits for
+ * uploads or downloads. It implements a variant of Token Bucket algorithm.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class SpeedLimiter : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Possible limit types.
+ */
+ enum Type {
+ Download = 0,
+ Upload = 1
+ };
+
+ /**
+ * Returns the global speed limiter instance.
+ */
+ static SpeedLimiter *self();
+
+ /**
+ * Class destructor.
+ */
+ ~SpeedLimiter();
+
+ /**
+ * Set a limit rate.
+ *
+ * @param type Limit type
+ * @param limit Rate
+ */
+ void setLimit(Type type, int limit);
+
+ /**
+ * Appends an item to be managed by the speed limiter.
+ *
+ * @param item Item instance
+ * @param type Limit type
+ */
+ void append(SpeedLimiterItem *item, Type type);
+
+ /**
+ * Removes an item from the speed limiter.
+ *
+ * @param item Item instance
+ */
+ void remove(SpeedLimiterItem *item);
+
+ /**
+ * Removes an item from the speed limiter.
+ *
+ * @param item Item instance
+ * @param type Limit type
+ */
+ void remove(SpeedLimiterItem *item, Type type);
+protected:
+ /**
+ * Static class instance.
+ */
+ static SpeedLimiter *m_self;
+
+ /**
+ * Class constructor.
+ */
+ SpeedLimiter();
+
+ /**
+ * Timer event.
+ */
+ void timerEvent(QTimerEvent*);
+private:
+ bool m_timer;
+ int m_limits[2];
+
+ QPtrList<SpeedLimiterItem> m_objects[2];
+
+ int m_tokenDebt[2];
+private slots:
+ void updateLimits();
+};
+
+/**
+ * This class represents an item managed by the speed limiter. This is
+ * usually a socket.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class SpeedLimiterItem {
+friend class SpeedLimiter;
+public:
+ /**
+ * Class constructor.
+ */
+ SpeedLimiterItem();
+
+ /**
+ * Returns the number of bytes allowed for consumption.
+ */
+ int allowedBytes() const { return m_availableBytes; }
+protected:
+ /**
+ * Updates object's byte usage.
+ */
+ void updateUsage(int bytes);
+private:
+ int m_availableBytes;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/ssl.cpp b/kftpgrabber/src/engine/ssl.cpp
new file mode 100644
index 0000000..92418bb
--- /dev/null
+++ b/kftpgrabber/src/engine/ssl.cpp
@@ -0,0 +1,264 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "ssl.h"
+
+#include <ksocketdevice.h>
+#include <kmdcodec.h>
+#include <ksslx509v3.h>
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include <unistd.h>
+
+namespace KFTPEngine {
+
+class Ssl::Private {
+public:
+ Private()
+ : ssl(0), sslCtx(0), bio(0)
+ {
+ }
+
+ bool initialized;
+
+ SSL *ssl;
+ SSL_CTX *sslCtx;
+ BIO *bio;
+ X509 *certificate;
+};
+
+Ssl::Ssl(KNetwork::KStreamSocket *socket)
+ : d(new Ssl::Private()),
+ m_socket(socket)
+{
+ d->ssl = 0;
+ d->sslCtx = 0;
+ d->bio = 0;
+ d->certificate = 0;
+ d->initialized = false;
+
+ initialize();
+}
+
+Ssl::~Ssl()
+{
+ close();
+ delete d;
+}
+
+void Ssl::initialize()
+{
+ if (!d->ssl) {
+ SSL_library_init();
+
+ d->sslCtx = SSL_CTX_new(SSLv23_client_method());
+ d->ssl = SSL_new(d->sslCtx);
+
+ SSL_CTX_set_options(d->sslCtx, SSL_OP_ALL);
+
+ // Initialize the socket BIO
+ d->bio = BIO_new_socket(m_socket->socketDevice()->socket(), BIO_NOCLOSE);
+ SSL_set_bio(d->ssl, d->bio, d->bio);
+ }
+
+ d->initialized = true;
+}
+
+bool Ssl::connect()
+{
+ if (!d->initialized)
+ return false;
+
+retry_connect:
+ int ret = SSL_connect(d->ssl);
+ if (ret == 1) {
+ // Connection established
+ setConnectionInfo();
+ return true;
+ } else {
+ int err = SSL_get_error(d->ssl, ret);
+
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+retry_poll:
+ bool input;
+ m_socket->socketDevice()->poll(&input, 0, 0, 0);
+
+ if (input)
+ goto retry_connect;
+ else {
+ ::usleep(20000);
+ goto retry_poll;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Ssl::setClientCertificate(KSSLPKCS12 *pkcs)
+{
+ if (!pkcs || !pkcs->getCertificate())
+ return false;
+
+ int ret;
+ X509 *x;
+ EVP_PKEY *k = pkcs->getPrivateKey();
+ QCString cert = QCString(pkcs->getCertificate()->toString().ascii());
+
+ QByteArray qba, qbb = cert.copy();
+ KCodecs::base64Decode(qbb, qba);
+#if OPENSSL_VERSION_NUMBER > 0x009070afL
+ const unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data());
+#else
+ unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data());
+#endif
+ x = d2i_X509(NULL, &qbap, qba.size());
+
+ if (!x || !k)
+ return false;
+
+ if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())
+ return false;
+
+ ret = SSL_CTX_use_certificate(d->sslCtx, x);
+ if (ret <= 0)
+ return false;
+
+ ret = SSL_CTX_use_PrivateKey(d->sslCtx, k);
+ if (ret <= 0)
+ return false;
+
+ return true;
+}
+
+void Ssl::setConnectionInfo()
+{
+ SSL_CIPHER *cipher;
+ char buffer[1024];
+
+ buffer[0] = 0;
+ cipher = SSL_get_current_cipher(d->ssl);
+
+ if (!cipher)
+ return;
+
+ m_connectionInfo.m_cipherUsedBits = SSL_CIPHER_get_bits(cipher, &(m_connectionInfo.m_cipherBits));
+ m_connectionInfo.m_cipherVersion = SSL_CIPHER_get_version(cipher);
+ m_connectionInfo.m_cipherName = SSL_CIPHER_get_name(cipher);
+ m_connectionInfo.m_cipherDescription = SSL_CIPHER_description(cipher, buffer, 1023);
+}
+
+SslConnectionInfo &Ssl::connectionInfo()
+{
+ return m_connectionInfo;
+}
+
+void Ssl::close()
+{
+ if (!d->initialized)
+ return;
+
+ if (d->certificate) {
+ X509_free(d->certificate);
+ d->certificate = 0;
+ }
+
+ if (d->ssl) {
+ SSL_shutdown(d->ssl);
+ SSL_free(d->ssl);
+ SSL_CTX_free(d->sslCtx);
+
+ d->ssl = 0;
+ d->sslCtx = 0;
+ d->bio = 0;
+ }
+}
+
+int Ssl::read(void *buffer, int bytes)
+{
+ if (!d->initialized)
+ return -1;
+
+ int ret = SSL_read(d->ssl, buffer, bytes);
+
+ if (ret <= 0) {
+ int err = SSL_get_error(d->ssl, ret);
+
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
+ return 0;
+ else
+ return -1;
+ }
+
+ return ret;
+}
+
+int Ssl::write(void *buffer, int bytes)
+{
+ if (!d->initialized)
+ return -1;
+
+retry_write:
+ int ret = SSL_write(d->ssl, buffer, bytes);
+
+ if (ret <= 0) {
+ int err = SSL_get_error(d->ssl, ret);
+
+ if (err == SSL_ERROR_WANT_READ) {
+retry_poll:
+ bool input;
+ m_socket->socketDevice()->poll(&input, 0, 0, 0);
+
+ if (input)
+ goto retry_write;
+ else {
+ ::usleep(20000);
+ goto retry_poll;
+ }
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ return -1;
+ } else {
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+}
diff --git a/kftpgrabber/src/engine/ssl.h b/kftpgrabber/src/engine/ssl.h
new file mode 100644
index 0000000..e0933ed
--- /dev/null
+++ b/kftpgrabber/src/engine/ssl.h
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPENGINESSL_H
+#define KFTPENGINESSL_H
+
+#include <kstreamsocket.h>
+#include <ksslcertificate.h>
+#include <ksslpkcs12.h>
+
+namespace KFTPEngine {
+
+/**
+ * This class contains information about the currently established SSL
+ * connection.
+ *
+ * @author Jernej Kos
+ */
+class SslConnectionInfo {
+friend class Ssl;
+public:
+ /**
+ * Get the cipher in use.
+ */
+ const QString &getCipher() const { return m_cipherName; }
+
+ /**
+ * Describe the cipher in use.
+ */
+ const QString &getCipherDescription() const { return m_cipherDescription; }
+
+ /**
+ * Get the version of the cipher in use.
+ */
+ const QString &getCipherVersion() const { return m_cipherVersion; }
+
+ /**
+ * Get the number of bits of the cipher that are actually used.
+ */
+ int getCipherUsedBits() const { return m_cipherUsedBits; }
+
+ /**
+ * Get bit-size of the cipher.
+ */
+ int getCipherBits() const { return m_cipherBits; }
+protected:
+ /**
+ * Class constructor.
+ */
+ SslConnectionInfo() {}
+
+ int m_cipherUsedBits;
+ int m_cipherBits;
+
+ QString m_cipherName;
+ QString m_cipherDescription;
+ QString m_cipherVersion;
+};
+
+/**
+ * A class that properly handles asynchronious SSL connections.
+ *
+ * @author Jernej Kos
+ */
+class Ssl {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param socket The socket to use as transport
+ */
+ Ssl(KNetwork::KStreamSocket *socket);
+
+ /**
+ * Class destructor.
+ */
+ ~Ssl();
+
+ /**
+ * Start the SSL handshake. This method will block until the
+ * handshake is completed.
+ *
+ * @return True if the handshake was successful, false otherwise
+ */
+ bool connect();
+
+ /**
+ * Close the SSL connection and deallocate resources.
+ */
+ void close();
+
+ /**
+ * Read from the underlying socket.
+ *
+ * @param buffer The tarrget buffer
+ * @param bytes Maximum number of bytes to read
+ * @return Number of bytes actually read or -1 in case of an error
+ */
+ int read(void *buffer, int bytes);
+
+ /**
+ * Write to the underlying socket.
+ *
+ * @param buffer The source buffer
+ * @param bytes Number of bytes to write
+ * @return Number of bytes actually written or -1 in case of an error
+ */
+ int write(void *buffer, int bytes);
+
+ /**
+ * Obtain a reference to the connection information.
+ *
+ * @return A reference ot the connection information, valid after connected
+ */
+ SslConnectionInfo &connectionInfo();
+
+ /**
+ * Set the client certificate to use.
+ *
+ * @return True if the certificate was successfuly set
+ */
+ bool setClientCertificate(KSSLPKCS12 *pkcs);
+private:
+ class Private;
+ Private *d;
+
+ KNetwork::KStreamSocket *m_socket;
+ SslConnectionInfo m_connectionInfo;
+protected:
+ /**
+ * Initialize the SSL session for operation.
+ */
+ void initialize();
+
+ /**
+ * Populate the connection info object with data retrieved from the SSL
+ * socket. Note that the socket has to be connected!
+ */
+ void setConnectionInfo();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/engine/thread.cpp b/kftpgrabber/src/engine/thread.cpp
new file mode 100644
index 0000000..3e151b5
--- /dev/null
+++ b/kftpgrabber/src/engine/thread.cpp
@@ -0,0 +1,346 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "thread.h"
+#include "ftpsocket.h"
+#include "sftpsocket.h"
+
+#include <qapplication.h>
+
+namespace KFTPEngine {
+
+Thread::Thread()
+ : QThread(),
+ m_eventHandler(new EventHandler(this)),
+ m_socket(0),
+ m_wakeupEvent(0),
+ m_abortLoop(false),
+ m_wakeUpPending(false)
+{
+ m_protocolMap.insert("ftp", new FtpSocket(this));
+ m_protocolMap.insert("sftp", new SftpSocket(this));
+
+ // FTP is the default protocol
+ m_socket = m_protocolMap["ftp"];
+
+ // Auto start the thread
+ start();
+}
+
+Thread::~Thread()
+{
+ m_abortLoop = true;
+
+ if (!wait(1000))
+ terminate();
+
+ // Destroy all protocol sockets
+ delete static_cast<FtpSocket*>(m_protocolMap["ftp"]);
+ delete static_cast<SftpSocket*>(m_protocolMap["sftp"]);
+
+ m_protocolMap.clear();
+}
+
+void Thread::run()
+{
+ while (!m_abortLoop) {
+ QThread::usleep(100);
+
+ // "Poll" the socket
+ m_socket->poll();
+
+ // Transmit wakeup events if any
+ if (m_wakeUpPending && m_socket->isBusy()) {
+ m_wakeupMutex.lock();
+ m_socket->wakeup(m_wakeupEvent);
+
+ delete m_wakeupEvent;
+ m_wakeupEvent = 0;
+ m_wakeUpPending = false;
+ m_wakeupMutex.unlock();
+ }
+
+ // Execute any pending commands if the socket isn't busy
+ if (!m_commandQueue.empty()) {
+ m_queueMutex.lock();
+
+ QValueList<Commands::Type>::iterator queueEnd = m_commandQueue.end();
+ for (QValueList<Commands::Type>::iterator i = m_commandQueue.begin(); i != queueEnd; ++i) {
+ Commands::Type cmdType = *i;
+
+ // Execute the command
+ if (cmdType == Commands::CmdNext) {
+ m_commandQueue.remove(i--);
+ m_socket->nextCommand();
+ } else if (!m_socket->isBusy()) {
+ m_commandQueue.remove(i--);
+ m_socket->setCurrentCommand(cmdType);
+
+ switch (cmdType) {
+ case Commands::CmdConnect: {
+ m_socket->protoConnect(nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdDisconnect: {
+ m_socket->protoDisconnect();
+ break;
+ }
+ case Commands::CmdList: {
+ m_socket->protoList(nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdScan: {
+ m_socket->protoScan(nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdGet: {
+ m_socket->protoGet(nextCommandParameter().asUrl(),
+ nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdPut: {
+ m_socket->protoPut(nextCommandParameter().asUrl(),
+ nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdDelete: {
+ m_socket->protoDelete(nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdRename: {
+ m_socket->protoRename(nextCommandParameter().asUrl(),
+ nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdChmod: {
+ m_socket->protoChmod(nextCommandParameter().asUrl(),
+ nextCommandParameter().asFileSize(),
+ nextCommandParameter().asBoolean());
+ break;
+ }
+ case Commands::CmdMkdir: {
+ m_socket->protoMkdir(nextCommandParameter().asUrl());
+ break;
+ }
+ case Commands::CmdRaw: {
+ m_socket->protoRaw(nextCommandParameter().asString());
+ break;
+ }
+ case Commands::CmdFxp: {
+ m_socket->protoSiteToSite(static_cast<Socket*>(nextCommandParameter().asData()),
+ nextCommandParameter().asUrl(),
+ nextCommandParameter().asUrl());
+ break;
+ }
+ default: {
+ // Just ignore unknown commands for now
+ break;
+ }
+ }
+ }
+ }
+
+ m_queueMutex.unlock();
+ }
+ }
+}
+
+void Thread::wakeup(WakeupEvent *event)
+{
+ QMutexLocker locker(&m_wakeupMutex);
+
+ m_wakeupEvent = event;
+ m_wakeUpPending = true;
+}
+
+void Thread::abort()
+{
+ // Clear any pending wakeup events
+ if (m_wakeUpPending) {
+ QMutexLocker locker(&m_wakeupMutex);
+
+ m_wakeupEvent = 0;
+ m_wakeUpPending = false;
+ }
+
+ m_socket->protoAbort();
+}
+
+void Thread::event(Event::Type type, QValueList<EventParameter> params)
+{
+ if (m_eventHandler) {
+ Event *e = new Event(type, params);
+ qApp->postEvent(m_eventHandler, e);
+ }
+}
+
+void Thread::selectSocketForProtocol(const KURL &url)
+{
+ if (url.protocol() == m_socket->protocolName())
+ return;
+
+ // Change the socket if one exists
+ Socket *socket = m_protocolMap.find(url.protocol());
+ if (socket)
+ m_socket = socket;
+}
+
+EventParameter Thread::nextCommandParameter()
+{
+ QMutexLocker locker(&m_paramsMutex);
+ EventParameter param = m_commandParams.front();
+ m_commandParams.pop_front();
+
+ return param;
+}
+
+void Thread::connect(const KURL &url)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ // Setup the correct socket to use for connection
+ selectSocketForProtocol(url);
+
+ m_commandQueue.append(Commands::CmdConnect);
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::disconnect()
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdDisconnect);
+}
+
+void Thread::list(const KURL &url)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdList);
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::scan(const KURL &url)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdScan);
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::get(const KURL &source, const KURL &destination)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdGet);
+ m_commandParams.append(EventParameter(destination));
+ m_commandParams.append(EventParameter(source));
+}
+
+void Thread::put(const KURL &source, const KURL &destination)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdPut);
+ m_commandParams.append(EventParameter(destination));
+ m_commandParams.append(EventParameter(source));
+}
+
+void Thread::remove(const KURL &url)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdDelete);
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::rename(const KURL &source, const KURL &destination)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdRename);
+ m_commandParams.append(EventParameter(destination));
+ m_commandParams.append(EventParameter(source));
+}
+
+void Thread::chmod(const KURL &url, int mode, bool recursive)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdChmod);
+ m_commandParams.append(EventParameter(recursive));
+ m_commandParams.append(EventParameter(mode));
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::mkdir(const KURL &url)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdMkdir);
+ m_commandParams.append(EventParameter(url));
+}
+
+void Thread::raw(const QString &raw)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdRaw);
+ m_commandParams.append(EventParameter(raw));
+}
+
+void Thread::siteToSite(Thread *thread, const KURL &source, const KURL &destination)
+{
+ QMutexLocker locker(&m_paramsMutex);
+ QMutexLocker lockerq(&m_queueMutex);
+
+ m_commandQueue.append(Commands::CmdFxp);
+ m_commandParams.append(EventParameter(destination));
+ m_commandParams.append(EventParameter(source));
+ m_commandParams.append(EventParameter(thread->socket()));
+}
+
+}
diff --git a/kftpgrabber/src/engine/thread.h b/kftpgrabber/src/engine/thread.h
new file mode 100644
index 0000000..62a36c4
--- /dev/null
+++ b/kftpgrabber/src/engine/thread.h
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPENGINETHREAD_H
+#define KFTPENGINETHREAD_H
+
+#include <qthread.h>
+#include <qmutex.h>
+#include <qvaluelist.h>
+#include <qdict.h>
+
+#include "event.h"
+#include "directorylisting.h"
+#include "commands.h"
+#include "socket.h"
+
+namespace KFTPEngine {
+
+/**
+ * This class represents a socket thread. It serves as a command queue to
+ * the underlying socket implementation and also as an abstraction layer
+ * to support multiple protocols.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class Thread : public QThread
+{
+friend class EventHandler;
+friend class Socket;
+public:
+ Thread();
+ ~Thread();
+
+ /**
+ * Returns the event handler for this thread. Should be used to connect
+ * to any signals this thread may emit.
+ *
+ * @return A pointer to the EventHandler object
+ */
+ EventHandler *eventHandler() { return m_eventHandler; }
+
+ /**
+ * Returns the underlying socket object.
+ *
+ * @return A pointer to the Socket object
+ */
+ Socket *socket() { return m_socket; }
+
+ /**
+ * Prepare the apropriate socket for use.
+ *
+ * @param url The url that should be used to identify the protocol
+ */
+ void selectSocketForProtocol(const KURL &url);
+
+ /**
+ * Schedules a wakeup event to be passed on to the underlying socket.
+ *
+ * @param event The wakeup event to pass on
+ */
+ void wakeup(WakeupEvent *event);
+
+ void abort();
+ void connect(const KURL &url);
+ void disconnect();
+ void list(const KURL &url);
+ void scan(const KURL &url);
+ void get(const KURL &source, const KURL &destination);
+ void put(const KURL &source, const KURL &destination);
+ void remove(const KURL &url);
+ void rename(const KURL &source, const KURL &destination);
+ void chmod(const KURL &url, int mode, bool recursive = false);
+ void mkdir(const KURL &url);
+ void raw(const QString &raw);
+ void siteToSite(Thread *thread, const KURL &source, const KURL &destination);
+protected:
+ void run();
+ void event(Event::Type type, QValueList<EventParameter> params);
+
+ EventParameter nextCommandParameter();
+protected:
+ EventHandler *m_eventHandler;
+ Socket *m_socket;
+
+ QMutex m_eventMutex;
+ QMutex m_wakeupMutex;
+ QMutex m_paramsMutex;
+ QMutex m_queueMutex;
+
+ QDict<Socket> m_protocolMap;
+ QValueList<Commands::Type> m_commandQueue;
+ QValueList<EventParameter> m_commandParams;
+ WakeupEvent *m_wakeupEvent;
+
+ bool m_abortLoop;
+ bool m_wakeUpPending;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/hi16-app-kftpgrabber.png b/kftpgrabber/src/hi16-app-kftpgrabber.png
new file mode 100644
index 0000000..d529947
--- /dev/null
+++ b/kftpgrabber/src/hi16-app-kftpgrabber.png
Binary files differ
diff --git a/kftpgrabber/src/hi22-app-kftpgrabber.png b/kftpgrabber/src/hi22-app-kftpgrabber.png
new file mode 100644
index 0000000..e58a6b6
--- /dev/null
+++ b/kftpgrabber/src/hi22-app-kftpgrabber.png
Binary files differ
diff --git a/kftpgrabber/src/hi32-app-kftpgrabber.png b/kftpgrabber/src/hi32-app-kftpgrabber.png
new file mode 100644
index 0000000..ebdef81
--- /dev/null
+++ b/kftpgrabber/src/hi32-app-kftpgrabber.png
Binary files differ
diff --git a/kftpgrabber/src/hi48-app-kftpgrabber.png b/kftpgrabber/src/hi48-app-kftpgrabber.png
new file mode 100644
index 0000000..d26533c
--- /dev/null
+++ b/kftpgrabber/src/hi48-app-kftpgrabber.png
Binary files differ
diff --git a/kftpgrabber/src/hi64-app-kftpgrabber.png b/kftpgrabber/src/hi64-app-kftpgrabber.png
new file mode 100644
index 0000000..9cd1acd
--- /dev/null
+++ b/kftpgrabber/src/hi64-app-kftpgrabber.png
Binary files differ
diff --git a/kftpgrabber/src/kftpbookmarkaction.cpp b/kftpgrabber/src/kftpbookmarkaction.cpp
new file mode 100644
index 0000000..60d4904
--- /dev/null
+++ b/kftpgrabber/src/kftpbookmarkaction.cpp
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpbookmarkaction.h"
+
+KFTPWalletAction::KFTPWalletAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name)
+ : KAction(text, pix, cut, receiver, slot, parent, name)
+{
+}
+
+KFTPZeroconfAction::KFTPZeroconfAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name)
+ : KAction(text, pix, cut, receiver, slot, parent, name)
+{
+}
+
+#if KDE_IS_VERSION(3,4,0)
+void KFTPZeroconfAction::setSite(DNSSD::RemoteService::Ptr service)
+{
+ m_service = service;
+}
+#endif
+
+KFTPBookmarkAction::KFTPBookmarkAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name)
+ : KAction(text, pix, cut, receiver, slot, parent, name)
+{
+ m_data = 0L;
+}
+
+
+#include "kftpbookmarkaction.moc"
diff --git a/kftpgrabber/src/kftpbookmarkaction.h b/kftpgrabber/src/kftpbookmarkaction.h
new file mode 100644
index 0000000..91b245c
--- /dev/null
+++ b/kftpgrabber/src/kftpbookmarkaction.h
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKACTION_H
+#define KFTPBOOKMARKACTION_H
+
+#include <kaction.h>
+#include <kurl.h>
+#include <kdeversion.h>
+
+#include <qdom.h>
+
+#if KDE_IS_VERSION(3,4,0)
+#include "kftpzeroconf.h"
+#endif
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+/**
+ * This class represents a bookmark KAction that executes a connection to
+ * the bookmarked site.
+ *
+ * @author Jernej Kos
+ */
+class KFTPWalletAction : public KAction
+{
+Q_OBJECT
+public:
+ KFTPWalletAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name = 0);
+
+ void setSite(const KURL &site) { m_site = site; };
+ KURL getSite() { return m_site; }
+private:
+ KURL m_site;
+};
+
+/**
+ * This class represents a zeroconf KAction that executes a connection to
+ * the zeroconf resolved site.
+ *
+ * @author Jernej Kos
+ */
+class KFTPZeroconfAction : public KAction
+{
+Q_OBJECT
+public:
+ KFTPZeroconfAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name = 0);
+
+#if KDE_IS_VERSION(3,4,0)
+ void setSite(DNSSD::RemoteService::Ptr service);
+ DNSSD::RemoteService::Ptr getSite() { return m_service; }
+private:
+ DNSSD::RemoteService::Ptr m_service;
+#endif
+};
+
+/**
+ * This class represents a bookmark KAction that executes a connection to
+ * the bookmarked site.
+ *
+ * @author Jernej Kos
+ */
+class KFTPBookmarkAction : public KAction
+{
+Q_OBJECT
+public:
+ KFTPBookmarkAction(const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name = 0);
+
+ void setSiteId(const QString &id) { m_siteId = id; }
+ void setData(QObject *data) { m_data = data; }
+
+ QObject *data() { return m_data; }
+ QString siteId() { return m_siteId; }
+private:
+ QObject *m_data;
+ QString m_siteId;
+};
+
+#endif
diff --git a/kftpgrabber/src/kftpbookmarks.cpp b/kftpgrabber/src/kftpbookmarks.cpp
new file mode 100644
index 0000000..0211ac6
--- /dev/null
+++ b/kftpgrabber/src/kftpbookmarks.cpp
@@ -0,0 +1,948 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpbookmarks.h"
+#include "kftpbookmarkaction.h"
+#include "kftpqueue.h"
+#include "kftpapi.h"
+#include "kftpwalletconnection.h"
+#include "misc.h"
+#include "desencryptor.h"
+#include "browser/view.h"
+#include "kftpsession.h"
+#include "bookmarks/listview.h"
+
+#include "misc/config.h"
+#include "engine/thread.h"
+#include "engine/ftpsocket.h"
+
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpassdlg.h>
+#include <kmessagebox.h>
+#include <kdeversion.h>
+#include <kapplication.h>
+#include <kio/passdlg.h>
+#include <kstaticdeleter.h>
+
+/* KSSL includes */
+#include <ksslpkcs12.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPBookmarks {
+
+Site::Site(QDomNode node)
+ : m_element(node.toElement())
+{
+ // generate id if it is not present!
+ if (m_element.tagName() == "category") {
+ m_type = ST_CATEGORY;
+
+ if (getAttribute("id").isEmpty())
+ setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));
+ } else if (m_element.tagName() == "server") {
+ m_type = ST_SITE;
+
+ if (getAttribute("id").isEmpty())
+ setAttribute("id", QString("site-%1").arg(KApplication::randomString(7)));
+ }
+
+ // Set the id
+ m_id = getAttribute("id");
+}
+
+Site::~Site()
+{
+}
+
+Site *Site::duplicate()
+{
+ Site *site = new Site(m_element.cloneNode());
+ site->setAttribute("name", i18n("Copy of") + " " + getAttribute("name"));
+
+ // Assign a new id
+ site->setAttribute("id", QString("site-%1").arg(KApplication::randomString(7)));
+ site->m_id = site->getAttribute("id");
+
+ m_element.parentNode().appendChild(site->m_element);
+
+ return site;
+}
+
+void Site::reparentSite(Site *site)
+{
+ // Move site's parent
+ m_element.appendChild(site->m_element);
+}
+
+Site *Site::addSite(QDomNode node)
+{
+ if (node.isNull()) {
+ // If there was no node specified, create a new one
+ node = m_element.ownerDocument().createElement("server");
+ }
+
+ Site *site = new Site(node);
+ m_element.appendChild(site->m_element);
+
+ return site;
+}
+
+void Site::addCategory(const QString &name)
+{
+ QDomElement cat = m_element.ownerDocument().createElement("category");
+
+ // Assign a new id and name
+ cat.setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));
+ cat.setAttribute("name", name);
+
+ m_element.appendChild(cat);
+}
+
+KURL Site::getUrl()
+{
+ KURL url;
+
+ url.setProtocol(getProperty("protocol"));
+ url.setHost(getProperty("host"));
+ url.setPort(getIntProperty("port"));
+ url.setUser(getProperty("username"));
+ url.setPass(getProperty("password"));
+
+ return url;
+}
+
+Site *Site::getParentSite()
+{
+ // Get parent's site id, then search for it
+ QDomElement parent = m_element.parentNode().toElement();
+
+ if (parent.isNull())
+ return NULL;
+ else
+ return KFTPBookmarks::Manager::self()->findCategory(parent.attribute("id"));
+}
+
+QString Site::getProperty(const QString &name)
+{
+ QDomNodeList nodes = m_element.elementsByTagName(name);
+
+ if (nodes.length() > 0) {
+ QString property = nodes.item(0).toElement().text();
+ property.stripWhiteSpace();
+
+ // Automagicly decode passwords from BASE64
+ if (name.contains("pass") == 1)
+ property = decodePassword(property);
+
+ return property;
+ } else {
+ return QString::null;
+ }
+}
+
+int Site::getIntProperty(const QString &name)
+{
+ return getProperty(name).toInt();
+}
+
+void Site::setProperty(const QString &name, const QString &value)
+{
+ // First delete the old property if it exists
+ QDomNodeList nodes = m_element.elementsByTagName(name);
+
+ if (nodes.length() > 0)
+ m_element.removeChild(nodes.item(0));
+
+ // Now add a new one
+ QDomElement property = m_element.ownerDocument().createElement(name);
+ m_element.appendChild(property);
+
+ QDomText text = m_element.ownerDocument().createTextNode(value);
+ property.appendChild(text);
+}
+
+void Site::setProperty(const QString &name, int value)
+{
+ setProperty(name, QString::number(value));
+}
+
+void Site::setAttribute(const QString &name, const QString &value)
+{
+ m_element.setAttribute(name, value);
+}
+
+QString Site::getAttribute(const QString &name)
+{
+ return m_element.attribute(name);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Manager *Manager::m_self = 0;
+static KStaticDeleter<Manager> staticBookmarkManagerDeleter;
+
+Manager *Manager::self()
+{
+ if (!m_self) {
+ staticBookmarkManagerDeleter.setObject(m_self, new Manager());
+ }
+
+ return m_self;
+}
+
+Manager::Manager()
+ : QObject()
+{
+ // Init the cache
+ m_siteCache.setAutoDelete(true);
+
+ // Init the DOM document
+ m_document = QDomDocument("KFTPGrabber");
+}
+
+Manager::Manager(const Manager &bookmarks)
+ : QObject()
+{
+ // Init the cache
+ m_siteCache.setAutoDelete(true);
+
+ // Init the DOM document
+ m_document = QDomDocument("KFTPGrabber");
+
+ // Copy the bookmarks
+ QDomNode tmp = m_document.importNode(bookmarks.m_document.documentElement(), true);
+ m_document.appendChild(tmp);
+}
+
+Manager::~Manager()
+{
+ if (m_self == this)
+ staticBookmarkManagerDeleter.setObject(m_self, 0, false);
+}
+
+void Manager::setBookmarks(KFTPBookmarks::Manager *bookmarks)
+{
+ // Init the DOM document
+ m_document = QDomDocument("KFTPGrabber");
+
+ QDomNode tmp = m_document.importNode(bookmarks->m_document.documentElement(), true);
+ m_document.appendChild(tmp);
+
+ // Clear the cache
+ m_siteCache.clear();
+
+ emit update();
+}
+
+void Manager::importSites(QDomNode node)
+{
+ QDomNode import = m_document.importNode(node, true);
+ m_document.documentElement().appendChild(import);
+
+ // Run sanity checks to generate missing ids
+ Manager::validate();
+}
+
+void Manager::load(const QString &filename)
+{
+ m_filename = filename;
+
+ QFile file(filename);
+ if (!file.open(IO_ReadOnly)) {
+ // Create a new empty XML
+ m_document.setContent(QString("<sites version=\"%1\"></sites>").arg(KFTP_BOOKMARKS_VERSION));
+
+ return;
+ }
+
+ // Check if the file is encrpyted
+ QCString content(file.readAll());
+
+ if (KFTPCore::Config::encryptBookmarks()) {
+ // File is encrypted
+pwd_entry:
+ int saveToWallet = 1;
+
+ QCString p_pass(KFTPAPI::getInstance()->walletConnection()->getPassword("bookmarkDecryptPwd").ascii());
+ if (QString(p_pass).isNull()) {
+ // Ask the user for a password
+ int ret = KPasswordDialog::getPassword(p_pass, i18n("This bookmark file is encrypted. Please enter key for decryption."), &saveToWallet);
+
+ if (ret != KPasswordDialog::Accepted) {
+ // User pressed cancel
+ p_pass = "";
+ }
+ }
+
+ // Try to decrypt
+ DESEncryptor des;
+ des.setKey(p_pass);
+ des.decrypt(content);
+
+ if (des.output().left(6) != "<sites" && des.output().left(9) != "<!DOCTYPE") {
+ // Clear any saved passwords
+ KFTPAPI::getInstance()->walletConnection()->setPassword("bookmarkDecryptPwd", QString::null);
+
+ if (KMessageBox::warningContinueCancel(
+ KFTPAPI::getInstance()->mainWindow(),
+ i18n("<qt>Bookmark file decryption has failed with provided key. Do you want to <b>overwrite</b> bookmarks with an empty file ?<br><br><font color=\"red\"><b>Warning:</b> If you overwrite, all current bookmarks will be lost.</font></qt>"),
+ i18n("Decryption Failed"),
+ KGuiItem(i18n("&Overwrite Bookmarks"))
+ ) != KMessageBox::Continue)
+ {
+ // Request the password again
+ goto pwd_entry;
+ }
+
+ // Create new empty XML
+ m_document.setContent(QString("<sites version=\"%1\"></sites>").arg(KFTP_BOOKMARKS_VERSION));
+
+ file.close();
+ return;
+ }
+
+ // Save the password for later encryption
+ m_decryptKey = p_pass;
+ content = des.output().ascii();
+
+ if (saveToWallet) {
+ // Save the password to KWallet
+ KFTPAPI::getInstance()->walletConnection()->setPassword("bookmarkDecryptPwd", p_pass);
+ }
+ }
+
+ m_document.setContent(QString::fromLocal8Bit(content));
+ file.close();
+
+ // Check for XML document version updates
+ versionUpdate();
+
+ // Document validation
+ Manager::validate();
+
+ // We have just loaded the bookmarks, so update all the menus
+ emit update();
+}
+
+void Manager::save()
+{
+ // Save the new XML file
+ if (m_filename.isEmpty()) {
+ qDebug("WARNING: No open XML file, will NOT save.");
+ return;
+ }
+
+ QFile file(m_filename);
+ if (!file.open(IO_WriteOnly)) {
+ qDebug("WARNING: Unable to open xml for writing!");
+ return;
+ }
+
+ // Should we encrypt the data ?
+ QString content = m_document.toString();
+ if (KFTPCore::Config::encryptBookmarks()) {
+ DESEncryptor des;
+
+ if (m_decryptKey.isEmpty()) {
+ // Ask the user for the password
+ KPasswordDialog::getPassword(m_decryptKey, i18n("Enter key for bookmark file encryption."));
+ }
+
+ des.setKey(m_decryptKey);
+ des.encrypt(content);
+
+ content = des.output();
+ }
+
+ // Write the XML data to the stream
+ QTextStream fileStream(&file);
+ fileStream << content;
+ file.flush();
+ file.close();
+}
+
+void Manager::validate(QDomNode node)
+{
+ if (node.isNull())
+ node = m_document.documentElement();
+
+ QDomNode n = node.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ if (!n.toElement().hasAttribute("id"))
+ n.toElement().setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));
+
+ Manager::validate(n);
+ } else if (n.toElement().tagName() == "server") {
+ if (!n.toElement().hasAttribute("id"))
+ n.toElement().setAttribute("id", QString("site-%1").arg(KApplication::randomString(7)));
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+void Manager::versionUpdate()
+{
+ int version = m_document.documentElement().attribute("version").toInt();
+
+ if (version < KFTP_BOOKMARKS_VERSION) {
+ // Conversion from an old bookmark file
+ qDebug("Detected an old (version %d, new version %d) bookmarks file. Starting conversion process...", version, KFTP_BOOKMARKS_VERSION);
+
+ // NOTE: There are no breaks here, since every update method updates to a specific
+ // version. So in order to convert to the most current from the oldest version, all
+ // methods need to be called!
+ switch (version) {
+ case 0:
+ case 1: versionFrom1Update();
+ }
+
+ // Fix the version
+ m_document.documentElement().setAttribute("version", KFTP_BOOKMARKS_VERSION);
+ }
+}
+
+void Manager::versionFrom1Update(QDomNode parent)
+{
+ // The original format had no site ids, so we have to generate them. Also the old
+ // format used something wierd called "options", we have to convert them as well.
+ // The username/password fields now have differend names.
+
+ if (parent.isNull())
+ parent = m_document.documentElement();
+
+ QDomNode n = parent.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ // Update the category id and all children
+ n.toElement().setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));
+
+ versionFrom1Update(n);
+ } else if (n.toElement().tagName() == "server") {
+ // Update the server id
+ n.toElement().setAttribute("id", QString("site-%1").arg(KApplication::randomString(7)));
+
+ // Convert the "options"
+ QDomNodeList nodes = n.toElement().elementsByTagName("option");
+
+ if (nodes.length() > 0) {
+ for (unsigned int i = 0; i < nodes.count(); i++) {
+ QDomNode node = nodes.item(i);
+
+ // Add a new standard property
+ QDomElement property = m_document.createElement(node.toElement().attribute("name"));
+ n.appendChild(property);
+
+ QDomText text = m_document.createTextNode(node.toElement().attribute("value"));
+ property.appendChild(text);
+
+ // And remove the option :>
+ node.parentNode().removeChild(node);
+ i--;
+ }
+ }
+
+ // Rename the username/password fields
+ nodes = n.toElement().elementsByTagName("downuser");
+ if (nodes.length() > 0) {
+ for (unsigned int i = 0; i < nodes.count(); i++) {
+ QDomNode node = nodes.item(i);
+ node.toElement().setTagName("username");
+ }
+ }
+
+ nodes = n.toElement().elementsByTagName("downpass");
+ if (nodes.length() > 0) {
+ for (unsigned int i = 0; i < nodes.count(); i++) {
+ QDomNode node = nodes.item(i);
+ node.toElement().setTagName("password");
+ }
+ }
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+Site *Manager::findSite(const KURL &url)
+{
+ // Find the node, then see if it is already present in the cache
+ QDomNode siteElement = findSiteElementByUrl(url);
+
+ if (!siteElement.isNull()) {
+ // Try to get a cached version
+ Site *site = m_siteCache.find(siteElement.toElement().attribute("id"));
+
+ if (!site) {
+ site = new Site(siteElement);
+ m_siteCache.insert(siteElement.toElement().attribute("id"), site);
+ }
+
+ return site;
+ } else {
+ return NULL;
+ }
+}
+
+Site *Manager::findSite(const QString &id)
+{
+ if (id.isNull())
+ return NULL;
+
+ // Try the cache first
+ Site *site = m_siteCache.find(id);
+
+ if (!site) {
+ // The site was not found, search in the DOM tree and add it to the
+ // cache if found.
+ QDomNode siteElement = findSiteElementById(id);
+
+ if (siteElement.isNull()) {
+ qDebug("WARNING: Unable to find site with id '%s'!", id.ascii());
+
+ return NULL;
+ }
+
+ site = new Site(siteElement);
+ m_siteCache.insert(id, site);
+ }
+
+ return site;
+}
+
+QDomNode Manager::findSiteElementByUrl(const KURL &url, QDomNode parent)
+{
+ if (parent.isNull())
+ parent = m_document.documentElement();
+
+ QDomNode n = parent.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ // Check the category
+ QDomNode site = findSiteElementByUrl(url, n);
+
+ if (!site.isNull())
+ return site;
+ } else if (n.toElement().tagName() == "server") {
+ // Check if the server matches
+ Site *tmp = new Site(n);
+
+ if (tmp->getProperty("host") == url.host() &&
+ tmp->getIntProperty("port") == url.port() &&
+ tmp->getProperty("username") == url.user() &&
+ tmp->getProperty("password") == url.pass())
+ {
+ delete tmp;
+ return n;
+ }
+
+ delete tmp;
+ }
+
+ n = n.nextSibling();
+ }
+
+ return QDomNode();
+}
+
+QDomNode Manager::findSiteElementById(const QString &id, QDomNode parent)
+{
+ if (parent.isNull())
+ parent = m_document.documentElement();
+
+ QDomNode n = parent.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ // Check the category
+ QDomNode site = findSiteElementById(id, n);
+
+ if (!site.isNull())
+ return site;
+ } else if (n.toElement().tagName() == "server") {
+ // Check if the server matches
+ if (n.toElement().attribute("id") == id)
+ return n;
+ }
+
+ n = n.nextSibling();
+ }
+
+ return QDomNode();
+}
+
+QDomNode Manager::findCategoryElementById(const QString &id, QDomNode parent)
+{
+ if (id == "root")
+ return m_document.documentElement();
+
+ if (parent.isNull())
+ parent = m_document.documentElement();
+
+ QDomNode n = parent.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ if (n.toElement().attribute("id") == id)
+ return n;
+
+ // Check the category
+ QDomNode site = findCategoryElementById(id, n);
+
+ if (!site.isNull())
+ return site;
+ }
+
+ n = n.nextSibling();
+ }
+
+ return QDomNode();
+}
+
+Site *Manager::findCategory(const QString &id)
+{
+ // Try the cache first
+ Site *site = m_siteCache.find(id);
+
+ if (!site) {
+ // The site was not found, search in the DOM tree and add it to the
+ // cache if found.
+ QDomNode siteElement = findCategoryElementById(id);
+
+ if (siteElement.isNull()) {
+ qDebug("WARNING: Unable to find category with id '%s'!", id.ascii());
+
+ return NULL;
+ }
+
+ site = new Site(siteElement);
+ m_siteCache.insert(id, site);
+ }
+
+ return site;
+}
+
+Site *Manager::addSite(Site *category, QDomNode node)
+{
+ if (category)
+ return category->addSite(node);
+
+ return NULL;
+}
+
+void Manager::delSite(Site *site)
+{
+ // Remove the node from the DOM tree
+ site->m_element.parentNode().removeChild(site->m_element);
+
+ // Remove the site from cache and it will be automaticly deleted
+ m_siteCache.remove(site->id());
+
+ emit update();
+}
+
+void Manager::setupClient(Site *site, KFTPEngine::Thread *client)
+{
+ if (site) {
+ // First activate the correct socket and reset the old flags
+ client->selectSocketForProtocol(KURL(QString("%1://test/").arg(site->getProperty("protocol"))));
+ client->socket()->initConfig();
+
+ int retryTime = site->getIntProperty("retrytime");
+ int retryCnt = site->getIntProperty("retrycount");
+
+ if (retryTime != 0) {
+ client->socket()->setConfig("retry", 1);
+ client->socket()->setConfig("max_retries", retryCnt);
+ client->socket()->setConfig("retry_delay", retryTime);
+ } else {
+ client->socket()->setConfig("retry", 0);
+ }
+
+ client->socket()->setConfig("keepalive.enabled", site->getIntProperty("doKeepalive"));
+ client->socket()->setConfig("keepalive.timeout", site->getIntProperty("keepaliveTimeout"));
+
+ client->socket()->setConfig("ssl.use_tls", site->getIntProperty("use_tls"));
+ client->socket()->setConfig("ssl.use_implicit", site->getIntProperty("use_implicit"));
+ client->socket()->setConfig("ssl.prot_mode", site->getProperty("tls_data_mode"));
+ client->socket()->setConfig("feat.pasv", site->getIntProperty("disablePASV") == 1 ? 0 : 1);
+ client->socket()->setConfig("feat.epsv", site->getIntProperty("disableEPSV") == 1 ? 0 : 1);
+ client->socket()->setConfig("pasv.use_site_ip", site->getIntProperty("pasvSiteIp"));
+ client->socket()->setConfig("active.no_force_ip", site->getIntProperty("disableForceIp"));
+ client->socket()->setConfig("stat_listings", site->getIntProperty("statListings"));
+ client->socket()->setConfig("encoding", site->getProperty("encoding"));
+
+ // Should we use a X509 certificate ?
+ if (site->getIntProperty("use_cert") && site->getProperty("protocol") == "ftp") {
+ // Ask the user for the decryption password
+ QCString certPass;
+ KPasswordDialog::getPassword(certPass, i18n("Please provide your X509 certificate decryption password."));
+
+ static_cast<KFTPEngine::FtpSocket*>(client->socket())->setSslClientCertificate(KSSLPKCS12::loadCertFile(site->getProperty("tls_cert_path"), certPass));
+ }
+ } else {
+ // Just reset the client, since we don't know the config
+ client->socket()->initConfig();
+ }
+}
+
+void Manager::guiPopulateBookmarksTree(KFTPWidgets::Bookmarks::ListView *tree, QDomNode parent, KFTPWidgets::Bookmarks::ListViewItem *item)
+{
+ if (parent.isNull()) {
+ // Clear the tree and set the parent
+ tree->clear();
+ parent = m_document.documentElement();
+ }
+
+ QDomNode n = parent.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ // Add a submenu
+ KFTPWidgets::Bookmarks::ListViewItem *cat;
+ Site *site = findCategory(n.toElement().attribute("id"));
+
+ if (!item)
+ cat = new KFTPWidgets::Bookmarks::ListViewItem(tree, site->getAttribute("name"));
+ else
+ cat = new KFTPWidgets::Bookmarks::ListViewItem(item, site->getAttribute("name"));
+
+ cat->setType(0);
+ cat->setSite(site);
+
+ cat->setPixmap(0, loadSmallPixmap("bookmark_folder"));
+ guiPopulateBookmarksTree(tree, n, cat);
+ } else if (n.toElement().tagName() == "server") {
+ KFTPWidgets::Bookmarks::ListViewItem *serv;
+ Site *site = findSite(n.toElement().attribute("id"));
+
+ if (!item)
+ serv = new KFTPWidgets::Bookmarks::ListViewItem(tree, site->getAttribute("name"));
+ else
+ serv = new KFTPWidgets::Bookmarks::ListViewItem(item, site->getAttribute("name"));
+
+ serv->setType(1);
+ serv->setSite(site);
+
+ serv->setPixmap(0, loadSmallPixmap("ftp"));
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+void Manager::guiPopulateBookmarksMenu(KActionMenu *parentMenu, QDomNode parentNode, bool base, QObject *data)
+{
+ if (parentNode.isNull())
+ parentNode = m_document.documentElement();
+
+ QDomNode n = parentNode.firstChild();
+ KActionMenu *menu = 0L;
+ KFTPBookmarkAction *action = 0L;
+
+ while (!n.isNull()) {
+ QString name = n.toElement().attribute("name");
+
+ if (n.toElement().tagName() == "category") {
+ menu = new KActionMenu(name, "bookmark_folder", parentMenu);
+ parentMenu->insert(menu, base ? 5 : 0);
+
+ // Fill the menu
+ guiPopulateBookmarksMenu(menu, n, false, data);
+ } else if (n.toElement().tagName() == "server") {
+ action = new KFTPBookmarkAction(name, "ftp", KShortcut(), this, SLOT(slotBookmarkExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection());
+ action->setSiteId(n.toElement().attribute("id"));
+ action->setData(data);
+
+ parentMenu->insert(action);
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+void Manager::guiPopulateZeroconfMenu(KActionMenu *parentMenu)
+{
+ // Clear the menu
+ parentMenu->popupMenu()->clear();
+
+#if KDE_IS_VERSION(3,4,0)
+ // Populate
+ QValueList<DNSSD::RemoteService::Ptr> list = KFTPAPI::getInstance()->zeroConfInterface()->getServiceList();
+
+ if (!list.empty()) {
+ QValueList<DNSSD::RemoteService::Ptr>::iterator end(list.end());
+
+ for (QValueList<DNSSD::RemoteService::Ptr>::iterator i(list.begin()); i != end; ++i) {
+ KFTPZeroconfAction *newService = new KFTPZeroconfAction((*i)->serviceName(), "lan", KShortcut(), this, SLOT(slotZeroconfExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection());
+ newService->setSite(*i);
+
+ parentMenu->insert(newService);
+ }
+ } else {
+ KAction *disabledAction = new KAction(i18n("<No Services Published>"));
+ disabledAction->setEnabled(false);
+ parentMenu->insert(disabledAction);
+ }
+#else
+ KAction *disabledAction = new KAction(i18n("<DNSSD Not Available>"));
+ disabledAction->setEnabled(false);
+ parentMenu->insert(disabledAction);
+#endif
+}
+
+void Manager::guiPopulateWalletMenu(KActionMenu *parentMenu)
+{
+ // Clear the menu
+ parentMenu->popupMenu()->clear();
+
+ // Populate
+ QValueList<KURL> list = KFTPAPI::getInstance()->walletConnection()->getSiteList();
+
+ if (!list.empty()) {
+ QValueList<KURL>::iterator end(list.end());
+
+ for (QValueList<KURL>::iterator i(list.begin()); i != end; ++i) {
+ QString desc;
+
+ if ((*i).port() != 21)
+ desc = QString("%1@%2:%3").arg((*i).user()).arg((*i).host()).arg((*i).port());
+ else
+ desc = QString("%1@%2").arg((*i).user()).arg((*i).host());
+
+ KFTPWalletAction *newSite = new KFTPWalletAction(desc, "ftp", KShortcut(), this, SLOT(slotWalletExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection());
+ newSite->setSite(*i);
+
+ parentMenu->insert(newSite);
+ }
+ } else {
+ KAction *disabledAction = new KAction(i18n("<No Sites In KWallet>"));
+ disabledAction->setEnabled(false);
+ parentMenu->insert(disabledAction);
+ }
+}
+
+void Manager::slotBookmarkExecuted()
+{
+ // Get the sender
+ KFTPBookmarkAction *action = (KFTPBookmarkAction*) QObject::sender();
+ Site *site = findSite(action->siteId());
+
+ // Get the node data from bookmarks
+ KURL siteUrl = site->getUrl();
+
+ // Handle empty usernames and passwords for non-anonymous sites
+ if (!siteUrl.hasUser() || !siteUrl.hasPass() && siteUrl.user() != "anonymous") {
+ KIO::PasswordDialog *dlg = new KIO::PasswordDialog(i18n("Please provide your username and password for connecting to this site."), siteUrl.user(), true);
+ dlg->addCommentLine(i18n("Site:"), QString("%1:%2").arg(siteUrl.host()).arg(siteUrl.port()));
+
+ if (dlg->exec() == KDialogBase::Accepted) {
+ siteUrl.setUser(dlg->username());
+ siteUrl.setPass(dlg->password());
+
+ if (dlg->keepPassword()) {
+ // Save password to the bookmarked site
+ site->setProperty("username", dlg->username());
+ site->setProperty("password", encodePassword(dlg->password()));
+ }
+
+ delete dlg;
+ } else {
+ // Abort connection attempt
+ delete dlg;
+ return;
+ }
+ }
+
+ if (action->data()) {
+ // A specific session was passed on to us
+ KFTPSession::Session *session = static_cast<KFTPSession::Session*>(action->data());
+
+ // Set the correct client for connection
+ KFTPEngine::Thread *client = session->getClient();
+
+ // Now, connect to the server
+ if (client->socket()->isConnected()) {
+ if (KFTPCore::Config::confirmDisconnects() && KMessageBox::warningYesNo(0, i18n("Do you want to drop current connection?")) == KMessageBox::No)
+ return;
+ }
+
+ client->socket()->setCurrentUrl(siteUrl);
+
+ // Set the session's site and connect
+ session->setSite(site);
+ session->reconnect(siteUrl);
+ } else {
+ // Just spawn a new session
+ KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl, site);
+ KFTPSession::Manager::self()->setActive(session);
+ }
+}
+
+void Manager::slotWalletExecuted()
+{
+ // Get the sender
+ KFTPWalletAction *action = (KFTPWalletAction*) QObject::sender();
+ KURL siteUrl = action->getSite();
+
+ // Just spawn a new session
+ KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl);
+}
+
+void Manager::slotZeroconfExecuted()
+{
+#if KDE_IS_VERSION(3,4,0)
+ // Get the sender
+ KFTPZeroconfAction *action = (KFTPZeroconfAction*) QObject::sender();
+ DNSSD::RemoteService::Ptr service = action->getSite();
+
+ KFTPAPI::getInstance()->mainWindow()->slotQuickConnect(service->serviceName(), service->hostName(), service->port());
+#endif
+}
+
+}
+
+
+#include "kftpbookmarks.moc"
diff --git a/kftpgrabber/src/kftpbookmarks.h b/kftpgrabber/src/kftpbookmarks.h
new file mode 100644
index 0000000..769bb72
--- /dev/null
+++ b/kftpgrabber/src/kftpbookmarks.h
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKS_H
+#define KFTPBOOKMARKS_H
+
+#include <qstring.h>
+#include <qdom.h>
+#include <qvaluelist.h>
+#include <qcache.h>
+
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kurl.h>
+
+namespace KFTPEngine {
+ class Thread;
+}
+
+namespace KFTPWidgets {
+namespace Bookmarks {
+ class ListView;
+ class ListViewItem;
+}
+}
+
+#define KFTP_BOOKMARKS_VERSION 2
+
+namespace KFTPBookmarks {
+
+enum SiteType {
+ ST_SITE,
+ ST_CATEGORY
+};
+
+class Site {
+friend class Manager;
+public:
+ Site(QDomNode node);
+ ~Site();
+
+ void reparentSite(Site *site);
+
+ Site *addSite(QDomNode node = QDomNode());
+ void addCategory(const QString &name);
+
+ KURL getUrl();
+ Site *getParentSite();
+
+ Site *duplicate();
+
+ QString getProperty(const QString &name);
+ int getIntProperty(const QString &name);
+
+ void setProperty(const QString &name, const QString &value);
+ void setProperty(const QString &name, int value);
+
+ void setAttribute(const QString &name, const QString &value);
+ QString getAttribute(const QString &name);
+
+ SiteType type() { return m_type; }
+ QString id() { return m_id; }
+private:
+ SiteType m_type;
+ QString m_id;
+ QDomElement m_element;
+};
+
+class Manager : public QObject {
+Q_OBJECT
+public:
+ static Manager *self();
+ Manager();
+ Manager(const Manager &bookmarks);
+ ~Manager();
+
+ void setBookmarks(KFTPBookmarks::Manager *bookmarks);
+ void importSites(QDomNode node);
+
+ void load(const QString &filename);
+ void save();
+
+ Site *findSite(const QString &id);
+ Site *findSite(const KURL &url) KDE_DEPRECATED;
+
+ Site *findCategory(const QString &id);
+
+ Site *addSite(Site *category, QDomNode node);
+ void delSite(Site *site);
+
+ void setupClient(Site *site, KFTPEngine::Thread *client);
+
+ void guiPopulateBookmarksMenu(KActionMenu *parentMenu, QDomNode parentNode = QDomNode(), bool base = true, QObject *data = 0);
+ void guiPopulateBookmarksTree(KFTPWidgets::Bookmarks::ListView *tree, QDomNode parent = QDomNode(), KFTPWidgets::Bookmarks::ListViewItem *item = 0);
+ void guiPopulateZeroconfMenu(KActionMenu *parentMenu);
+ void guiPopulateWalletMenu(KActionMenu *parentMenu);
+
+ void emitUpdate() { emit update(); }
+protected:
+ static Manager *m_self;
+private:
+ QCache<Site> m_siteCache;
+ QDomDocument m_document;
+
+ QCString m_decryptKey;
+ QString m_filename;
+
+ QDomNode findSiteElementByUrl(const KURL &url, QDomNode parent = QDomNode());
+ QDomNode findSiteElementById(const QString &id, QDomNode parent = QDomNode());
+ QDomNode findCategoryElementById(const QString &id, QDomNode parent = QDomNode());
+
+ // Validation
+ void validate(QDomNode node = QDomNode());
+
+ // XML conversion methods
+ void versionUpdate();
+ void versionFrom1Update(QDomNode parent = QDomNode());
+private slots:
+ void slotBookmarkExecuted();
+ void slotZeroconfExecuted();
+ void slotWalletExecuted();
+signals:
+ void update();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftpfileexistsactions.cpp b/kftpgrabber/src/kftpfileexistsactions.cpp
new file mode 100644
index 0000000..d9e7e69
--- /dev/null
+++ b/kftpgrabber/src/kftpfileexistsactions.cpp
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpfileexistsactions.h"
+
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+
+#include <kdialog.h>
+#include <kcombobox.h>
+#include <klocale.h>
+
+namespace KFTPQueue {
+
+QString &operator<<(QString &s, const FileExistsActions &a)
+{
+ s.truncate(0);
+
+ ActionMap::ConstIterator end( a.m_actions.end() );
+ for (ActionMap::ConstIterator i( a.m_actions.begin() ); i != end; ++i) {
+ s.append(QString("%1;").arg(i.data()));
+ }
+
+ return s;
+}
+
+QString &operator>>(QString &s, FileExistsActions &a)
+{
+ for (unsigned int i = 0; i < 9; i++) {
+ a.m_actions[i+1] = static_cast<FEAction>(s.section(';', i, i).toInt());
+ }
+
+ return s;
+}
+
+QWidget *FileExistsActions::getConfigWidget(QWidget *parent)
+{
+ QGroupBox *gb = new QGroupBox(0, Qt::Vertical, i18n("On File Exists Actions (%1)").arg(m_type), parent);
+ gb->layout()->setSpacing(KDialog::spacingHint());
+ gb->layout()->setMargin(KDialog::marginHint());
+
+ QGridLayout *gl = new QGridLayout(gb->layout(), 5, 4, 5);
+
+ QLabel *l = new QLabel(i18n("Size/Timestamp"), gb);
+ gl->addWidget(l, 1, 0);
+
+ l = new QLabel(i18n("Same"), gb);
+ gl->addWidget(l, 1, 1);
+
+ l = new QLabel(i18n("Older"), gb);
+ gl->addWidget(l, 1, 2);
+
+ l = new QLabel(i18n("Newer"), gb);
+ gl->addWidget(l, 1, 3);
+
+ l = new QLabel(i18n("Same"), gb);
+ gl->addWidget(l, 2, 0);
+
+ l = new QLabel(i18n("Smaller"), gb);
+ gl->addWidget(l, 3, 0);
+
+ l = new QLabel(i18n("Bigger"), gb);
+ gl->addWidget(l, 4, 0);
+
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ KComboBox *cb = new KComboBox(gb);
+ m_combos[row][col] = cb;
+
+ cb->insertItem(i18n("Skip"));
+ cb->insertItem(i18n("Overwrite"));
+ cb->insertItem(i18n("Resume"));
+ cb->insertItem(i18n("Rename"));
+ cb->insertItem(i18n("Ask"));
+ cb->setCurrentItem(m_actions[row * 3 + col + 1]);
+
+ gl->addWidget(cb, row+2, col+1);
+ }
+ }
+
+ return gb;
+}
+
+void FileExistsActions::updateWidget()
+{
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ m_combos[row][col]->setCurrentItem(m_actions[row * 3 + col + 1]);
+ }
+ }
+}
+
+void FileExistsActions::updateConfig()
+{
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ m_actions[row * 3 + col + 1] = static_cast<FEAction>(m_combos[row][col]->currentItem());
+ }
+ }
+}
+
+FEAction FileExistsActions::getActionForSituation(filesize_t src_fileSize, time_t src_fileTimestamp,
+ filesize_t dst_fileSize, time_t dst_fileTimestamp)
+{
+ // There are 9 different scenarios
+ int situation = -1;
+
+ if (dst_fileTimestamp == src_fileTimestamp) {
+ // SAME TIMESTAMP
+ situation = 1;
+ } else if (dst_fileTimestamp < src_fileTimestamp) {
+ // OLDER
+ situation = 2;
+ } else {
+ // NEWER
+ situation = 3;
+ }
+
+ if (dst_fileSize < src_fileSize) {
+ // SMALLER FILE
+ situation += 3;
+ } else if (dst_fileSize > src_fileSize) {
+ // BIGGER FILE
+ situation += 6;
+ }
+
+ // Situation calculated, now get the action
+ return m_actions[situation];
+}
+
+}
diff --git a/kftpgrabber/src/kftpfileexistsactions.h b/kftpgrabber/src/kftpfileexistsactions.h
new file mode 100644
index 0000000..be2fdfe
--- /dev/null
+++ b/kftpgrabber/src/kftpfileexistsactions.h
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEKFTPFILEEXISTSACTIONS_H
+#define KFTPQUEUEKFTPFILEEXISTSACTIONS_H
+
+#include <sys/types.h>
+
+#include <qwidget.h>
+#include <qmap.h>
+
+typedef unsigned long long int filesize_t;
+
+class KComboBox;
+
+namespace KFTPQueue {
+
+enum FEAction {
+ FE_DISABLE_ACT = -1,
+ FE_SKIP_ACT = 0,
+ FE_OVERWRITE_ACT = 1,
+ FE_RESUME_ACT = 2,
+ FE_RENAME_ACT = 3,
+ FE_USER_ACT = 4
+};
+
+typedef QMap<int, FEAction> ActionMap;
+
+/**
+ * This class provides configurable "on file exists" actions. They are
+ * represented in a 3x3 matrix which determines the 9 possible scenarios.
+ * The matrix goes like this:
+ * <pre>
+ * | SAME TIMESTAMP | OLDER | NEWER
+ * SAME FILESIZE | 1 | 2 | 3
+ * SMALLER | 4 | 5 | 6
+ * BIGGER | 7 | 8 | 9
+ * </pre>
+ *
+ * @author Jernej Kos
+ */
+class FileExistsActions
+{
+public:
+ /**
+ * This method will construct a new widget that will represent
+ * the current status of the overwrite matrix and the ability to
+ * change the values.
+ *
+ * @param parent Widget's parent
+ * @return A new @ref QWidget
+ */
+ QWidget *getConfigWidget(QWidget *parent = 0);
+
+ /**
+ * Set action for a specific file exists situation.
+ *
+ * @param situation Situation (acoording to the above matrix)
+ * @action A FEAction that determines the appropriate action
+ */
+ void setActionForSituation(int situation, FEAction action) { m_actions[situation] = action; }
+
+ /**
+ * Get action for specific file exists situation.
+ *
+ * @param src_fileSize File size of the file that exists
+ * @param src_fileTimestamp File timestamp of the file that exists
+ * @param dst_fileSize File size of the file that will (or not) replace the old one
+ * @param dst_fileTimestamp File timestamp of the file that will (or not) replace the old one
+ * @return An action as @ref FEAction
+ */
+ FEAction getActionForSituation(filesize_t src_fileSize, time_t src_fileTimestamp,
+ filesize_t dst_fileSize, time_t dst_fileTimestamp);
+
+ /**
+ * Sets a text that will be used as type for these actions (like download/upload).
+ *
+ * @param text The text
+ */
+ void setTypeText(const QString &text) { m_type = text; }
+
+ /**
+ * Update the current GUI widget with new settings.
+ */
+ void updateWidget();
+
+ /**
+ * Update the current configuration with the new settings (as dictated by the
+ * GUI).
+ */
+ void updateConfig();
+private:
+ ActionMap m_actions;
+ QString m_type;
+ KComboBox *m_combos[3][3];
+
+ friend QString &operator<<(QString &s, const FileExistsActions &a);
+ friend QString &operator>>(QString &s, FileExistsActions &a);
+};
+
+QString &operator<<(QString &s, const FileExistsActions &a);
+QString &operator>>(QString &s, FileExistsActions &a);
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftpgrabber-bi-wizard.png b/kftpgrabber/src/kftpgrabber-bi-wizard.png
new file mode 100644
index 0000000..906e4c8
--- /dev/null
+++ b/kftpgrabber/src/kftpgrabber-bi-wizard.png
Binary files differ
diff --git a/kftpgrabber/src/kftpgrabber-logo.png b/kftpgrabber/src/kftpgrabber-logo.png
new file mode 100644
index 0000000..b5ba48c
--- /dev/null
+++ b/kftpgrabber/src/kftpgrabber-logo.png
Binary files differ
diff --git a/kftpgrabber/src/kftpgrabber.desktop b/kftpgrabber/src/kftpgrabber.desktop
new file mode 100644
index 0000000..b26bc06
--- /dev/null
+++ b/kftpgrabber/src/kftpgrabber.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=KFTPGrabber
+Name[sv]=KFTPgrabber
+Name[xx]=xxKFTPGrabberxx
+GenericName=FTP Client
+GenericName[ar]= زبون ميفاق نقل الملفات FTP
+GenericName[bg]=FTP клиент
+GenericName[br]=Kliant FTP
+GenericName[ca]=Client FTP
+GenericName[cs]=FTP klient
+GenericName[cy]=Dibynnydd FTP
+GenericName[da]=FTP klient
+GenericName[de]=FTP-Client
+GenericName[el]=Πελάτης FTP
+GenericName[es]=Cliente de FTP
+GenericName[et]=FTP-klient
+GenericName[fr]=Client FTP
+GenericName[ga]=Cliant FTP
+GenericName[gl]=Cliente FTP
+GenericName[it]=Client FTP
+GenericName[ja]=FTP クライアント
+GenericName[ka]=FTP კლიენტი
+GenericName[lt]=FTP klientas
+GenericName[nl]=FTP-cliënt
+GenericName[pa]=FTP ਕਲਾਂਇਟ
+GenericName[pt]=Cliente de FTP
+GenericName[pt_BR]=Cliente FTP
+GenericName[ru]=Клиент FTP
+GenericName[rw]=Umukiriya FTP
+GenericName[sr]=FTP клијент
+GenericName[sr@Latn]=FTP klijent
+GenericName[sv]=FTP-klient
+GenericName[tr]=FTP İstemcisi
+GenericName[uk]=Клієнт FTP
+GenericName[xh]=Umxhasi we FTP
+GenericName[xx]=xxFTP Clientxx
+GenericName[zh_CN]=FTP 客户端
+GenericName[zu]=Umthengi we FTP
+Comment=A graphical FTP client
+Comment[ar]=زبون رسومي لميفاق نقل الملفات
+Comment[bg]=Графичен FTP клиент
+Comment[br]=Ur gliant skeudenn FTP
+Comment[ca]=Un client gràfic FTP
+Comment[cs]=Grafický FTP klient
+Comment[da]=En grafisk FTP klient
+Comment[de]=Graphischer FTP-Client
+Comment[el]=Ένας γραφικός πελάτης FTP
+Comment[es]=Un cliente gráfico de FTP
+Comment[et]=Graafiline FTP-klient
+Comment[fr]=Un client FTP graphique
+Comment[ga]=Cliant grafach FTP
+Comment[gl]=Un cliente FTP gráfico
+Comment[it]=Un client FTP grafico
+Comment[ja]=グラフィカルな FTP クライアント
+Comment[ka]=გრაფიკული FTP კლიენტი
+Comment[lt]=Grafinis FTP klientas
+Comment[nl]=Een grafische FTP-cliënt
+Comment[pa]=ਇੱਕ ਗਰਾਫਿਕਲ FTP ਕਲਾਂਇਟ
+Comment[pt]=Um cliente gráfico de FTP
+Comment[pt_BR]=Um cliente gráfico para FTP
+Comment[ru]=Графический клиент FTP
+Comment[sr]=Графички FTP лијент
+Comment[sr@Latn]=Grafički FTP lijent
+Comment[sv]=En grafisk FTP-klient
+Comment[tr]=Grafiksel FTP İstemcisi
+Comment[uk]=Клієнт FTP з графічним інтерфейсом
+Comment[xx]=xxA graphical FTP clientxx
+Comment[zh_CN]=图形化 FTP 客户端
+Exec=kftpgrabber
+Icon=kftpgrabber
+Type=Application
+Categories=Qt;KDE;Network;FileTransfer;
diff --git a/kftpgrabber/src/kftpgrabberui.rc b/kftpgrabber/src/kftpgrabberui.rc
new file mode 100644
index 0000000..b67eb2f
--- /dev/null
+++ b/kftpgrabber/src/kftpgrabberui.rc
@@ -0,0 +1,31 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kftpgrabber" version="1">
+
+<MenuBar>
+ <text>Main Menu</text>
+
+ <Menu name="file">
+ <text>&amp;File</text>
+
+ <Action name="file_newsession" />
+ <Action name="file_quick_connect" />
+ </Menu>
+
+ <Action name="bookmarks" />
+
+</MenuBar>
+
+<ToolBar name="mainToolBar" noMerge="1" fullWidth="true"><text>Main Toolbar</text>
+ <Action name="file_newsession" />
+ <Action name="file_quick_connect" />
+ <Action name="transfermode" />
+</ToolBar>
+
+<ToolBar name="bookmarkToolBar" noMerge="1" fullWidth="true"><text>Bookmark Toolbar</text>
+ <Action name="bookmark_new" />
+ <Action name="bookmark_edit2" />
+ <Action name="bookmark_delete" />
+ <Separator/>
+ <Action name="bookmark_subcat" />
+</ToolBar>
+</kpartgui>
diff --git a/kftpgrabber/src/kftpqueue.cpp b/kftpgrabber/src/kftpqueue.cpp
new file mode 100644
index 0000000..0186c4e
--- /dev/null
+++ b/kftpgrabber/src/kftpqueue.cpp
@@ -0,0 +1,834 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <math.h>
+
+#include "kftpqueue.h"
+#include "kftpbookmarks.h"
+#include "widgets/systemtray.h"
+#include "kftpqueueprocessor.h"
+#include "kftpsession.h"
+
+#include "misc/config.h"
+#include "misc/filter.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kio/renamedlg.h>
+#include <kdiskfreesp.h>
+#include <kfileitem.h>
+#include <kopenwith.h>
+#include <kstaticdeleter.h>
+#include <kservice.h>
+#include <kuserprofile.h>
+#include <kstandarddirs.h>
+#include <krun.h>
+#include <kmdcodec.h>
+
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qobjectlist.h>
+#include <qfile.h>
+
+using namespace KFTPEngine;
+using namespace KFTPCore::Filter;
+
+namespace KFTPQueue {
+
+OpenedFile::OpenedFile(TransferFile *transfer)
+ : m_source(transfer->getSourceUrl()),
+ m_dest(transfer->getDestUrl()),
+ m_hash(QString::null)
+{
+ // Calculate the file's MD5 hash
+ QFile file(m_dest.path());
+ if (!file.open(IO_ReadOnly)) {
+ return;
+ }
+
+ KMD5 context;
+ if (context.update(file))
+ m_hash = QString(context.hexDigest());
+ file.close();
+}
+
+bool OpenedFile::hasChanged()
+{
+ // Compare the file's MD5 hash with stored value
+ QFile file(m_dest.path());
+ if (!file.open(IO_ReadOnly)) {
+ return false;
+ }
+
+ QString tmp = QString::null;
+ KMD5 context;
+ if (context.update(file))
+ tmp = QString(context.hexDigest());
+ file.close();
+
+ return tmp != m_hash;
+}
+
+UserDialogRequest::UserDialogRequest(TransferFile *transfer, filesize_t srcSize, time_t srcTime,
+ filesize_t dstSize, time_t dstTime)
+ : m_transfer(transfer),
+ m_srcSize(srcSize),
+ m_srcTime(srcTime),
+ m_dstSize(dstSize),
+ m_dstTime(dstTime)
+{
+}
+
+void UserDialogRequest::sendResponse(FileExistsWakeupEvent *event)
+{
+ m_transfer->wakeup(event);
+ delete this;
+}
+
+Manager *Manager::m_self = 0;
+static KStaticDeleter<Manager> staticManagerDeleter;
+
+Manager *Manager::self()
+{
+ if (!m_self) {
+ staticManagerDeleter.setObject(m_self, new Manager());
+ }
+
+ return m_self;
+}
+
+Manager::Manager()
+ : m_topLevel(new QueueObject(this, QueueObject::Toplevel)),
+ m_processingQueue(false),
+ m_feDialogOpen(false),
+ m_defaultFeAction(FE_DISABLE_ACT)
+{
+ m_topLevel->setId(0);
+
+ m_lastQID = 1;
+ m_curDownSpeed = 0;
+ m_curUpSpeed = 0;
+
+ m_emitUpdate = true;
+
+ // Create the queue processor object
+ m_queueProc = new KFTPQueueProcessor(this);
+
+ connect(m_queueProc, SIGNAL(queueComplete()), this, SLOT(slotQueueProcessingComplete()));
+ connect(m_queueProc, SIGNAL(queueAborted()), this, SLOT(slotQueueProcessingAborted()));
+
+ // Create the queue converter object
+ m_converter = new KFTPQueueConverter(this);
+}
+
+Manager::~Manager()
+{
+ if (m_self == this)
+ staticManagerDeleter.setObject(m_self, 0, false);
+}
+
+void Manager::stopAllTransfers()
+{
+ if (isProcessing()) {
+ abort();
+ } else {
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ if (i->isRunning()) {
+ i->abort();
+ } else {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ if (t->isRunning())
+ t->abort();
+ }
+ }
+ }
+ }
+}
+
+Transfer *Manager::findTransfer(long id)
+{
+ // First try the cache
+ QueueObject *object = m_queueObjectCache[QString::number(id)];
+
+ if (!object) {
+ object = m_topLevel->findChildObject(id);
+ m_queueObjectCache.insert(QString::number(id), object);
+ }
+
+ return static_cast<Transfer*>(object);
+}
+
+Site *Manager::findSite(KURL url, bool noCreate)
+{
+ // Reset path
+ url.setPath("/");
+
+ if (url.isLocalFile())
+ return NULL;
+
+ // Find the appropriate site and if one doesn't exist create a new one
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ if (i->getType() == QueueObject::Site) {
+ Site *site = static_cast<Site*>(i);
+
+ if (site->getUrl() == url)
+ return site;
+ }
+ }
+
+ // The site doesn't exist, let's create one
+ if (!noCreate) {
+ Site *site = new Site(topLevelObject(), url);
+ site->setId(m_lastQID++);
+ emit newSite(site);
+
+ return site;
+ }
+
+ return 0;
+}
+
+void Manager::insertTransfer(Transfer *transfer)
+{
+ // Set id
+ transfer->setId(m_lastQID++);
+
+ // Reparent transfer
+ filesize_t size = transfer->getSize();
+ transfer->addSize(-size);
+
+ if (transfer->hasParentObject())
+ transfer->parentObject()->delChildObject(transfer);
+
+ if (transfer->parent())
+ transfer->parent()->removeChild(transfer);
+
+ Site *site = 0;
+
+ switch (transfer->getTransferType()) {
+ case Download: site = findSite(transfer->getSourceUrl()); break;
+ case Upload: site = findSite(transfer->getDestUrl()); break;
+ case FXP: site = findSite(transfer->getSourceUrl()); break;
+ }
+
+ site->insertChild(transfer);
+ site->addChildObject(transfer);
+ transfer->addSize(size);
+
+ emit newTransfer(transfer);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::insertTransfer(KURLDrag *drag)
+{
+ // Decode the drag
+ KIO::MetaData p_meta;
+ KURL::List p_urls;
+ KURLDrag::decode(drag, p_urls, p_meta);
+
+ // TODO make support for local drops - eg. from konqueror, where
+ // we get no meta data, so we must get the file info ourselves and
+ // reject remote urls (or show a dialog to ask the user if he
+ // wants to connect to the remote site)
+
+ // Now we should add transfers for all URLs
+ Transfer *lastTransfer = 0L;
+ KURL::List::iterator end(p_urls.end());
+
+ for (KURL::List::iterator i(p_urls.begin()); i != end; ++i) {
+ QString p_data = p_meta[(*i).htmlURL().local8Bit()];
+ QChar type = p_data.at(0);
+ filesize_t size = p_data.section(':', 1, 1).toULongLong();
+ KURL sourceUrl = (*i);
+ KURL destinationUrl = KURL(p_meta["DestURL"]);
+ destinationUrl.addPath(sourceUrl.fileName());
+
+ // Skip where both files are local
+ if (sourceUrl.isLocalFile() && destinationUrl.isLocalFile())
+ continue;
+
+ lastTransfer = spawnTransfer(sourceUrl, destinationUrl, size, type == 'D', true, true, 0L, true);
+ }
+
+ // Execute the transfer if set in configuration
+ if (!KFTPCore::Config::queueOnDND() && lastTransfer)
+ static_cast<KFTPQueue::Site*>(lastTransfer->parentObject())->delayedExecute();
+}
+
+Transfer *Manager::spawnTransfer(KURL sourceUrl, KURL destinationUrl, filesize_t size, bool dir, bool ignoreSkip,
+ bool insertToQueue, QObject *parent, bool noScan)
+{
+ const ActionChain *actionChain = Filters::self()->process(sourceUrl, size, dir);
+
+ if (!ignoreSkip && (actionChain && actionChain->getAction(Action::Skip)))
+ return 0;
+
+ // Determine transfer type
+ TransferType type;
+
+ if (sourceUrl.isLocalFile())
+ type = Upload;
+ else if (destinationUrl.isLocalFile())
+ type = Download;
+ else
+ type = FXP;
+
+ // Should we lowercase the destination path ?
+ if (actionChain && actionChain->getAction(Action::Lowercase))
+ destinationUrl.setPath(destinationUrl.directory() + "/" + destinationUrl.fileName().lower());
+
+ // Reset a possible preconfigured default action
+ setDefaultFileExistsAction();
+
+ if (!parent)
+ parent = this;
+
+ Transfer *transfer = 0L;
+
+ if (dir)
+ transfer = new TransferDir(parent);
+ else {
+ transfer = new TransferFile(parent);
+ transfer->addSize(size);
+ }
+
+ transfer->setSourceUrl(sourceUrl);
+ transfer->setDestUrl(destinationUrl);
+ transfer->setTransferType(type);
+
+ if (insertToQueue) {
+ insertTransfer(transfer);
+ } else {
+ transfer->setId(m_lastQID++);
+ emit newTransfer(transfer);
+ }
+
+ if (dir && !noScan) {
+ // This is a directory, we should scan the directory and add all files/dirs found
+ // as parent of current object
+ KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, sourceUrl, 0, true);
+ session->scanDirectory(transfer);
+ }
+
+ return transfer;
+}
+
+void Manager::removeTransfer(Transfer *transfer, bool abortSession)
+{
+ if (!transfer)
+ return;
+
+ transfer->abort();
+ long id = transfer->getId();
+ long sid = transfer->parentObject()->getId();
+
+ // Remove transfer from cache
+ m_queueObjectCache.remove(QString::number(id));
+
+ // Should the site be removed as well ?
+ QueueObject *site = 0;
+ if (transfer->parentObject()->getType() == QueueObject::Site && transfer->parentObject()->getChildrenList().count() == 1)
+ site = transfer->parentObject();
+
+ // Signal destruction & delete transfer
+ transfer->faceDestruction(abortSession);
+ delete transfer;
+
+ if (site) {
+ delete site;
+ emit siteRemoved(sid);
+ }
+
+ emit transferRemoved(id);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::revalidateTransfer(Transfer *transfer)
+{
+ QueueObject *i = transfer;
+
+ while (i) {
+ if (i->parentObject() == topLevelObject())
+ break;
+
+ i = i->parentObject();
+ }
+
+ // We have the site
+ Site *curSite = static_cast<Site*>(i);
+ Site *site = 0;
+
+ switch (transfer->getTransferType()) {
+ case Download: site = findSite(transfer->getSourceUrl()); break;
+ case Upload: site = findSite(transfer->getDestUrl()); break;
+ case FXP: site = findSite(transfer->getSourceUrl()); break;
+ }
+
+ // If the sites don't match, reparent transfer
+ if (site != curSite) {
+ transfer->parentObject()->delChildObject(transfer);
+ transfer->parent()->removeChild(transfer);
+
+ site->insertChild(transfer);
+ site->addChildObject(transfer);
+
+ emit transferRemoved(transfer->getId());
+ emit newTransfer(transfer);
+
+ if (curSite->getChildrenList().count() == 0) {
+ emit siteRemoved(curSite->getId());
+ curSite->deleteLater();
+ }
+ }
+}
+
+void Manager::removeFailedTransfer(FailedTransfer *transfer)
+{
+ // Remove the transfer and signal removal
+ m_failedTransfers.remove(transfer);
+ emit failedTransferRemoved(transfer->getTransfer()->getId());
+
+ delete transfer;
+}
+
+void Manager::clearFailedTransferList()
+{
+ // Clear the failed transfers list
+ FailedTransfer *transfer;
+ QPtrListIterator<KFTPQueue::FailedTransfer> i(m_failedTransfers);
+
+ while ((transfer = i.current()) != 0) {
+ ++i;
+ removeFailedTransfer(transfer);
+ }
+}
+
+void Manager::moveTransferUp(QueueObject *object)
+{
+ object->parentObject()->moveChildUp(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferDown(QueueObject *object)
+{
+ object->parentObject()->moveChildDown(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferTop(QueueObject *object)
+{
+ object->parentObject()->moveChildTop(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferBottom(QueueObject *object)
+{
+ object->parentObject()->moveChildBottom(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+bool Manager::canBeMovedUp(QueueObject *object)
+{
+ return object ? object->parentObject()->canMoveChildUp(object) : false;
+}
+
+bool Manager::canBeMovedDown(QueueObject *object)
+{
+ return object ? object->parentObject()->canMoveChildDown(object) : false;
+}
+
+void Manager::doEmitUpdate()
+{
+ m_curDownSpeed = 0;
+ m_curUpSpeed = 0;
+
+ topLevelObject()->removeMarkedTransfers();
+
+ // Get download/upload speeds
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ KFTPQueue::Transfer *tmp = static_cast<Transfer*>(t);
+
+ switch (tmp->getTransferType()) {
+ case Download: m_curDownSpeed += tmp->getSpeed(); break;
+ case Upload: m_curUpSpeed += tmp->getSpeed(); break;
+ case FXP: {
+ m_curDownSpeed += tmp->getSpeed();
+ m_curUpSpeed += tmp->getSpeed();
+ break;
+ }
+ }
+ }
+ }
+
+ // Emit global update to all GUI objects
+ emit queueUpdate();
+}
+
+void Manager::start()
+{
+ if (m_processingQueue)
+ return;
+
+ m_processingQueue = true;
+
+ // Now, go trough all queued files and execute them - try to do as little server connects
+ // as possible
+ m_queueProc->startProcessing();
+}
+
+void Manager::abort()
+{
+ m_processingQueue = false;
+
+ // Stop further queue processing
+ m_queueProc->stopProcessing();
+
+ emit queueUpdate();
+}
+
+void Manager::slotQueueProcessingComplete()
+{
+ m_processingQueue = false;
+
+ // Queue processing is now complete
+ if (KFTPCore::Config::showBalloons())
+ KFTPWidgets::SystemTray::self()->showBalloon(i18n("All queued transfers have been completed."));
+
+ emit queueUpdate();
+}
+
+void Manager::slotQueueProcessingAborted()
+{
+ m_processingQueue = false;
+}
+
+void Manager::clearQueue()
+{
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next())
+ removeTransfer(static_cast<Transfer*>(t));
+ }
+}
+
+int Manager::getTransferPercentage()
+{
+ return 0;
+}
+
+int Manager::getNumRunning(bool onlyDirs)
+{
+ int running = 0;
+
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ if (t->isRunning() && (!onlyDirs || t->isDir()))
+ running++;
+ }
+
+ if (i->isRunning())
+ running++;
+ }
+
+ return running;
+}
+
+int Manager::getNumRunning(const KURL &remoteUrl)
+{
+ int running = 0;
+ Site *site = findSite(remoteUrl, true);
+
+ if (site) {
+ QueueObject *i;
+ QPtrList<QueueObject> transfers = site->getChildrenList();
+
+ for (i = transfers.first(); i; i = transfers.next()) {
+ if (i->isRunning())
+ running++;
+ }
+ }
+
+ return running;
+}
+
+KFTPEngine::FileExistsWakeupEvent *Manager::fileExistsAction(TransferFile *transfer,
+ QValueList<KFTPEngine::DirectoryEntry> stat)
+{
+ FileExistsWakeupEvent *event = new FileExistsWakeupEvent();
+ FileExistsActions *fa = NULL;
+ FEAction action;
+
+ filesize_t srcSize = 0;
+ time_t srcTime = 0;
+
+ filesize_t dstSize = 0;
+ time_t dstTime = 0;
+
+ // Check if there is a default action set
+ action = getDefaultFileExistsAction();
+
+ if (action == FE_DISABLE_ACT) {
+ switch (transfer->getTransferType()) {
+ case KFTPQueue::Download: {
+ KFileItem info(KFileItem::Unknown, KFileItem::Unknown, transfer->getDestUrl());
+ dstSize = info.size();
+ dstTime = info.time(KIO::UDS_MODIFICATION_TIME);
+
+ srcSize = stat[0].size();
+ srcTime = stat[0].time();
+
+ fa = KFTPCore::Config::self()->dActions();
+ break;
+ }
+ case KFTPQueue::Upload: {
+ KFileItem info(KFileItem::Unknown, KFileItem::Unknown, transfer->getSourceUrl());
+ srcSize = info.size();
+ srcTime = info.time(KIO::UDS_MODIFICATION_TIME);
+
+ dstSize = stat[0].size();
+ dstTime = stat[0].time();
+
+ fa = KFTPCore::Config::self()->uActions();
+ break;
+ }
+ case KFTPQueue::FXP: {
+ srcSize = stat[0].size();
+ srcTime = stat[0].time();
+
+ dstSize = stat[1].size();
+ dstTime = stat[1].time();
+
+ fa = KFTPCore::Config::self()->fActions();
+ break;
+ }
+ }
+
+ // Now that we have all data, get the action and do it
+ action = fa->getActionForSituation(srcSize, srcTime, dstSize, dstTime);
+ }
+
+ switch (action) {
+ default:
+ case FE_SKIP_ACT: event->action = FileExistsWakeupEvent::Skip; break;
+ case FE_OVERWRITE_ACT: event->action = FileExistsWakeupEvent::Overwrite; break;
+ case FE_RESUME_ACT: event->action = FileExistsWakeupEvent::Resume; break;
+ case FE_RENAME_ACT:
+ case FE_USER_ACT: {
+ appendUserDialogRequest(new UserDialogRequest(transfer, srcSize, srcTime, dstSize, dstTime));
+
+ // Event shall be deferred
+ delete event;
+ event = 0;
+ }
+ }
+
+ return event;
+}
+
+void Manager::appendUserDialogRequest(UserDialogRequest *request)
+{
+ m_userDialogRequests.append(request);
+
+ if (m_userDialogRequests.count() == 1) {
+ processUserDialogRequest();
+ }
+}
+
+void Manager::processUserDialogRequest()
+{
+ UserDialogRequest *request = m_userDialogRequests.getFirst();
+ if (!request)
+ return;
+
+ FEAction action = getDefaultFileExistsAction();
+ FileExistsWakeupEvent *event = new FileExistsWakeupEvent();
+
+ if (action == FE_DISABLE_ACT || action == FE_USER_ACT) {
+ // A dialog really needs to be displayed
+ TransferFile *transfer = request->getTransfer();
+
+ QString newDestPath;
+ KIO::RenameDlg_Result r = KIO::open_RenameDlg(
+ i18n("File Exists"),
+ transfer->getSourceUrl().prettyURL(),
+ transfer->getDestUrl().prettyURL(),
+ (KIO::RenameDlg_Mode) (KIO::M_OVERWRITE | KIO::M_RESUME | KIO::M_SKIP | KIO::M_MULTI),
+ newDestPath,
+ request->sourceSize(),
+ request->destinationSize(),
+ request->sourceTime(),
+ request->destinationTime()
+ );
+
+ switch (r) {
+ case KIO::R_RENAME: {
+ KURL url = transfer->getDestUrl();
+ url.setPath(newDestPath);
+ transfer->setDestUrl(url);
+
+ event->action = FileExistsWakeupEvent::Rename;
+ event->newFileName = newDestPath;
+ break;
+ }
+ case KIO::R_CANCEL: {
+ // Abort queue processing
+ abort();
+ transfer->abort();
+
+ // An event is not required, since we will not be recalling the process
+ delete event;
+ event = 0;
+ break;
+ }
+ case KIO::R_AUTO_SKIP: setDefaultFileExistsAction(FE_SKIP_ACT);
+ case KIO::R_SKIP: event->action = FileExistsWakeupEvent::Skip; break;
+ case KIO::R_RESUME_ALL: setDefaultFileExistsAction(FE_RESUME_ACT);
+ case KIO::R_RESUME: event->action = FileExistsWakeupEvent::Resume; break;
+ case KIO::R_OVERWRITE_ALL: setDefaultFileExistsAction(FE_OVERWRITE_ACT);
+ default: event->action = FileExistsWakeupEvent::Overwrite; break;
+ }
+ } else {
+ switch (action) {
+ default:
+ case FE_SKIP_ACT: event->action = FileExistsWakeupEvent::Skip; break;
+ case FE_OVERWRITE_ACT: event->action = FileExistsWakeupEvent::Overwrite; break;
+ case FE_RESUME_ACT: event->action = FileExistsWakeupEvent::Resume; break;
+ }
+ }
+
+ // Send a response to this request
+ request->sendResponse(event);
+
+ m_userDialogRequests.removeFirst();
+
+ if (!m_userDialogRequests.isEmpty())
+ processUserDialogRequest();
+}
+
+void Manager::openAfterTransfer(TransferFile *transfer)
+{
+ QString mimeType = KMimeType::findByURL(transfer->getDestUrl(), 0, true, true)->name();
+ KService::Ptr offer = KServiceTypeProfile::preferredService(mimeType, "Application");
+
+ if (!offer) {
+ KOpenWithDlg dialog(KURL::List(transfer->getDestUrl()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ offer = dialog.service();
+
+ if (!offer)
+ offer = new KService("", dialog.text(), "");
+ } else {
+ return;
+ }
+ }
+
+ QStringList params = KRun::processDesktopExec(*offer, KURL::List(transfer->getDestUrl()), false);
+ KProcess *p = new KProcess(this);
+ *p << params;
+
+ connect(p, SIGNAL(processExited(KProcess*)), this, SLOT(slotEditProcessTerminated(KProcess*)));
+
+ p->start();
+
+ // Save the process
+ m_editProcessList.insert(p->pid(), OpenedFile(transfer));
+}
+
+void Manager::slotEditProcessTerminated(KProcess *p)
+{
+ // A process has terminated, we should reupload
+ OpenedFile file = m_editProcessList[p->pid()];
+
+ // Only upload a file if it has been changed
+ if (file.hasChanged()) {
+ TransferFile *transfer = new TransferFile(KFTPQueue::Manager::self());
+ transfer->setSourceUrl(file.destination());
+ transfer->setDestUrl(file.source());
+ transfer->setTransferType(KFTPQueue::Upload);
+ transfer->addSize(KFileItem(KFileItem::Unknown, KFileItem::Unknown, file.destination()).size());
+ insertTransfer(transfer);
+
+ // Execute the transfer
+ transfer->delayedExecute();
+ }
+
+ // Cleanup
+ m_editProcessList.remove(p->pid());
+ p->deleteLater();
+}
+
+}
+
+#include "kftpqueue.moc"
diff --git a/kftpgrabber/src/kftpqueue.h b/kftpgrabber/src/kftpqueue.h
new file mode 100644
index 0000000..d20d817
--- /dev/null
+++ b/kftpgrabber/src/kftpqueue.h
@@ -0,0 +1,531 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUE_H
+#define KFTPQUEUE_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qtimer.h>
+#include <qcache.h>
+#include <qmap.h>
+#include <qpair.h>
+
+#include <kurl.h>
+#include <kurldrag.h>
+#include <kprocess.h>
+
+#include "kftpqueueprocessor.h"
+#include "kftpqueueconverter.h"
+#include "kftpfileexistsactions.h"
+#include "misc.h"
+
+#include "engine/directorylisting.h"
+#include "engine/event.h"
+
+#include "directoryscanner.h"
+#include "kftptransfer.h"
+#include "kftptransferfile.h"
+#include "kftptransferdir.h"
+#include "site.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPSession {
+ class Session;
+ class Connection;
+}
+
+class KFTPQueueConverter;
+
+typedef QPtrList<KFTPQueue::Transfer> KFTPQueueTransfers;
+
+namespace KFTPQueue {
+
+class FailedTransfer;
+
+/**
+ * This class represents an opened remote file. The file is stored locally
+ * while its being displayed to the user.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class OpenedFile {
+public:
+ OpenedFile() {}
+
+ /**
+ * Creates a new OpenedFile object.
+ *
+ * @param transfer The transfer used to transfer the file
+ */
+ OpenedFile(TransferFile *transfer);
+
+ /**
+ * Get file's source (remote).
+ *
+ * @return File's remote source URL
+ */
+ KURL source() { return m_source; }
+
+ /**
+ * Get file's destination (local).
+ *
+ * @return File's local destination URL
+ */
+ KURL destination() { return m_dest; }
+
+ /**
+ * Has the file changed since the transfer ?
+ *
+ * @return True if the file has been changed since being transfered
+ */
+ bool hasChanged();
+private:
+ KURL m_source;
+ KURL m_dest;
+
+ QString m_hash;
+};
+
+/**
+ * This class represents a request for "file already exists" dialog
+ * display request.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class UserDialogRequest {
+public:
+ /**
+ * Class constructor.
+ */
+ UserDialogRequest(TransferFile *transfer, filesize_t srcSize, time_t srcTime,
+ filesize_t dstSize, time_t dstTime);
+
+ /**
+ * Sends a response to this request.
+ *
+ * @param event A valid file exists wakeup event
+ */
+ void sendResponse(KFTPEngine::FileExistsWakeupEvent *event);
+
+ /**
+ * Returns the transfer that initiated the request.
+ */
+ TransferFile *getTransfer() const { return m_transfer; }
+
+ /**
+ * Returns source file size.
+ */
+ filesize_t sourceSize() const { return m_srcSize; }
+
+ /**
+ * Returns source file time.
+ */
+ time_t sourceTime() const { return m_srcTime; }
+
+ /**
+ * Returns destination file size.
+ */
+ filesize_t destinationSize() const { return m_dstSize; }
+
+ /**
+ * Returns destination file time.
+ */
+ time_t destinationTime() const { return m_dstTime; }
+private:
+ TransferFile *m_transfer;
+ filesize_t m_srcSize;
+ time_t m_srcTime;
+ filesize_t m_dstSize;
+ time_t m_dstTime;
+};
+
+/**
+ * This class is responsible for managing the complete queue hierarchy. All
+ * queued items descend from QueueObject and are contained in a simple tree
+ * model. Statistics and abort requests propagate from bottom to top and exec
+ * requests go in the other direction.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class Manager : public QObject {
+Q_OBJECT
+friend class KFTPSession::Session;
+friend class KFTPSession::Connection;
+friend class ::KFTPQueueConverter;
+friend class DirectoryScanner::ScannerThread;
+friend class KFTPQueue::FailedTransfer;
+public:
+ /**
+ * Returns the global manager instance.
+ */
+ static Manager *self();
+
+ /**
+ * Class destructor.
+ */
+ ~Manager();
+
+ /**
+ * Stop all queued transfers.
+ */
+ void stopAllTransfers();
+
+ /**
+ * Get the toplevel queue object. The direct children of this object are different
+ * KFTPQueue::Site objects that represent separate sites.
+ *
+ * @return A QueueObject representing the toplevel object
+ */
+ QueueObject *topLevelObject() const { return m_topLevel; }
+
+ /**
+ * Queues a new transfer by looking at the URL drag data.
+ *
+ * @param drag The drag data
+ */
+ void insertTransfer(KURLDrag *drag);
+
+ /**
+ * Queues a new transfer. This method will create the site if one doesn't exist yet
+ * for this transfer. The object will be reparented under the assigned site.
+ *
+ * @param transfer The transfer to be queued
+ */
+ void insertTransfer(Transfer *transfer);
+
+ /**
+ * Remove a transfer from the queue. The faceDestruction method will be called on the
+ * transfer object before removal. After calling this method, you shouldn't use the
+ * object anymore!
+ *
+ * @param transfer The transfer to be removed from queue
+ * @param abortSession If true any session that this transfer is using is aborted
+ */
+ void removeTransfer(Transfer *transfer, bool abortSession = true);
+
+ /**
+ * This method removes all the transfers from the queue.
+ */
+ void clearQueue();
+
+ /**
+ * Check if the transfer is under the correct site and move it if not.
+ *
+ * @param transfer The transfer to check
+ */
+ void revalidateTransfer(Transfer *transfer);
+
+ /**
+ * Finds a transfer by its id.
+ *
+ * @param id The transfer's id
+ * @return The transfer object
+ */
+ Transfer *findTransfer(long id);
+
+ /**
+ * Finds a site by its URL.
+ *
+ * @param url The site's URL
+ * @param noCreate If set to true the site will not be created when not found
+ * and NULL will be returned
+ * @return The site object
+ */
+ Site *findSite(KURL url, bool noCreate = false);
+
+ /**
+ * Remove a failed transfer from the list.
+ *
+ * @param transfer The failed transfer object to be removed
+ */
+ void removeFailedTransfer(FailedTransfer *transfer);
+
+ /**
+ * Remove all failed transfers from the list. This method actually calls the
+ * removeFailedTransfer for every failed transfer present.
+ */
+ void clearFailedTransferList();
+
+ /**
+ * Moves the specified transfer up in the queue.
+ *
+ * @param object The queue object to be moved
+ */
+ void moveTransferUp(QueueObject *object);
+
+ /**
+ * Moves the specified transfer down in the queue.
+ *
+ * @param object The queue object to be moved
+ */
+ void moveTransferDown(QueueObject *object);
+
+ /**
+ * Moves the specified transfer to the top of the queue (only within the
+ * parent boundaries).
+ *
+ * @param object The queue object to be moved
+ */
+ void moveTransferTop(QueueObject *object);
+
+ /**
+ * Moves the specified transfer to the bottom of the queue (only within the
+ * parent boundaries).
+ *
+ * @param object The queue object to be moved
+ */
+ void moveTransferBottom(QueueObject *object);
+
+ /**
+ * Can the transfer be moved up ?
+ *
+ * @param object The queue object to be moved
+ * @return True if the transfer can be moved
+ */
+ bool canBeMovedUp(QueueObject *object);
+
+ /**
+ * Can the transfer be moved down ?
+ *
+ * @param object The queue object to be moved
+ * @return True if the transfer can be moved
+ */
+ bool canBeMovedDown(QueueObject *object);
+
+ /**
+ * Returns the list of failed transfers.
+ *
+ * @return The QPtrList of FailedTransfer objects
+ */
+ QPtrList<KFTPQueue::FailedTransfer> *getFailedTransferList() { return &m_failedTransfers; }
+
+ /**
+ * Return the queue converter (exporter).
+ *
+ * @return the KFTPQueueConverter object
+ */
+ KFTPQueueConverter *getConverter() const { return m_converter; }
+
+ /**
+ * Opens the file with the registred application for it's MIME type and waits
+ * for the process to exit (then it will reupload the file if it has changed).
+ *
+ * @param transfer The transfer whose destination should be opened
+ */
+ void openAfterTransfer(TransferFile *transfer);
+
+ /**
+ * Should the update() be emitted on changes ?
+ *
+ * @param value True if the value should be emitted, false otherwise
+ */
+ void setEmitUpdate(bool value) { m_emitUpdate = value; }
+
+ /**
+ * Does a global queue update and removes all transfers that have the "delete me"
+ * variable set.
+ */
+ void doEmitUpdate();
+
+ /**
+ * Get the current download speed.
+ *
+ * @return The current download speed
+ */
+ filesize_t getDownloadSpeed() const { return m_curDownSpeed; }
+
+ /**
+ * Get the current upload speed.
+ *
+ * @return The current upload speed
+ */
+ filesize_t getUploadSpeed() const { return m_curUpSpeed; }
+
+ /**
+ * Get the percentage of the queue's completion.
+ *
+ * @return The percentage of the queue's completion
+ */
+ int getTransferPercentage();
+
+ /**
+ * Get the number of currently running transfers.
+ *
+ * @param onlyDirs Should only directories be counted
+ * @return The number of currently running transfers
+ */
+ int getNumRunning(bool onlyDirs = false);
+
+ /**
+ * Get the number of currently running transfers under a specific
+ * site.
+ *
+ * @param url The remote URL
+ * @return The number of currently running transfers
+ */
+ int getNumRunning(const KURL &remoteUrl);
+
+ /**
+ * Start the queue processing.
+ */
+ void start();
+
+ /**
+ * Abort the queue processing.
+ */
+ void abort();
+
+ /**
+ * Is the queue being processed ?
+ *
+ * @return True if the queue is being processed, false otherwise
+ */
+ bool isProcessing() { return m_queueProc->isRunning(); }
+
+ /**
+ * Return the next available transfer id and reserve it.
+ *
+ * @return The next available transfer id
+ */
+ long nextTransferId() { return m_lastQID++; }
+
+ /**
+ * Set a default action to take when encountering an existing file situation. Note that
+ * the action set here will override any preconfigured user actions unless set to the
+ * value of FE_DISABLE_ACT.
+ *
+ * @param action The action to take
+ */
+ void setDefaultFileExistsAction(FEAction action = FE_DISABLE_ACT) { m_defaultFeAction = action; }
+
+ /**
+ * Get the default action preset for situations where a file already exists.
+ *
+ * @return A valid FEAction
+ */
+ FEAction getDefaultFileExistsAction() const { return m_defaultFeAction; }
+
+ /**
+ * Decides what to do with the existing file. It will return a valid wakeup event to
+ * dispatch. It will first consider the pre-configured "on file exists" action matrix.
+ *
+ * @param transfer The transfer object
+ * @param srcStat Source file information (if remote)
+ * @param dstStat Destination file information (if remote)
+ * @return A FileExistsWakeupEvent that will be sent to the engine
+ */
+ KFTPEngine::FileExistsWakeupEvent *fileExistsAction(KFTPQueue::TransferFile *transfer,
+ QValueList<KFTPEngine::DirectoryEntry> stat);
+
+ /**
+ * Spawn a new transfer.
+ *
+ * @param sourceUrl Source URL
+ * @param destinationUrl Destination URL
+ * @param size Filesize
+ * @param dir True if this transfer represents a directory
+ * @param ignoreSkip Ignore skiplist for this transfer
+ * @param insertToQueue Should the new transfer be queued
+ * @param parent Optional parent object
+ * @param noScan True if directory transfers shouldn't be scanned
+ * @return A valid KFTPQueue::Transfer instance
+ */
+ KFTPQueue::Transfer *spawnTransfer(KURL sourceUrl, KURL destinationUrl, filesize_t size, bool dir,
+ bool ignoreSkip = false, bool insertToQueue = true, QObject *parent = 0L, bool noScan = false);
+protected:
+ /**
+ * Global class instance.
+ */
+ static Manager *m_self;
+
+ /**
+ * Class constructor.
+ */
+ Manager();
+
+ /**
+ * Appends a new user dialog request.
+ *
+ * @param request Request instance
+ */
+ void appendUserDialogRequest(UserDialogRequest *request);
+
+ /**
+ * Processes the top user dialog request by opening the desired "file
+ * already exists" dialog.
+ */
+ void processUserDialogRequest();
+private:
+ QueueObject *m_topLevel;
+ QCache<QueueObject> m_queueObjectCache;
+
+ QMap<pid_t, OpenedFile> m_editProcessList;
+ QPtrList<KFTPQueue::FailedTransfer> m_failedTransfers;
+ KFTPQueueProcessor *m_queueProc;
+ KFTPQueueConverter *m_converter;
+
+ long m_lastQID;
+ bool m_emitUpdate;
+ bool m_processingQueue;
+
+ filesize_t m_curDownSpeed;
+ filesize_t m_curUpSpeed;
+
+ bool m_feDialogOpen;
+ FEAction m_defaultFeAction;
+ QPtrList<UserDialogRequest> m_userDialogRequests;
+private slots:
+ void slotQueueProcessingComplete();
+ void slotQueueProcessingAborted();
+
+ void slotEditProcessTerminated(KProcess *p);
+signals:
+ void newSite(KFTPQueue::Site*);
+ void newTransfer(KFTPQueue::Transfer*);
+
+ void transferRemoved(long);
+ void siteRemoved(long);
+
+ void queueUpdate();
+
+ void failedTransferNew(KFTPQueue::FailedTransfer*);
+ void failedTransferRemoved(long);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftpqueueconverter.cpp b/kftpgrabber/src/kftpqueueconverter.cpp
new file mode 100644
index 0000000..700ad2f
--- /dev/null
+++ b/kftpgrabber/src/kftpqueueconverter.cpp
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpqueueconverter.h"
+#include "kftpqueue.h"
+
+#include <qfile.h>
+#include <qobjectlist.h>
+
+#include <kfilterdev.h>
+
+KFTPQueueConverter::KFTPQueueConverter(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+}
+
+void KFTPQueueConverter::importQueue(const QString &filename)
+{
+ m_xml = QDomDocument("KFTPGrabberQueue");
+ KFTPQueue::Manager::self()->clearQueue();
+
+ // Load from file
+ QIODevice *file = KFilterDev::deviceForFile(filename);
+ m_xml.setContent(file);
+ file->close();
+ delete file;
+
+ // Parse XML and create KFTPQueueTransfers
+ QDomNode n = m_xml.documentElement().firstChild();
+ while (!n.isNull()) {
+ importNode(n);
+
+ n = n.nextSibling();
+ }
+
+ KFTPQueue::Manager::self()->doEmitUpdate();
+}
+
+void KFTPQueueConverter::exportQueue(const QString &filename)
+{
+ m_xml = QDomDocument("KFTPGrabberQueue");
+ m_xml.setContent(QString("<queue></queue>"));
+
+ // Go trough all KFTPQueueTransfers and generate XML
+ KFTPQueue::QueueObject *i;
+ QPtrList<KFTPQueue::QueueObject> sites = KFTPQueue::Manager::self()->topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ KFTPQueue::QueueObject *t;
+ QPtrList<KFTPQueue::QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next())
+ generateXML(static_cast<KFTPQueue::Transfer*>(t), m_xml.documentElement());
+ }
+
+ // Save to file
+ QIODevice *file = KFilterDev::deviceForFile(filename, "application/x-gzip");
+ if (!file->open(IO_WriteOnly)) {
+ qDebug("WARNING: Unable to open xml for writing!");
+ return;
+ }
+
+ QTextStream fileStream(file);
+ m_xml.save(fileStream, 2);
+ file->flush();
+ file->close();
+ delete file;
+}
+
+void KFTPQueueConverter::generateXML(KFTPQueue::Transfer *transfer, QDomNode parent)
+{
+ // Create the item
+ QDomElement item = m_xml.createElement("item");
+ parent.appendChild(item);
+
+ // Create text nodes
+ createTextNode("source", transfer->getSourceUrl().url(), item);
+ createTextNode("dest", transfer->getDestUrl().url(), item);
+ createTextNode("size", QString::number(transfer->getSize()), item);
+ createTextNode("type", transfer->isDir() ? "directory" : "file", item);
+
+ if (transfer->isDir() && transfer->children()) {
+ // Transfer has children, add them as well
+ QDomElement tag = m_xml.createElement("children");
+ item.appendChild(tag);
+
+ KFTPQueue::QueueObject *i;
+ QPtrList<KFTPQueue::QueueObject> list = transfer->getChildrenList();
+ for (i = list.first(); i; i = list.next()) {
+ generateXML(static_cast<KFTPQueue::Transfer*>(i), tag);
+ }
+ }
+}
+
+void KFTPQueueConverter::importNode(QDomNode node, QObject *parent)
+{
+ // Get node data
+ KURL srcUrl = KURL(getTextNode("source", node));
+ KURL dstUrl = KURL(getTextNode("dest", node));
+ filesize_t size = getTextNode("size", node).toULongLong();
+ bool dir = getTextNode("type", node) == "directory";
+
+ KFTPQueue::TransferType transType = KFTPQueue::Download;
+
+ if (srcUrl.isLocalFile() && !dstUrl.isLocalFile()) {
+ transType = KFTPQueue::Upload;
+ } else if (!srcUrl.isLocalFile() && dstUrl.isLocalFile()) {
+ transType = KFTPQueue::Download;
+ } else if (!srcUrl.isLocalFile() && !dstUrl.isLocalFile()) {
+ transType = KFTPQueue::FXP;
+ }
+
+ // Create new transfer
+ if (!parent)
+ parent = KFTPQueue::Manager::self()->topLevelObject();
+
+ KFTPQueue::Transfer *transfer = 0L;
+ if (dir)
+ transfer = new KFTPQueue::TransferDir(parent);
+ else
+ transfer = new KFTPQueue::TransferFile(parent);
+
+ transfer->setSourceUrl(srcUrl);
+ transfer->setDestUrl(dstUrl);
+ transfer->addSize(dir ? 0 : size);
+ transfer->setTransferType(transType);
+
+ if (parent == KFTPQueue::Manager::self()->topLevelObject()) {
+ KFTPQueue::Manager::self()->insertTransfer(transfer);
+ } else {
+ transfer->setId(KFTPQueue::Manager::self()->m_lastQID++);
+ emit KFTPQueue::Manager::self()->newTransfer(transfer);
+ }
+
+ QDomNodeList tagNodes = node.toElement().elementsByTagName("children");
+ if (dir && tagNodes.length() > 0) {
+ // Import all child nodes
+ QDomNode n = node.firstChild();
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "children") {
+ n = n.firstChild();
+ break;
+ }
+
+ n = n.nextSibling();
+ }
+
+ while (!n.isNull()) {
+ importNode(n, transfer);
+
+ n = n.nextSibling();
+ }
+ }
+}
+
+void KFTPQueueConverter::createTextNode(const QString &name, const QString &value, QDomNode parent)
+{
+ QDomElement tag = m_xml.createElement(name);
+ parent.appendChild(tag);
+
+ QDomText textNode = m_xml.createTextNode(value);
+ tag.appendChild(textNode);
+}
+
+QString KFTPQueueConverter::getTextNode(const QString &name, QDomNode parent)
+{
+ QDomNodeList tagNodes = parent.toElement().elementsByTagName(name);
+
+ if (tagNodes.length() > 0) {
+ QString prop = tagNodes.item(0).toElement().text();
+ prop.stripWhiteSpace();
+
+ return prop;
+ } else {
+ return QString::null;
+ }
+}
+
+#include "kftpqueueconverter.moc"
diff --git a/kftpgrabber/src/kftpqueueconverter.h b/kftpgrabber/src/kftpqueueconverter.h
new file mode 100644
index 0000000..345439f
--- /dev/null
+++ b/kftpgrabber/src/kftpqueueconverter.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUECONVERTER_H
+#define KFTPQUEUECONVERTER_H
+
+#include <qobject.h>
+#include <qdom.h>
+
+namespace KFTPQueue {
+ class Transfer;
+}
+
+/**
+This class provides queue export/import to XML files.
+
+@author Jernej Kos
+*/
+class KFTPQueueConverter : public QObject
+{
+Q_OBJECT
+public:
+ KFTPQueueConverter(QObject *parent = 0, const char *name = 0);
+
+ /**
+ * Import queue from XML file. When called, this function will create
+ * new KFTPQueueTransfers.
+ *
+ * @param filename XML file that contains the queue
+ */
+ void importQueue(const QString &filename);
+
+ /**
+ * Export queue to XML file. It will take all current KFTPQueueTransfers
+ * and convert their properties to XML format.
+ *
+ * @param filename File where queue will be exported
+ */
+ void exportQueue(const QString &filename);
+private:
+ QDomDocument m_xml;
+
+ void generateXML(KFTPQueue::Transfer *transfer, QDomNode parent);
+ void createTextNode(const QString &name, const QString &value, QDomNode parent);
+
+ void importNode(QDomNode node, QObject *parent = 0);
+ QString getTextNode(const QString &name, QDomNode parent);
+};
+
+#endif
diff --git a/kftpgrabber/src/kftpqueueprocessor.cpp b/kftpgrabber/src/kftpqueueprocessor.cpp
new file mode 100644
index 0000000..f6051a2
--- /dev/null
+++ b/kftpgrabber/src/kftpqueueprocessor.cpp
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpqueueprocessor.h"
+#include "kftpqueue.h"
+#include "kftpsession.h"
+#include "misc.h"
+
+using namespace KFTPGrabberBase;
+
+KFTPQueueProcessor::KFTPQueueProcessor(QObject *parent)
+ : QObject(parent)
+{
+ m_running = false;
+}
+
+bool KFTPQueueProcessor::isRunning()
+{
+ return m_running;
+}
+
+void KFTPQueueProcessor::processActiveSite()
+{
+ if (!m_activeSite)
+ return;
+
+ // Start the transfer
+ m_activeSite->delayedExecute();
+}
+
+bool KFTPQueueProcessor::nextSite()
+{
+ // Select the first server (should be next) if there is none, we are done :)
+ //KFTPQueueTransfers *transfers = FTPQueueManager->getQueueTransferList();
+ QPtrList<KFTPQueue::QueueObject> sites = KFTPQueue::Manager::self()->topLevelObject()->getChildrenList();
+
+ if (sites.count() > 0 && m_running) {
+ if (m_activeSite && m_activeSite->getId() == sites.at(0)->getId()) {
+ // Because this function can be called from slotSiteComplete and signal gets
+ // emitted *before* site is removed we should use the second site on the list
+ m_activeSite = static_cast<KFTPQueue::Site*>(sites.at(1));
+ } else {
+ m_activeSite = static_cast<KFTPQueue::Site*>(sites.at(0));
+ }
+ } else {
+ m_activeSite = 0L;
+ }
+
+ if (m_activeSite) {
+ // Connect the signals
+ connect(m_activeSite, SIGNAL(destroyed(QObject*)), this, SLOT(slotSiteComplete()));
+ connect(m_activeSite, SIGNAL(siteAborted()), this, SLOT(slotSiteAborted()));
+
+ return true;
+ } else {
+ // We are done, so we emit the proper signal
+ m_running = false;
+
+ emit queueComplete();
+ return false;
+ }
+
+ return false;
+}
+
+void KFTPQueueProcessor::startProcessing()
+{
+ m_running = true;
+
+ // Select the site and process it
+ if (nextSite())
+ processActiveSite();
+}
+
+void KFTPQueueProcessor::stopProcessing()
+{
+ // Stop the queue processing
+ m_running = false;
+
+ // Abort current transfer
+ if (m_activeSite) {
+ // Disconnect signals
+ m_activeSite->QObject::disconnect(this);
+ m_activeSite->abort();
+ }
+
+ m_activeSite = 0L;
+ emit queueAborted();
+}
+
+void KFTPQueueProcessor::slotSiteComplete()
+{
+ // Transfer is complete, move to the next
+ if (nextSite())
+ processActiveSite();
+}
+
+void KFTPQueueProcessor::slotSiteAborted()
+{
+ // Transfer has aborted, we should stop
+ stopProcessing();
+}
+
+#include "kftpqueueprocessor.moc"
+
diff --git a/kftpgrabber/src/kftpqueueprocessor.h b/kftpgrabber/src/kftpqueueprocessor.h
new file mode 100644
index 0000000..90de1fd
--- /dev/null
+++ b/kftpgrabber/src/kftpqueueprocessor.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEPROCESSOR_H
+#define KFTPQUEUEPROCESSOR_H
+
+#include <qobject.h>
+#include <qthread.h>
+#include <qapplication.h>
+#include <qguardedptr.h>
+
+namespace KFTPQueue {
+ class Site;
+}
+
+/**
+@author Jernej Kos
+*/
+class KFTPQueueProcessor : public QObject
+{
+friend class KFTPQueueManager;
+Q_OBJECT
+public:
+ KFTPQueueProcessor(QObject *parent);
+
+ void startProcessing();
+ void stopProcessing();
+
+ bool isRunning();
+private:
+ QGuardedPtr<KFTPQueue::Site> m_activeSite;
+ bool m_running;
+
+ void processActiveSite();
+ bool nextSite();
+private slots:
+ void slotSiteComplete();
+ void slotSiteAborted();
+signals:
+ void queueComplete();
+ void queueAborted();
+};
+
+#endif
diff --git a/kftpgrabber/src/kftpsession.cpp b/kftpgrabber/src/kftpsession.cpp
new file mode 100644
index 0000000..126eb3e
--- /dev/null
+++ b/kftpgrabber/src/kftpsession.cpp
@@ -0,0 +1,920 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpsession.h"
+#include "kftpapi.h"
+#include "browser/detailsview.h"
+#include "browser/treeview.h"
+#include "browser/view.h"
+#include "kftpbookmarks.h"
+#include "misc.h"
+#include "widgets/systemtray.h"
+#include "mainactions.h"
+
+#include "misc/config.h"
+#include "misc/filter.h"
+
+#include <qdir.h>
+#include <qobjectlist.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpassdlg.h>
+#include <kstaticdeleter.h>
+
+using namespace KFTPGrabberBase;
+using namespace KFTPEngine;
+using namespace KFTPCore::Filter;
+
+namespace KFTPSession {
+
+//////////////////////////////////////////////////////////////////
+//////////////////////// Connection ///////////////////////
+//////////////////////////////////////////////////////////////////
+
+Connection::Connection(Session *session, bool primary)
+ : QObject(session),
+ m_primary(primary),
+ m_busy(false),
+ m_aborting(false),
+ m_scanning(false)
+{
+ // Create the actual connection client
+ m_client = new KFTPEngine::Thread();
+
+ connect(m_client->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+
+ // If this is not a core session connection, connect
+ if (!primary) {
+ // Connect to the server
+ KURL url = session->getClient()->socket()->getCurrentUrl();
+
+ KFTPBookmarks::Manager::self()->setupClient(session->getSite(), m_client);
+ m_client->connect(url);
+ }
+}
+
+Connection::~Connection()
+{
+ delete m_client;
+}
+
+bool Connection::isConnected()
+{
+ return !static_cast<Session*>(parent())->isRemote() || m_client->socket()->isConnected();
+}
+
+void Connection::acquire(KFTPQueue::Transfer *transfer)
+{
+ if (m_busy || !static_cast<Session*>(parent())->isRemote())
+ return;
+
+ m_curTransfer = transfer;
+ m_busy = true;
+
+ connect(transfer, SIGNAL(transferComplete(long)), this, SLOT(slotTransferCompleted()));
+ connect(transfer, SIGNAL(transferAbort(long)), this, SLOT(slotTransferCompleted()));
+
+ emit connectionAcquired();
+}
+
+void Connection::remove()
+{
+ // Disconnect all signals from the transfer
+ if (m_curTransfer)
+ m_curTransfer->QObject::disconnect(this);
+
+ m_curTransfer = 0L;
+ m_busy = false;
+
+ emit connectionRemoved();
+ emit static_cast<Session*>(parent())->freeConnectionAvailable();
+}
+
+void Connection::abort()
+{
+ if (m_aborting || !m_client->socket()->isBusy())
+ return;
+
+ // Emit the signal before aborting
+ emit aborting();
+
+ // Abort transfer
+ m_aborting = true;
+ m_client->abort();
+ m_aborting = false;
+}
+
+void Connection::scanDirectory(KFTPQueue::Transfer *parent)
+{
+ // Lock the connection and the transfer
+ acquire(parent);
+ parent->lock();
+
+ m_scanning = true;
+
+ if (isConnected())
+ m_client->scan(parent->getSourceUrl());
+}
+
+void Connection::addScannedDirectory(KFTPEngine::DirectoryTree *tree, KFTPQueue::Transfer *parent)
+{
+ // Directories
+ DirectoryTree::DirIterator dirEnd = tree->directories()->end();
+ for (DirectoryTree::DirIterator i = tree->directories()->begin(); i != dirEnd; i++) {
+ KURL sourceUrlBase = parent->getSourceUrl();
+ KURL destUrlBase = parent->getDestUrl();
+
+ sourceUrlBase.addPath((*i)->info().filename());
+ destUrlBase.addPath((*i)->info().filename());
+
+ // Check if we should skip this entry
+ const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, 0, true);
+
+ if (actionChain && actionChain->getAction(Action::Skip))
+ continue;
+
+ // Add directory transfer
+ KFTPQueue::TransferDir *transfer = new KFTPQueue::TransferDir(parent);
+ transfer->setSourceUrl(sourceUrlBase);
+ transfer->setDestUrl(destUrlBase);
+ transfer->setTransferType(parent->getTransferType());
+ transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
+
+ emit KFTPQueue::Manager::self()->newTransfer(transfer);
+
+ addScannedDirectory(*i, transfer);
+
+ if (KFTPCore::Config::skipEmptyDirs() && !transfer->hasChildren())
+ KFTPQueue::Manager::self()->removeTransfer(transfer, false);
+ }
+
+ // Files
+ DirectoryTree::FileIterator fileEnd = tree->files()->end();
+ for (DirectoryTree::FileIterator i = tree->files()->begin(); i != fileEnd; i++) {
+ KURL sourceUrlBase = parent->getSourceUrl();
+ KURL destUrlBase = parent->getDestUrl();
+
+ sourceUrlBase.addPath((*i).filename());
+ destUrlBase.addPath((*i).filename());
+
+ // Check if we should skip this entry
+ const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, (*i).size(), false);
+
+ if (actionChain && actionChain->getAction(Action::Skip))
+ continue;
+
+ // Add file transfer
+ KFTPQueue::TransferFile *transfer = new KFTPQueue::TransferFile(parent);
+ transfer->addSize((*i).size());
+ transfer->setSourceUrl(sourceUrlBase);
+ transfer->setDestUrl(destUrlBase);
+ transfer->setTransferType(parent->getTransferType());
+ transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
+
+ emit KFTPQueue::Manager::self()->newTransfer(transfer);
+ }
+}
+
+void Connection::slotEngineEvent(KFTPEngine::Event *event)
+{
+ switch (event->type()) {
+ case Event::EventDisconnect: {
+ emit connectionLost(this);
+ break;
+ }
+ case Event::EventConnect: {
+ emit connectionEstablished();
+
+ if (m_scanning) {
+ // Connected successfully, let's scan
+ m_client->scan(m_curTransfer->getSourceUrl());
+ }
+ break;
+ }
+ case Event::EventError: {
+ ErrorCode error = event->getParameter(0).asErrorCode();
+
+ if (m_scanning && (error == ConnectFailed || error == LoginFailed || error == OperationFailed)) {
+ // Scanning should be aborted, since there was an error
+ m_scanning = false;
+ m_curTransfer->unlock();
+ remove();
+
+ emit static_cast<Session*>(parent())->dirScanDone();
+ }
+ break;
+ }
+ case Event::EventScanComplete: {
+ if (m_scanning) {
+ // We have the listing
+ DirectoryTree *tree = static_cast<DirectoryTree*>(event->getParameter(0).asData());
+ addScannedDirectory(tree, m_curTransfer);
+ delete tree;
+
+ m_scanning = false;
+ m_curTransfer->unlock();
+ remove();
+
+ emit static_cast<Session*>(parent())->dirScanDone();
+ }
+ break;
+ }
+ default: break;
+ }
+}
+
+void Connection::slotTransferCompleted()
+{
+ // Remove the lock
+ remove();
+}
+
+void Connection::reconnect()
+{
+ if (!m_client->socket()->isConnected()) {
+ KFTPBookmarks::Manager::self()->setupClient(static_cast<Session*>(parent())->getSite(), m_client);
+ m_client->connect(m_client->socket()->getCurrentUrl());
+ }
+}
+
+////////////////////////////////////////////////////////
+//////////////////// Session ////////////////////
+////////////////////////////////////////////////////////
+
+Session::Session(Side side)
+ : QObject(),
+ m_side(side),
+ m_remote(false),
+ m_aborting(false),
+ m_registred(false),
+ m_site(0)
+{
+ // Register this session
+ Manager::self()->registerSession(this);
+}
+
+Session::~Session()
+{
+}
+
+KFTPEngine::Thread *Session::getClient()
+{
+ // Return the first (core) connection's client
+ return m_connections.at(0)->getClient();
+}
+
+bool Session::isConnected()
+{
+ // If there are no connections, just check if the session is remote
+ if (m_connections.count() == 0)
+ return !m_remote;
+
+ return m_connections.at(0)->isConnected();
+}
+
+void Session::slotClientEngineEvent(KFTPEngine::Event *event)
+{
+ switch (event->type()) {
+ case Event::EventConnect: {
+ // ***************************************************************************
+ // ****************************** EventConnect *******************************
+ // ***************************************************************************
+ m_remote = true;
+ m_aborting = false;
+ m_lastUrl = getClient()->socket()->getCurrentUrl();
+
+ QString siteName;
+ if (m_site)
+ siteName = m_site->getAttribute("name");
+ else
+ siteName = m_lastUrl.host();
+
+ Manager::self()->getTabs(m_side)->changeTab(m_fileView, siteName);
+ Manager::self()->getStatTabs()->changeTab(m_log, i18n("Log (%1)").arg(siteName));
+ Manager::self()->getStatTabs()->showPage(m_log);
+ Manager::self()->doEmitUpdate();
+
+ KURL homeUrl = getClient()->socket()->getCurrentUrl();
+
+ if (m_site && !m_site->getProperty("defremotepath").isEmpty())
+ homeUrl.setPath(m_site->getProperty("defremotepath"));
+ else
+ homeUrl.setPath(getClient()->socket()->getDefaultDirectory());
+
+ m_fileView->setHomeUrl(homeUrl);
+ m_fileView->goHome();
+
+ Session *opposite = Manager::self()->getActive(oppositeSide(m_side));
+
+ if (m_site && !opposite->isRemote()) {
+ QString localPath = m_site->getProperty("deflocalpath");
+
+ if (!localPath.isEmpty())
+ opposite->getFileView()->openUrl(KURL(localPath));
+ }
+ break;
+ }
+ case Event::EventDisconnect: {
+ // ***************************************************************************
+ // **************************** EventDisconnect ******************************
+ // ***************************************************************************
+ m_remote = false;
+ m_aborting = false;
+
+ Manager::self()->getTabs(m_side)->changeTab(m_fileView, i18n("Local Session"));
+ Manager::self()->getStatTabs()->changeTab(m_log, "[" + i18n("Log") + "]");
+ Manager::self()->doEmitUpdate();
+
+ m_fileView->setHomeUrl(KURL(KFTPCore::Config::defLocalDir()));
+ m_fileView->goHome();
+ break;
+ }
+ case Event::EventCommand: m_log->ftpLog(1, event->getParameter(0).asString()); break;
+ case Event::EventMultiline: m_log->ftpLog(2, event->getParameter(0).asString()); break;
+ case Event::EventResponse: m_log->ftpLog(0, event->getParameter(0).asString()); break;
+ case Event::EventMessage: m_log->ftpLog(3, event->getParameter(0).asString()); break;
+ case Event::EventRetrySuccess: {
+ // ***************************************************************************
+ // ************************** EventRetrySuccess ******************************
+ // ***************************************************************************
+ if (KFTPCore::Config::showRetrySuccessBalloon()) {
+ KFTPWidgets::SystemTray::self()->showBalloon(i18n("Connection with %1 has been successfully established.").arg(getClient()->socket()->getCurrentUrl().host()));
+ }
+ break;
+ }
+ case Event::EventReloadNeeded: {
+ // We should only do refreshes if the queue is not being processed
+ if (KFTPQueue::Manager::self()->getNumRunning() == 0)
+ m_fileView->reload();
+ break;
+ }
+ case Event::EventPubkeyPassword: {
+ // A public-key authentication password was requested
+ QCString pass;
+ int ret = KPasswordDialog::getPassword(pass, i18n("Please provide your private key decryption password."));
+
+ if (ret == KPasswordDialog::Accepted) {
+ PubkeyWakeupEvent *event = new PubkeyWakeupEvent();
+ event->password = pass;
+
+ getClient()->wakeup(event);
+ } else {
+ getClient()->abort();
+ }
+ break;
+ }
+ default: break;
+ }
+}
+
+void Session::scanDirectory(KFTPQueue::Transfer *parent, Connection *connection)
+{
+ // Go trough all files in path and add them as transfers that have parent as their parent
+ // transfer
+ KURL path = parent->getSourceUrl();
+
+ if (path.isLocalFile()) {
+ connect(new DirectoryScanner(parent), SIGNAL(completed()), this, SIGNAL(dirScanDone()));
+ } else if (m_remote) {
+ if (!connection) {
+ if (!isFreeConnection()) {
+ emit dirScanDone();
+ return;
+ }
+
+ // Assign a new connection (it might be unconnected!)
+ connection = assignConnection();
+ }
+
+ connection->scanDirectory(parent);
+ }
+}
+
+void Session::abort()
+{
+ if (m_aborting)
+ return;
+
+ m_aborting = true;
+
+ emit aborting();
+
+ // Abort all connections
+ Connection *conn;
+ for (conn = m_connections.first(); conn; conn = m_connections.next()) {
+ conn->abort();
+ }
+
+ m_aborting = false;
+}
+
+void Session::reconnect(const KURL &url)
+{
+ // Set the reconnect url
+ m_reconnectUrl = url;
+
+ if (m_remote && getClient()->socket()->isConnected()) {
+ abort();
+
+ connect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
+ getClient()->disconnect();
+ } else {
+ // The session is already disconnected, just call the slot
+ slotStartReconnect();
+ }
+}
+
+void Session::slotStartReconnect()
+{
+ disconnect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
+
+ // Reconnect only if this is a remote url
+ if (!m_reconnectUrl.isLocalFile()) {
+ KFTPBookmarks::Manager::self()->setupClient(m_site, getClient());
+ getClient()->connect(m_reconnectUrl);
+ }
+
+ // Invalidate the url
+ m_reconnectUrl = KURL();
+}
+
+int Session::getMaxThreadCount()
+{
+ // First get the global thread count
+ int count = KFTPCore::Config::threadCount();
+
+ if (!KFTPCore::Config::threadUsePrimary())
+ count++;
+
+ // Try to see if threads are disabled for this site
+ if (count > 1 && isRemote()) {
+ if (m_site && m_site->getIntProperty("disableThreads"))
+ return 1;
+ }
+
+ return count;
+}
+
+bool Session::isFreeConnection()
+{
+ unsigned int max = getMaxThreadCount();
+ unsigned int free = 0;
+
+ if ((m_connections.count() < max && max > 1) || !isRemote())
+ return true;
+
+ Connection *conn;
+ for (conn = m_connections.first(); conn; conn = m_connections.next()) {
+ if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
+ free++;
+ }
+
+ return free > 0;
+}
+
+Connection *Session::assignConnection()
+{
+ unsigned int max = getMaxThreadCount();
+
+ if (m_connections.count() == 0) {
+ // We need a new core connection
+ Connection *conn = new Connection(this, true);
+ m_connections.append(conn);
+
+ Manager::self()->doEmitUpdate();
+
+ return conn;
+ } else {
+ // Find a free connection
+ Connection *conn;
+ for (conn = m_connections.first(); conn; conn = m_connections.next()) {
+ if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
+ return conn;
+ }
+
+ // No free connection has been found, but we may be able to create
+ // another (if we are within limits)
+ if (m_connections.count() < max) {
+ conn = new Connection(this);
+ m_connections.append(conn);
+
+ Manager::self()->doEmitUpdate();
+
+ return conn;
+ }
+ }
+
+ return 0;
+}
+
+void Session::disconnectAllConnections()
+{
+ // Abort any possible transfers first
+ abort();
+
+ // Now disconnect all connections
+ Connection *conn;
+
+ for (conn = m_connections.first(); conn; conn = m_connections.next()) {
+ if (conn->getClient()->socket()->isConnected()) {
+ conn->getClient()->disconnect();
+ }
+ }
+}
+
+////////////////////////////////////////////////////////
+/////////////////////// Manager ////////////////////////
+////////////////////////////////////////////////////////
+
+Manager *Manager::m_self = 0;
+
+Manager *Manager::self()
+{
+ return m_self;
+}
+
+Manager::Manager(QObject *parent, QTabWidget *stat, KFTPTabWidget *left, KFTPTabWidget *right)
+ : QObject(parent),
+ m_statTabs(stat),
+ m_leftTabs(left),
+ m_rightTabs(right),
+ m_active(0),
+ m_leftActive(0),
+ m_rightActive(0)
+{
+ Manager::m_self = this;
+
+ // Connect some signals
+ connect(left, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotActiveChanged(QWidget*)));
+ connect(right, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotActiveChanged(QWidget*)));
+
+ connect(left, SIGNAL(closeRequest(QWidget*)), this, SLOT(slotSessionCloseRequest(QWidget*)));
+ connect(right, SIGNAL(closeRequest(QWidget*)), this, SLOT(slotSessionCloseRequest(QWidget*)));
+}
+
+void Manager::registerSession(Session *session)
+{
+ m_active = session;
+
+ // Create some new stuff and assign it to the session
+ session->assignConnection();
+ session->m_fileView = new KFTPWidgets::Browser::View(0L, "", session->getClient(), session);
+ session->m_log = new KFTPWidgets::LogView();
+
+ // Install event filters
+ session->getFileView()->getDetailsView()->installEventFilter(this);
+ session->getFileView()->getTreeView()->installEventFilter(this);
+ session->getFileView()->m_toolBarFirst->installEventFilter(this);
+ session->getFileView()->m_toolBarSecond->installEventFilter(this);
+
+ connect(session->getFileView()->getDetailsView(), SIGNAL(clicked(QListViewItem*)), this, SLOT(slotSwitchFocus()));
+ connect(session->getFileView()->getTreeView(), SIGNAL(clicked(QListViewItem*)), this, SLOT(slotSwitchFocus()));
+ connect(session->getFileView()->m_toolBarFirst, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));
+ connect(session->getFileView()->m_toolBarSecond, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));
+
+ // Connect some signals
+ connect(session->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), session, SLOT(slotClientEngineEvent(KFTPEngine::Event*)));
+
+ // Assign GUI positions
+ m_statTabs->addTab(session->m_log, "[" + i18n("Log") + "]");
+ getTabs(session->m_side)->addTab(session->m_fileView, KFTPGrabberBase::loadSmallIcon("system"), i18n("Session"));
+
+ // Actually add the session
+ m_sessionList.append(session);
+ session->m_registred = true;
+}
+
+KFTPWidgets::Browser::View *Manager::getActiveView()
+{
+ if (m_active)
+ return m_active->getFileView();
+
+ return 0;
+}
+
+Session *Manager::getActiveSession()
+{
+ return m_active;
+}
+
+bool Manager::eventFilter(QObject *object, QEvent *event)
+{
+ if (event->type() == QEvent::FocusIn || event->type() == QEvent::MouseButtonPress) {
+ switchFocusToObject(object);
+ }
+
+ return false;
+}
+
+void Manager::slotSwitchFocus()
+{
+ switchFocusToObject(QObject::sender());
+}
+
+void Manager::switchFocusToObject(const QObject *object)
+{
+ if (!object)
+ return;
+
+ for (;;) {
+ if (object->isA("KFTPWidgets::Browser::View"))
+ break;
+
+ if (!(object = object->parent()))
+ break;
+ }
+
+ if (object) {
+ // We have the proper object
+ Session *session = find(static_cast<const KFTPWidgets::Browser::View*>(object));
+
+ if (session && session != m_active) {
+ m_active = session;
+
+ // Open the current session's log tab
+ if (session->isRemote())
+ m_statTabs->showPage(session->getLog());
+ }
+ }
+}
+
+void Manager::unregisterSession(Session *session)
+{
+ // Destroy all objects related to the session and remove it
+ getTabs(session->m_side)->removePage(session->m_fileView);
+ m_statTabs->removePage(session->m_log);
+
+ if (session->getClient()->socket()->isConnected()) {
+ session->abort();
+ session->getClient()->disconnect();
+ }
+
+ // Delete objects
+ session->m_fileView->deleteLater();
+ session->m_log->deleteLater();
+
+ // Actually remove the session
+ m_sessionList.remove(session);
+ delete session;
+
+ // Emit update
+ emit update();
+}
+
+void Manager::doEmitUpdate()
+{
+ emit update();
+}
+
+void Manager::disconnectAllSessions()
+{
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next())
+ i->disconnectAllConnections();
+}
+
+Session *Manager::find(KFTPEngine::Thread *client)
+{
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ if (i->getClient() == client)
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::find(KFTPWidgets::Browser::View *fileView)
+{
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ if (i->m_fileView == fileView)
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::find(KFTPWidgets::LogView *log)
+{
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ if (i->m_log == log)
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::find(const KURL &url, bool mustUnlock)
+{
+ if (url.isLocalFile())
+ return find(true);
+
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ KURL tmp = i->getClient()->socket()->getCurrentUrl();
+ tmp.setPath(url.path());
+
+ if (tmp == url && i->isRemote() && i->isConnected() && (!mustUnlock || i->isFreeConnection()))
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::find(bool local)
+{
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ if (i->m_remote != local)
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::findLast(const KURL &url, Side side)
+{
+ if (url.isLocalFile())
+ return find(true);
+
+ Session *i;
+
+ for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
+ KURL tmp = i->m_lastUrl;
+ tmp.setPath(url.path());
+
+ if (tmp == url && !i->isRemote() && (i->getSide() || side == IgnoreSide))
+ return i;
+ }
+
+ return 0L;
+}
+
+Session *Manager::spawnLocalSession(Side side, bool forceNew)
+{
+ // Creates a new local session
+ Session *session = 0L;
+
+ if (forceNew || (session = find(true)) == 0L || (session->m_side != side && side != IgnoreSide)) {
+ side = side == IgnoreSide ? LeftSide : side;
+
+ session = new Session(side);
+ session->m_remote = false;
+ getTabs(side)->changeTab(session->m_fileView, i18n("Local Session"));
+ getStatTabs()->changeTab(session->m_log, "[" + i18n("Log") + "]");
+ }
+
+ setActive(session);
+ return session;
+}
+
+Session *Manager::spawnRemoteSession(Side side, const KURL &remoteUrl, KFTPBookmarks::Site *site, bool mustUnlock)
+{
+ // Creates a new remote session and connects it to the correct server
+ Session *session;
+
+ if (remoteUrl.isLocalFile())
+ return spawnLocalSession(side);
+
+ if ((session = find(remoteUrl, mustUnlock)) == 0L || (session->m_side != side && side != IgnoreSide)) {
+ // Try to find the session that was last connected to this URL
+ if ((session = findLast(remoteUrl, side)) == 0L) {
+ // Attempt to reuse a local session if one exists one the right side
+ session = getActive(RightSide);
+
+ if (session->isRemote()) {
+ side = side == IgnoreSide ? RightSide : side;
+ session = new Session(side);
+ }
+ }
+
+ // Try to find the site by url if it is not set
+ if (!site)
+ site = KFTPBookmarks::Manager::self()->findSite(remoteUrl);
+
+ // Set properties
+ session->m_remote = true;
+ session->m_site = site;
+ m_active = session;
+
+ KFTPBookmarks::Manager::self()->setupClient(site, session->getClient());
+ session->getClient()->connect(remoteUrl);
+ }
+
+ return session;
+}
+
+void Manager::setActive(Session *session)
+{
+ // Make a session active on its own side ;)
+ Session *oldActive = getActive(session->m_side);
+
+ oldActive ? oldActive->m_active = false : 0;
+ session->m_active = true;
+
+ switch (session->m_side) {
+ case LeftSide: m_leftActive = session; break;
+ case RightSide: m_rightActive = session; break;
+ case IgnoreSide: qDebug("Invalid side specified!"); return;
+ }
+
+ // Refresh the GUI
+ getTabs(session->m_side)->showPage(session->m_fileView);
+}
+
+Session *Manager::getActive(Side side)
+{
+ switch (side) {
+ case LeftSide: return m_leftActive;
+ case RightSide: return m_rightActive;
+ case IgnoreSide: qDebug("Invalid side specified!"); break;
+ }
+
+ return NULL;
+}
+
+KFTPTabWidget *Manager::getTabs(Side side)
+{
+ switch (side) {
+ case LeftSide: return m_leftTabs;
+ case RightSide: return m_rightTabs;
+ case IgnoreSide: qDebug("Invalid side specified!"); break;
+ }
+
+ return NULL;
+}
+
+void Manager::slotActiveChanged(QWidget *page)
+{
+ Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));
+ setActive(session);
+}
+
+void Manager::slotSessionCloseRequest(QWidget *page)
+{
+ Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));
+
+ if (getTabs(session->m_side)->count() == 1) {
+ KMessageBox::error(0L, i18n("At least one session must remain open on each side."));
+ return;
+ }
+
+ if ((session->m_remote && session->getClient()->socket()->isBusy()) || !session->isFreeConnection()) {
+ KMessageBox::error(0L, i18n("Please finish all transfers before closing the session."));
+ return;
+ } else {
+ // Remove the session
+ if (session->getClient()->socket()->isConnected()) {
+ if (KMessageBox::questionYesNo(0L, i18n("This session is currently connected. Are you sure you wish to disconnect?"), i18n("Close Session")) == KMessageBox::No)
+ return;
+ }
+
+ unregisterSession(session);
+ }
+}
+
+}
+
+#include "kftpsession.moc"
diff --git a/kftpgrabber/src/kftpsession.h b/kftpgrabber/src/kftpsession.h
new file mode 100644
index 0000000..9e95213
--- /dev/null
+++ b/kftpgrabber/src/kftpsession.h
@@ -0,0 +1,616 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPSESSION_H
+#define KFTPSESSION_H
+
+#include <qobject.h>
+#include <qguardedptr.h>
+#include <qdom.h>
+
+#include <qlistview.h>
+
+#include "kftpqueue.h"
+#include "logview.h"
+#include "kftptabwidget.h"
+
+#include "engine/thread.h"
+
+namespace KFTPWidgets {
+ namespace Browser {
+ class Actions;
+ class View;
+ }
+}
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+namespace KFTPSession {
+
+class Session;
+
+enum Side {
+ LeftSide,
+ RightSide,
+ IgnoreSide
+};
+
+#define oppositeSide(x) (x == KFTPSession::LeftSide ? KFTPSession::RightSide : KFTPSession::LeftSide)
+
+/**
+ * The Connection class represents a session's connection to a ftp server. There
+ * can be many connections in the same session (to the same server), thus providing
+ * support for multiple threads at once.
+ *
+ * Individual transfers must acquire connections before they can use them. When a
+ * connection is acquired it cannot be used by another transfer or another remote
+ * operation.
+ *
+ * @author Jernej Kos
+ */
+class Connection : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param session The parent session
+ * @param primary Set to true if this is session's primary connection
+ */
+ Connection(Session *session, bool primary = false);
+
+ /**
+ * Class destructor.
+ */
+ ~Connection();
+
+ /**
+ * Returns the client thread for this connection.
+ *
+ * @return A KFTPClientThr object representing a client.
+ */
+ KFTPEngine::Thread *getClient() const { return m_client; }
+
+ /**
+ * Returns the URL this connection is connected to.
+ *
+ * @return A KURL this connection is connected to.
+ */
+ KURL getUrl() { return m_client->socket()->getCurrentUrl(); }
+
+ /**
+ * Returns the current transfer if this connection is locked by one. If
+ * it isn't locked NULL will be returned.
+ */
+ KFTPQueue::Transfer *getTransfer() const { return m_curTransfer; }
+
+ /**
+ * Lock this connection for a specific transfer. While the connection is
+ * locked no other transfer may use it. The connection will be automaticly
+ * unlocked when the transfer completes.
+ *
+ * @param transfer The transfer which is locking this connection.
+ */
+ void acquire(KFTPQueue::Transfer *transfer);
+
+ /**
+ * Clear existing connection lock. Only the transfer who locked the connection
+ * should do this!
+ */
+ void remove();
+
+ /**
+ * Abort any actions going via this connection. It will call abort on the
+ * underlying client and emit the aborting signal.
+ */
+ void abort();
+
+ /**
+ * Connect to the previously connected URL. If this connection is already
+ * established this method does nothing.
+ */
+ void reconnect();
+
+ /**
+ * Is the current connection locked by some transfer ?
+ *
+ * @return Returns true if the current connection is locked, false otherwise.
+ */
+ bool isBusy() const { return m_busy; }
+
+ /**
+ * Is the current connection actually connected to a server ?
+ *
+ * @return Returns true if a connection to a server is established.
+ */
+ bool isConnected();
+
+ /**
+ * Is the current connection the primary session connection ?
+ *
+ * @return Returns true if the current connection is primary, false otherwise
+ */
+ bool isPrimary() const { return m_primary; }
+
+ /**
+ * Scans a directory - usually called from KFTPSession for remote scans.
+ *
+ * @param parent The transfer that requested the scan
+ */
+ void scanDirectory(KFTPQueue::Transfer *parent);
+private:
+ void addScannedDirectory(KFTPEngine::DirectoryTree *tree, KFTPQueue::Transfer *parent);
+private:
+ bool m_primary;
+ bool m_busy;
+ bool m_aborting;
+ bool m_scanning;
+
+ QGuardedPtr<KFTPQueue::Transfer> m_curTransfer;
+ KFTPEngine::Thread *m_client;
+private slots:
+ void slotTransferCompleted();
+
+ void slotEngineEvent(KFTPEngine::Event *event);
+signals:
+ /**
+ * This signal gets emitted when the connection is acquired for exclusive
+ * use by a transfer.
+ */
+ void connectionAcquired();
+
+ /**
+ * This signal gets emitted when connection is returned to the pool and is
+ * no longer locked.
+ */
+ void connectionRemoved();
+
+ /**
+ * This signal gets emitted when the connection is lost.
+ *
+ * @param connection This connection instance
+ */
+ void connectionLost(KFTPSession::Connection *connection);
+
+ /**
+ * This signal gets emitted when connection with the remote server is
+ * established.
+ */
+ void connectionEstablished();
+
+ /**
+ * This signal gets emitted when this connection is in the process of
+ * aborting any currently running operations.
+ */
+ void aborting();
+};
+
+/**
+ * A Session instance connects all the relevant elements together. Via the
+ * manager this abstraction allows many independent sessions inside one
+ * applications.
+ *
+ * Each session can either be local or remote. A remote session can have
+ * multiple connections open (each connection is represented by a Connection
+ * instance).
+ *
+ * @author Jernej Kos
+ */
+class Session : public QObject
+{
+Q_OBJECT
+friend class KFTPWidgets::Browser::Actions;
+friend class Manager;
+friend class Connection;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param side The side the session should be located on
+ */
+ Session(Side side);
+
+ /**
+ * Class destructor.
+ */
+ ~Session();
+
+ /**
+ * Has the session registration procedure already been completed ?
+ *
+ * @return True if the registration procedure has been completed, false otherwise
+ */
+ bool isRegistred() const { return m_registred; }
+
+ /**
+ * Is this a remote session ?
+ *
+ * @return Returns true if this is a remote session.
+ */
+ bool isRemote() const { return m_remote; }
+
+ /**
+ * Is this session currently active (=visible to the user) ?
+ *
+ * @return Returns true if this session is currently active.
+ */
+ bool isActive() const { return m_active; }
+
+ /**
+ * Is this session currently connected to a server ? This actually checks if
+ * the primary connection is connected to a server.
+ *
+ * @return Returns true if this session is connected to a server.
+ */
+ bool isConnected();
+
+ /**
+ * Get the site in bookmarks this session is asociated with or NULL if there
+ * is no such site.
+ *
+ * @return Returns the asociated bookmarks site.
+ */
+ KFTPBookmarks::Site *getSite() const { return m_site; }
+
+ /**
+ * Get the session's client thread. This actually returns the client thread
+ * of the primary session's connection.
+ *
+ * @return A KFTPEngine::Thread object representing a client.
+ */
+ KFTPEngine::Thread *getClient();
+
+ /**
+ * Get this session's log widget.
+ *
+ * @return Returns the session's log widget.
+ */
+ KFTPWidgets::LogView *getLog() const { return m_log; }
+
+ /**
+ * Get this session's file view.
+ *
+ * @return Returns the session's file view.
+ */
+ KFTPWidgets::Browser::View *getFileView() const { return m_fileView; }
+
+ /**
+ * Get the side on which this session is located.
+ *
+ * @return Returns the session's side.
+ */
+ Side getSide() const { return m_side; }
+
+ /**
+ * Set bookmark site asociation.
+ *
+ * @param site A valid site to which this session is asociated.
+ */
+ void setSite(KFTPBookmarks::Site *site) { m_site = site; }
+
+ /**
+ * Are there any free connections (or if some new can be created) to lock ?
+ *
+ * @return Returns true if there is a connection that can be locked.
+ */
+ bool isFreeConnection();
+
+ /**
+ * Assigns a free connection if there is one. A connection can be created if
+ * the limit hasn't yet been reached. If there are no free connections this
+ * method returns NULL.
+ *
+ * @return A free Connection or NULL if there is none.
+ */
+ Connection *assignConnection();
+
+ /**
+ * Disconnects all connections for this session.
+ */
+ void disconnectAllConnections();
+
+ /**
+ * Get the list of current connections for this session.
+ *
+ * @return A list of current connections.
+ */
+ QPtrList<Connection> *getConnectionList() { return &m_connections; }
+
+ /**
+ * Reconnect to a new URL. The current connections will be droped and reconnected
+ * to the new URL.
+ *
+ * @param url The URL to connect to.
+ */
+ void reconnect(const KURL &url);
+
+ /**
+ * Abort this session. This will actually call abort on all connections for
+ * this session.
+ */
+ void abort();
+
+ /**
+ * Initiate a directory scan, adding all new files and directories under the
+ * transfer specified as parent. This method will change the current transfer's
+ * status to Locked and will return imediately. The actual scan will take
+ * place in a separate thread.
+ *
+ * @param parent The transfer which requested the scan
+ * @param connection An optional connection to use
+ */
+ void scanDirectory(KFTPQueue::Transfer *parent, Connection *connection = 0);
+private:
+ Side m_side;
+ bool m_remote;
+ bool m_active;
+ bool m_aborting;
+ bool m_registred;
+
+ // Session description
+ KFTPBookmarks::Site *m_site;
+ KFTPWidgets::LogView *m_log;
+ KFTPWidgets::Browser::View *m_fileView;
+ KURL m_lastUrl;
+ KURL m_reconnectUrl;
+
+ // Connection list
+ QPtrList<Connection> m_connections;
+
+ int getMaxThreadCount();
+private slots:
+ void slotClientEngineEvent(KFTPEngine::Event *event);
+ void slotStartReconnect();
+signals:
+ void dirScanDone();
+ void aborting();
+
+ void freeConnectionAvailable();
+};
+
+typedef QPtrList<Session> SessionList;
+
+/**
+ * The Manager class provides access to sessions, their registration and deletion.
+ *
+ * @author Jernej Kos
+ */
+class Manager : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Get a global manager instance.
+ */
+ static Manager *self();
+
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent object
+ * @param stat A widget that contains log tabs
+ * @param left A widget that contains sessions on the left side
+ * @param right A widget that contains sessions on the right side
+ */
+ Manager(QObject *parent, QTabWidget *stat, KFTPTabWidget *left, KFTPTabWidget *right);
+
+ /**
+ * Spawn a new local (=unconnected) session. This method may reuse an old local session.
+ *
+ * @param side The side on which the session should be created.
+ * @param forceNew Should a new session be created if a similar session already exists.
+ * @return Allways returns a valid session.
+ */
+ Session *spawnLocalSession(Side side, bool forceNew = false);
+
+ /**
+ * Spawn a new remote session. This method may reuse an old remote session. It may also
+ * spawn a new local session if the URL appears local.
+ *
+ * @param side The side on which the session should be created.
+ * @param remoteUrl URL to which the session should connect upon creation.
+ * @param site The bookmarked site the session is connecting to.
+ * @param mustUnlock Must the returned session be unlocked ?
+ * @return Allways returns a valid session.
+ */
+ Session *spawnRemoteSession(Side side, const KURL &remoteUrl, KFTPBookmarks::Site *site = 0, bool mustUnlock = false);
+
+ /**
+ * Register a new session with the session manager. Every session calls this method in
+ * its constructor to init the session - this method shouldn't be called otherwise.
+ *
+ * @param session A new session.
+ */
+ void registerSession(Session *session);
+
+ /**
+ * Destroy the session. All connections and transfers related to this session are
+ * aborted and disconnected first.
+ *
+ * @param session The session that is going to be destroyed.
+ */
+ void unregisterSession(Session *session);
+
+ /**
+ * Disconnects all sessions and their connections.
+ */
+ void disconnectAllSessions();
+
+ /**
+ * Find a session related to a client thread.
+ *
+ * @param client The client thread related to a session.
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *find(KFTPEngine::Thread *client);
+
+ /**
+ * Find a session related to a file view widget.
+ *
+ * @param fileView The file view widget related to a session.
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *find(KFTPWidgets::Browser::View *fileView);
+
+ /**
+ * Find a session related to a log widget.
+ *
+ * @param log The log widget related to a session.
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *find(KFTPWidgets::LogView *log);
+
+ /**
+ * Find a session by the url it is connected to.
+ *
+ * @param url The URL a session is connected to.
+ * @param mustUnlock Must the session be unlocked ?
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *find(const KURL &url, bool mustUnlock = false);
+
+ /**
+ * Finds a session by its state (remote/local).
+ *
+ * @param local Must a session be local ?
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *find(bool local);
+
+ /**
+ * Finds a session that was last connected to a specific URL that is placed
+ * on a specific side.
+ *
+ * @param url The URL to which the session was connected to.
+ * @param side The side where the session must be.
+ * @return Returns a valid session if one is found, NULL otherwise.
+ */
+ Session *findLast(const KURL &url, Side side);
+
+ /**
+ * Get the list of all sessions in existance.
+ *
+ * @return The session list.
+ */
+ SessionList *getSessionList() { return &m_sessionList; }
+
+ /**
+ * Emits the update signal.
+ */
+ void doEmitUpdate();
+
+ /**
+ * Returns the tab widget that holds the log widgets.
+ *
+ * @return Returns a QTabWidget that holds the log widgets.
+ */
+ QTabWidget *getStatTabs() const { return m_statTabs; }
+
+ /**
+ * Returns the tab widget that holds the sessions on a specific side.
+ *
+ * @param side The side of the tab widget.
+ * @return Returns a KFTPTabWidget that holds the sessions.
+ */
+ KFTPTabWidget *getTabs(Side side);
+
+ /**
+ * Make a session active (=visible to the user).
+ *
+ * @param session Session to be made active.
+ */
+ void setActive(Session *session);
+
+ /**
+ * Get the active session on a specific side.
+ *
+ * @param side The side where the session is active.
+ * @return Returns a valid session.
+ */
+ Session *getActive(Side side);
+
+ /**
+ * Get the currently active view.
+ *
+ * @return The active view instance
+ */
+ KFTPWidgets::Browser::View *getActiveView();
+
+ /**
+ * Get the currently active session.
+ */
+ Session *getActiveSession();
+protected:
+ static Manager *m_self;
+
+ /**
+ * Event filter handler.
+ */
+ bool eventFilter(QObject *object, QEvent *event);
+
+ /**
+ * Change the currently active session to the browser view inheriting
+ * the passed object.
+ *
+ * @param object The object that has browser view as some parent
+ */
+ void switchFocusToObject(const QObject *object);
+private:
+ SessionList m_sessionList;
+
+ // These variables should be assigned right after construction
+ QTabWidget *m_statTabs;
+ KFTPTabWidget *m_leftTabs;
+ KFTPTabWidget *m_rightTabs;
+
+ // Currently active sessions
+ Session *m_active;
+ Session *m_leftActive;
+ Session *m_rightActive;
+private slots:
+ void slotActiveChanged(QWidget *page);
+ void slotSwitchFocus();
+public slots:
+ void slotSessionCloseRequest(QWidget *page);
+signals:
+ void update();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftptransfer.cpp b/kftpgrabber/src/kftptransfer.cpp
new file mode 100644
index 0000000..3c127ba
--- /dev/null
+++ b/kftpgrabber/src/kftptransfer.cpp
@@ -0,0 +1,374 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftptransfer.h"
+#include "kftptransferdir.h"
+#include "widgets/systemtray.h"
+#include "kftpqueue.h"
+#include "kftpsession.h"
+
+#include "misc/config.h"
+
+#include <klocale.h>
+
+using namespace KFTPSession;
+
+namespace KFTPQueue {
+
+FailedTransfer::FailedTransfer(QObject *parent, TransferFile *transfer, const QString &error)
+ : QObject(parent),
+ m_transfer(transfer),
+ m_error(error)
+{
+ // Check if the transfer's site should be removed as well
+ QueueObject *site = 0;
+ if (transfer->parentObject()->getType() == QueueObject::Site && transfer->parentObject()->getChildrenList().count() == 1)
+ site = transfer->parentObject();
+
+ // Reparent the transfer, we can't remove it from the QueueObject's children list yet, because
+ // that will cause an iterator reset for directories, so we'll do that later in the fail method.
+ transfer->parentObject()->addSize(-transfer->m_actualSize);
+
+ transfer->parent()->removeChild(transfer);
+ insertChild(transfer);
+ KFTPQueue::Manager::self()->getFailedTransferList()->append(this);
+
+ // Check if the transfer's site should be removed as well
+ if (site) {
+ emit KFTPQueue::Manager::self()->siteRemoved(site->getId());
+ delete site;
+ }
+
+ // Mark transfer as failed
+ transfer->m_status = Transfer::Failed;
+}
+
+FailedTransfer::~ FailedTransfer()
+{
+}
+
+TransferFile *FailedTransfer::restore()
+{
+ // Emit failed transfer removal
+ emit KFTPQueue::Manager::self()->failedTransferRemoved(m_transfer->getId());
+
+ // Add the transfer back to the queue
+ removeChild(m_transfer);
+ KFTPQueue::Manager::self()->insertTransfer(m_transfer);
+
+ // Change the transfer's status, so it can be started
+ m_transfer->m_status = Transfer::Stopped;
+
+ // This object is now useless, so it shall be removed
+ KFTPQueue::Manager::self()->getFailedTransferList()->remove(this);
+ deleteLater();
+
+ return m_transfer;
+}
+
+void FailedTransfer::fail(TransferFile *transfer, const QString &error)
+{
+ // Should the transfer be retried
+ if (KFTPCore::Config::failedAutoRetry() && transfer->m_retryCount < KFTPCore::Config::failedAutoRetryCount()) {
+ // Semi-reset the current transfer
+ transfer->addCompleted(-transfer->m_completed);
+
+ transfer->m_retryCount++;
+ transfer->m_resumed = 0;
+ transfer->m_completed = 0;
+ transfer->m_aborting = false;
+ transfer->m_size = transfer->m_actualSize;
+
+ transfer->setSpeed(0);
+
+ // Restart the transfer
+ transfer->delayedExecute();
+ } else {
+ // Abort the transfer
+ transfer->blockSignals(true);
+ transfer->abort();
+ transfer->blockSignals(false);
+
+ QGuardedPtr<QueueObject> transferParent(transfer->parentObject());
+
+ // Create a new failed transfer (will automaticly reparent the transfer)
+ FailedTransfer *ft = new FailedTransfer(KFTPQueue::Manager::self(), transfer, error);
+
+ // Notify others about transfer's "completion"
+ emit transfer->transferComplete(transfer->m_id);
+
+ // Now that the iterators have been incremented we can remove the child without
+ // doing any harm to the queue processing.
+ if (transferParent)
+ transferParent->delChildObject(transfer);
+
+ // Fake transfer removal and signal new failed transfer
+ emit KFTPQueue::Manager::self()->transferRemoved(transfer->m_id);
+ emit KFTPQueue::Manager::self()->failedTransferNew(ft);
+ }
+}
+
+Transfer::Transfer(QObject *parent, Type type)
+ : QueueObject(parent, type),
+ m_deleteMe(false),
+ m_openAfterTransfer(false),
+ m_srcSession(0),
+ m_dstSession(0),
+ m_srcConnection(0),
+ m_dstConnection(0),
+ m_retryCount(0)
+{
+}
+
+
+Transfer::~Transfer()
+{
+}
+
+Connection *Transfer::getOppositeConnection(Connection *conn)
+{
+ return m_srcConnection == conn ? m_dstConnection : m_srcConnection;
+}
+
+Connection *Transfer::remoteConnection()
+{
+ return m_srcConnection ? m_srcConnection : m_dstConnection;
+}
+
+Connection *Transfer::initializeSession(Session *session)
+{
+ if (!session->isFreeConnection()) {
+ // We should wait for a connection to come
+ connect(session, SIGNAL(freeConnectionAvailable()), this, SLOT(slotConnectionAvailable()));
+
+ if (m_status != Waiting) {
+ m_status = Waiting;
+ emit objectUpdated();
+ }
+
+ return 0;
+ }
+
+ Connection *connection = session->assignConnection();
+ connection->acquire(this);
+
+ connect(connection->getClient()->eventHandler(), SIGNAL(connected()), this, SLOT(slotConnectionConnected()));
+
+ return connection;
+}
+
+void Transfer::deinitializeConnections()
+{
+ if (m_srcConnection) {
+ m_srcConnection->getClient()->eventHandler()->disconnect(this);
+ m_srcConnection->remove();
+ }
+
+ if (m_dstConnection) {
+ m_dstConnection->getClient()->eventHandler()->disconnect(this);
+ m_dstConnection->remove();
+ }
+}
+
+bool Transfer::assignSessions(Session *source, Session *destination)
+{
+ bool result = true;
+
+ // We need a source session
+ if (!m_sourceUrl.isLocalFile() && !m_srcConnection) {
+ if (!source)
+ m_srcSession = KFTPSession::Manager::self()->spawnRemoteSession(IgnoreSide, m_sourceUrl, 0);
+ else
+ m_srcSession = source;
+
+ if (!(m_srcConnection = initializeSession(m_srcSession)))
+ result = false;
+ }
+
+ // We need a destination session
+ if (!m_destUrl.isLocalFile() && !m_dstConnection) {
+ if (!destination)
+ m_dstSession = KFTPSession::Manager::self()->spawnRemoteSession(m_srcSession ? oppositeSide(m_srcSession->getSide()) : IgnoreSide, m_destUrl, 0);
+ else
+ m_dstSession = destination;
+
+ if (!(m_dstConnection = initializeSession(m_dstSession)))
+ result = false;
+ }
+
+ return result;
+}
+
+bool Transfer::connectionsReady()
+{
+ return (m_sourceUrl.isLocalFile() || m_srcConnection) && (m_destUrl.isLocalFile() || m_dstConnection);
+}
+
+void Transfer::slotConnectionAvailable()
+{
+ if (getStatus() != Waiting || (m_srcSession && !m_srcSession->isFreeConnection()) || (m_dstSession && !m_dstSession->isFreeConnection()))
+ return;
+
+ if (m_srcSession)
+ m_srcSession->QObject::disconnect(this, SLOT(slotConnectionAvailable()));
+
+ if (m_dstSession)
+ m_dstSession->QObject::disconnect(this, SLOT(slotConnectionAvailable()));
+
+ // Connection has become available, grab it now
+ execute();
+}
+
+void Transfer::slotConnectionConnected()
+{
+ if ((m_srcConnection && !m_srcConnection->isConnected()) || (m_dstConnection && !m_dstConnection->isConnected()))
+ return;
+
+ // Everything is ready for immediate execution
+ delayedExecute();
+}
+
+void Transfer::faceDestruction(bool abortSession)
+{
+ // This method is called before the object is deleted by the queue
+ // manager.
+ if (hasParentObject()) {
+ parentObject()->addSize(-m_actualSize);
+ parentObject()->delChildObject(this);
+ }
+
+ // Abort any dir scans that might be in progress
+ if (abortSession) {
+ KFTPSession::Session *session = KFTPSession::Manager::self()->find(this);
+
+ if (session)
+ session->abort();
+ }
+}
+
+Transfer *Transfer::parentTransfer()
+{
+ if (!hasParentTransfer())
+ return 0L;
+
+ return static_cast<Transfer*>(parent());
+}
+
+void Transfer::resetTransfer()
+{
+ // Disconnect signals
+ if (getStatus() != Waiting) {
+ if (m_srcConnection)
+ m_srcConnection->getClient()->eventHandler()->QObject::disconnect(this, SLOT(slotConnectionConnected()));
+
+ if (m_dstConnection)
+ m_dstConnection->getClient()->eventHandler()->QObject::disconnect(this, SLOT(slotConnectionConnected()));
+ }
+
+ // Reset connections & session pointers
+ m_srcConnection = 0L;
+ m_dstConnection = 0L;
+
+ m_srcSession = 0L;
+ m_dstSession = 0L;
+
+ m_status = Stopped;
+ m_resumed = 0;
+ m_completed = 0;
+ m_aborting = false;
+ m_size = m_actualSize;
+ m_retryCount = 0;
+
+ // Set the transfer speed to zero
+ setSpeed(0);
+}
+
+bool Transfer::canMove()
+{
+ return !isRunning();
+}
+
+void Transfer::update()
+{
+ KFTPQueue::Manager::self()->doEmitUpdate();
+ emit objectUpdated();
+}
+
+void Transfer::abort()
+{
+ // Unlock if the transfer was locked
+ unlock();
+
+ // Set the aborting flag
+ m_aborting = true;
+
+ emit transferAbort(m_id);
+}
+
+void Transfer::showTransCompleteBalloon()
+{
+ // Show a balloon :P
+ if (KFTPCore::Config::showBalloons() && !KFTPQueue::Manager::self()->isProcessing()) {
+ if (!KFTPCore::Config::showBalloonWhenQueueEmpty() || (KFTPQueue::Manager::self()->topLevelObject()->getChildrenList().count() == 1 && !hasParentTransfer())) {
+ QString transCompleteStr = i18n("Transfer of the following files is complete:");
+ transCompleteStr += "<br><i>";
+ transCompleteStr += getSourceUrl().fileName();
+ transCompleteStr += "</i>";
+ KFTPWidgets::SystemTray::self()->showBalloon(transCompleteStr);
+ }
+ }
+}
+
+void Transfer::lock()
+{
+ // Transfers can only be locked if they are not currently running
+ if (m_status == Stopped) {
+ m_status = Locked;
+
+ emit objectUpdated();
+ }
+}
+
+void Transfer::unlock()
+{
+ if (isLocked()) {
+ m_status = Stopped;
+
+ emit objectUpdated();
+ }
+}
+
+}
+#include "kftptransfer.moc"
diff --git a/kftpgrabber/src/kftptransfer.h b/kftpgrabber/src/kftptransfer.h
new file mode 100644
index 0000000..78573a2
--- /dev/null
+++ b/kftpgrabber/src/kftptransfer.h
@@ -0,0 +1,337 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEKFTPTRANSFER_H
+#define KFTPQUEUEKFTPTRANSFER_H
+
+#include "queueobject.h"
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <qguardedptr.h>
+
+#include <kurl.h>
+
+namespace KFTPSession {
+ class Session;
+ class Connection;
+}
+
+namespace KFTPQueue {
+
+enum TransferType {
+ Download = 0,
+ Upload = 1,
+ FXP = 2
+};
+
+class TransferFile;
+
+/**
+ * This class represents a failed transfer. Such a transfer is removed
+ * from queue so the error message can later be examined and the transfer
+ * restarted.
+ *
+ * @author Jernej Kos
+ */
+class FailedTransfer : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Constructs a new failed transfer object. The actual transfer
+ * will be reparented (the FailedTransfer object will become its
+ * parent).
+ */
+ FailedTransfer(QObject *parent, TransferFile *transfer, const QString &error);
+ ~FailedTransfer();
+
+ /**
+ * Returns the error message.
+ *
+ * @return The error message.
+ */
+ QString getError() const { return m_error; }
+
+ /**
+ * Add this transfer back to the queue. The FailedTransfer object
+ * will be destroyed afterwards!
+ *
+ * @return Pointer to the TransferFile object that was just restored.
+ */
+ TransferFile *restore();
+
+ /**
+ * Returns the actual transfer object that failed. This transfer is
+ * marked as failed so execute() method can't be called!
+ *
+ * @return A KFTPQueue::TransferFile object.
+ */
+ TransferFile *getTransfer() const { return m_transfer; }
+
+ /**
+ * Use this method to declare a transfer as failed. The transfer will
+ * be aborted, removed from queue and added to the failed transfer
+ * list.
+ *
+ * @param transfer Pointer to the transfer object that failed.
+ * @param error The error that ocurred.
+ */
+ static void fail(TransferFile *transfer, const QString &error);
+private:
+ QGuardedPtr<TransferFile> m_transfer;
+ QString m_error;
+};
+
+/**
+ * This class is the base class for all transfers used in KFTPGrabber. It
+ * provides some basic methods that are extended by KFTPQueue::TransferFile and
+ * KFTPQueue::TransferDir for specific file or dir operations.
+ *
+ * @author Jernej Kos
+ */
+class Transfer : public QueueObject
+{
+friend class FailedTransfer;
+friend class TransferDir;
+friend class Manager;
+friend class KFTPSession::Session;
+friend class KFTPSession::Connection;
+Q_OBJECT
+public:
+ Transfer(QObject *parent, Type type);
+ ~Transfer();
+
+ /**
+ * Returns the source KURL of this transfer.
+ *
+ * @return Source url
+ */
+ KURL getSourceUrl() const { return m_sourceUrl; }
+
+ /**
+ * Returns the destination KURL of this transfer.
+ *
+ * @return Destination url
+ */
+ KURL getDestUrl() const { return m_destUrl; }
+
+ /**
+ * Set the source KURL of this transfer.
+ *
+ * @param url Source url wannabe
+ */
+ void setSourceUrl(const KURL &url) { m_sourceUrl = url; }
+
+ /**
+ * Set the destination url of this transfer.
+ *
+ * @param url Destination url wannabe
+ */
+ void setDestUrl(const KURL &url) { m_destUrl = url; }
+
+ /**
+ * Return the KFTPQueue::TransferType -- that is if this transfer is an Upload, Download
+ * or FXP transfer.
+ *
+ * @return Upload, Download or FXP
+ */
+ TransferType getTransferType() const { return m_transferType; }
+
+ /**
+ * Set current KFTPQueue::TransferType -- that is Upload, Download or FXP
+ *
+ * @param type Upload, Download or FXP
+ */
+ void setTransferType(TransferType type) { m_transferType = type; }
+
+ /**
+ * Get the source session for this transfer.
+ *
+ * @return A valid KFTPSession::Session instance or 0 if not started
+ */
+ KFTPSession::Session *getSourceSession() const { return m_srcSession; }
+
+ /**
+ * Get the destination session for this transfer.
+ *
+ * @return A valid KFTPSession::Session instance or 0 if not started
+ */
+ KFTPSession::Session *getDestinationSession() const { return m_dstSession; }
+
+ /**
+ * Returns the connection opposite of one that is passed. So if you
+ * pass the source connection, the destination one is returned and
+ * vice-versa.
+ *
+ * @param conn The connection
+ * @return The opposite Connection
+ */
+ KFTPSession::Connection *getOppositeConnection(KFTPSession::Connection *conn);
+
+ /**
+ * Returns the remote connection. If both connections are remote, this
+ * method returns the source connection.
+ *
+ * @return The remote connection
+ */
+ KFTPSession::Connection *remoteConnection();
+
+ /**
+ * Is this transfer a child of another transfer ?
+ *
+ * @return true if this transfer is a child of another KFTPQueue::Transfer
+ */
+ bool hasParentTransfer() const { return parent()->inherits("KFTPQueue::Transfer"); }
+
+ /**
+ * Should a transfered file be automagicly opened after transfer ? This only applies for
+ * download transfers.
+ *
+ * @param value The setting
+ */
+ void setOpenAfterTransfer(bool value) { m_openAfterTransfer = value; }
+
+ /**
+ * Is this transfer marked for deletion ?
+ *
+ * @return true if this transfer is marked for deletion
+ */
+ bool isDeleteMarked() const { return m_deleteMe; }
+
+ /**
+ * Get the transfer's parent transfer.
+ *
+ * @return Transfer's parent or NULL if isChild() returns false
+ */
+ Transfer *parentTransfer();
+
+ /**
+ * Lock this transfer for further changes.
+ */
+ void lock();
+
+ /**
+ * Unlock a previously locked transfer.
+ */
+ void unlock();
+
+ /**
+ * Abort current transfer.
+ */
+ virtual void abort();
+
+ /**
+ * Just emits the objectUpdated() signal.
+ */
+ void emitUpdate() { emit objectUpdated(); }
+
+ /**
+ * Assign sessions to this transfer in advance (= before starting the
+ * actual transfer). Both sessions must have free connections. If you
+ * pass NULL to both parameters sessions will be looked up and might
+ * be spawned.
+ *
+ * Note that the sessions MUST be the right ones based on the transfer's
+ * URL, otherwise unexpected results will ocurr!
+ *
+ * @param source The source session
+ * @param destination The destination session
+ * @return True if the sessions are ready for immediate use
+ */
+ virtual bool assignSessions(KFTPSession::Session *source = 0, KFTPSession::Session *destination = 0);
+
+ /**
+ * This method returns true if both connections have been properly
+ * initialized.
+ */
+ bool connectionsReady();
+protected:
+ bool m_deleteMe;
+ bool m_openAfterTransfer;
+ TransferType m_transferType;
+
+ /* Source/destination URL */
+ KURL m_sourceUrl;
+ KURL m_destUrl;
+
+ /* Transfer sessions */
+ KFTPSession::Session *m_srcSession;
+ KFTPSession::Session *m_dstSession;
+
+ /* Source/destination connections */
+ KFTPSession::Connection *m_srcConnection;
+ KFTPSession::Connection *m_dstConnection;
+
+ int m_retryCount;
+
+ void showTransCompleteBalloon();
+ void resetTransfer();
+
+ void update();
+ bool canMove();
+
+ /**
+ * This method gets called just before the transfer is removed.
+ *
+ * @param abortSession If true any session that this transfer is using is aborted
+ */
+ void faceDestruction(bool abortSession = true);
+
+ /**
+ * Initialize the specified session for use with this transfer.
+ *
+ * @param session The session to use
+ * @return A valid Connection or NULL if one wasn't available
+ */
+ KFTPSession::Connection *initializeSession(KFTPSession::Session *session);
+
+ /**
+ * Deinitialize currently acquired connections. Do not call this method
+ * unless you know what you are doing.
+ */
+ void deinitializeConnections();
+private slots:
+ void slotConnectionAvailable();
+ void slotConnectionConnected();
+signals:
+ void transferStart(long id);
+ void transferComplete(long id);
+ void transferAbort(long id);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftptransferdir.cpp b/kftpgrabber/src/kftptransferdir.cpp
new file mode 100644
index 0000000..c5359c4
--- /dev/null
+++ b/kftpgrabber/src/kftptransferdir.cpp
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftptransferdir.h"
+#include "kftpqueue.h"
+#include "kftpsession.h"
+#include "queuegroup.h"
+
+#include <kstandarddirs.h>
+
+using namespace KFTPEngine;
+using namespace KFTPSession;
+
+namespace KFTPQueue {
+
+TransferDir::TransferDir(QObject *parent)
+ : Transfer(parent, Transfer::Directory),
+ m_scanned(false),
+ m_group(new QueueGroup(this))
+{
+ // Connect to some group signals
+ connect(m_group, SIGNAL(interrupted()), this, SLOT(slotGroupInterrupted()));
+ connect(m_group, SIGNAL(done()), this, SLOT(slotGroupDone()));
+}
+
+void TransferDir::execute()
+{
+ // Assign sessions if they are missing
+ if (!connectionsReady() && !assignSessions(m_srcSession, m_dstSession))
+ return;
+
+ if ((m_dstConnection && !m_dstConnection->isConnected()) || (m_srcConnection && !m_srcConnection->isConnected())) {
+ // If not yet connected, wait for the connection
+ m_status = Connecting;
+ return;
+ }
+
+ if (!m_scanned && !hasParentTransfer() && m_children.count() == 0) {
+ if (m_srcSession) {
+ m_srcSession->scanDirectory(this, m_srcConnection);
+ m_scanned = true;
+
+ connect(m_srcSession, SIGNAL(dirScanDone()), this, SLOT(slotDirScanDone()));
+ } else {
+ connect(new DirectoryScanner(this), SIGNAL(completed()), this, SLOT(slotDirScanDone()));
+ }
+
+ return;
+ }
+
+ m_status = Running;
+
+ // If the directory is empty, create it anyway
+ if (m_children.count() == 0) {
+ if (m_destUrl.isLocalFile()) {
+ KStandardDirs::makeDir(m_destUrl.path());
+ } else {
+ m_dstSession->getClient()->mkdir(m_destUrl);
+ }
+ }
+
+ // We no longer need the connections, release them
+ deinitializeConnections();
+
+ // Reset and start the group
+ m_group->reset();
+ m_group->executeNextTransfer();
+}
+
+void TransferDir::abort()
+{
+ // If not running, just return
+ if (!isRunning()) return;
+
+ Transfer::abort();
+
+ // Signal abort to all child transfers
+ if (!m_deleteMe) {
+ QueueObject *i;
+
+ for (i = m_children.first(); i; i = m_children.next()) {
+ if (i->isRunning() && !i->isAborting())
+ i->abort();
+ }
+ }
+
+ resetTransfer();
+ update();
+}
+
+void TransferDir::slotGroupDone()
+{
+ // There are no more transfers, so we are finished
+ showTransCompleteBalloon();
+ m_deleteMe = true;
+ resetTransfer();
+
+ emit transferComplete(m_id);
+ KFTPQueue::Manager::self()->doEmitUpdate();
+}
+
+void TransferDir::slotGroupInterrupted()
+{
+ if (!m_aborting)
+ abort();
+}
+
+void TransferDir::slotDirScanDone()
+{
+ if (m_srcSession)
+ disconnect(m_srcSession, SIGNAL(dirScanDone()), this, SLOT(slotDirScanDone()));
+
+ // Reexecute the transfer
+ delayedExecute();
+}
+
+}
+
+#include "kftptransferdir.moc"
diff --git a/kftpgrabber/src/kftptransferdir.h b/kftpgrabber/src/kftptransferdir.h
new file mode 100644
index 0000000..1f0e7ff
--- /dev/null
+++ b/kftpgrabber/src/kftptransferdir.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEKFTPTRANSFERDIR_H
+#define KFTPQUEUEKFTPTRANSFERDIR_H
+
+#include "kftptransfer.h"
+
+namespace KFTPEngine {
+ class Event;
+}
+
+namespace KFTPQueue {
+
+/**
+ * This class represents a queued directory transfer. It can have child transfers that
+ * will be executed one by one, when this transfer is executed.
+ *
+ * @author Jernej Kos
+ */
+class TransferDir : public Transfer
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent object
+ */
+ TransferDir(QObject *parent);
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::Transfer.
+ */
+ void execute();
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::Transfer.
+ */
+ void abort();
+private:
+ bool m_scanned;
+ QueueGroup *m_group;
+private slots:
+ void slotGroupDone();
+ void slotGroupInterrupted();
+
+ void slotDirScanDone();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/kftptransferfile.cpp b/kftpgrabber/src/kftptransferfile.cpp
new file mode 100644
index 0000000..9f49ae5
--- /dev/null
+++ b/kftpgrabber/src/kftptransferfile.cpp
@@ -0,0 +1,413 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftptransferfile.h"
+#include "widgets/systemtray.h"
+#include "kftpsession.h"
+#include "statistics.h"
+
+#include "engine/thread.h"
+
+#include "misc/config.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kio/renamedlg.h>
+#include <kdiskfreesp.h>
+
+#include <qtimer.h>
+#include <qfileinfo.h>
+
+using namespace KFTPEngine;
+using namespace KFTPSession;
+
+namespace KFTPQueue {
+
+TransferFile::TransferFile(QObject *parent)
+ : Transfer(parent, Transfer::File),
+ m_updateTimer(0),
+ m_dfTimer(0)
+{
+}
+
+bool TransferFile::assignSessions(Session *source, Session *destination)
+{
+ if (!Transfer::assignSessions(source, destination))
+ return false;
+
+ // Connect signals
+ if (m_srcConnection) {
+ connect(m_srcConnection->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+ connect(m_srcConnection, SIGNAL(aborting()), this, SLOT(slotSessionAborting()));
+ connect(m_srcConnection, SIGNAL(connectionLost(KFTPSession::Connection*)), this, SLOT(slotConnectionLost(KFTPSession::Connection*)));
+ }
+
+ if (m_dstConnection) {
+ connect(m_dstConnection->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+ connect(m_dstConnection, SIGNAL(aborting()), this, SLOT(slotSessionAborting()));
+ connect(m_dstConnection, SIGNAL(connectionLost(KFTPSession::Connection*)), this, SLOT(slotConnectionLost(KFTPSession::Connection*)));
+ }
+
+ return true;
+}
+
+void TransferFile::execute()
+{
+ // Failed transfers aren't allowed to be executed until they are readded to
+ // the queue and the Failed status is changed to Stopped.
+ if (getStatus() == Failed)
+ return;
+
+ // Assign sessions if they are missing
+ if (!connectionsReady() && !assignSessions(m_srcSession, m_dstSession))
+ return;
+
+ if ((m_dstConnection && !m_dstConnection->isConnected()) || (m_srcConnection && !m_srcConnection->isConnected())) {
+ m_status = Connecting;
+ return;
+ }
+
+ // We are running now
+ m_status = Running;
+
+ m_completed = 0;
+ m_resumed = 0;
+
+ // Init timer to follow the update
+ if (!m_updateTimer) {
+ m_updateTimer = new QTimer(this);
+ connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(slotTimerUpdate()));
+ m_updateTimer->start(1000);
+ }
+
+ // Should we check for free space ?
+ if (KFTPCore::Config::diskCheckSpace() && !m_dfTimer) {
+ m_dfTimer = new QTimer(this);
+ connect(m_dfTimer, SIGNAL(timeout()), this, SLOT(slotTimerDiskFree()));
+ m_dfTimer->start(KFTPCore::Config::diskCheckInterval() * 1000);
+ }
+
+ emit transferStart(m_id);
+
+ switch(m_transferType) {
+ case Download: {
+ m_srcConnection->getClient()->get(m_sourceUrl, m_destUrl);
+ break;
+ }
+ case Upload: {
+ m_dstConnection->getClient()->put(m_sourceUrl, m_destUrl);
+ break;
+ }
+ case FXP: {
+ // Start the timer to extrapolate transfer rate
+ m_elapsedTime.start();
+ m_srcConnection->getClient()->siteToSite(m_dstConnection->getClient(), m_sourceUrl, m_destUrl);
+ break;
+ }
+ }
+}
+
+void TransferFile::slotConnectionLost(KFTPSession::Connection *connection)
+{
+ if (!isRunning())
+ return;
+
+ if (m_status != Connecting) {
+ // Semi-reset the current transfer
+ addCompleted(-m_completed);
+
+ m_resumed = 0;
+ m_completed = 0;
+ m_aborting = false;
+ m_size = m_actualSize;
+
+ setSpeed(0);
+
+ // Wait for the connection to come
+ m_status = Connecting;
+ connection->reconnect();
+ } else
+ connection->reconnect();
+}
+
+void TransferFile::slotEngineEvent(KFTPEngine::Event *event)
+{
+ if (!isRunning())
+ return;
+
+ switch (event->type()) {
+ case Event::EventTransferComplete: {
+ // ***************************************************************************
+ // ************************ EventTransferComplete ****************************
+ // ***************************************************************************
+ // Calculate transfer rate for last transfer, and save to site's statistics
+ if (getTransferType() == FXP) {
+ if (m_elapsedTime.elapsed() > 10000) {
+ double speed = (m_size - m_resumed) / (double) m_elapsedTime.elapsed();
+ Statistics::self()->getSite(m_sourceUrl)->setLastFxpSpeed(speed * 1024);
+ }
+ }
+
+ // Update the completed size if the transfer was faster than the update timer
+ addCompleted(m_size - m_completed);
+
+ m_updateTimer->stop();
+ m_updateTimer->QObject::disconnect();
+
+ if (m_openAfterTransfer && m_transferType == Download) {
+ // Set status to stopped, so the view gets reloaded
+ m_status = Stopped;
+
+ Manager::self()->openAfterTransfer(this);
+ } else {
+ showTransCompleteBalloon();
+ }
+
+ m_deleteMe = true;
+ addActualSize(-m_size);
+
+ resetTransfer();
+ emit transferComplete(m_id);
+
+ KFTPQueue::Manager::self()->doEmitUpdate();
+ break;
+ }
+ case Event::EventResumeOffset: {
+ // ***************************************************************************
+ // ************************** EventResumeOffset ******************************
+ // ***************************************************************************
+ m_resumed = event->getParameter(0).asFileSize();
+ addCompleted(m_resumed);
+ break;
+ }
+ case Event::EventError: {
+ // ***************************************************************************
+ // ****************************** EventError *********************************
+ // ***************************************************************************
+ ErrorCode error = event->getParameter(0).asErrorCode();
+
+ switch (error) {
+ case ConnectFailed: {
+ FailedTransfer::fail(this, i18n("Connection to the server has failed."));
+ break;
+ }
+ case LoginFailed: {
+ FailedTransfer::fail(this, i18n("Login to the server has failed"));
+ break;
+ }
+ case FileNotFound: {
+ FailedTransfer::fail(this, i18n("Source file cannot be found."));
+ break;
+ }
+ case PermissionDenied: {
+ FailedTransfer::fail(this, i18n("Permission was denied."));
+ break;
+ }
+ case FileOpenFailed: {
+ FailedTransfer::fail(this, i18n("Unable to open local file for read or write operations."));
+ break;
+ }
+ case OperationFailed: {
+ FailedTransfer::fail(this, i18n("Transfer failed for some reason."));
+ break;
+ }
+ default: break;
+ }
+
+ break;
+ }
+ case Event::EventFileExists: {
+ // ***************************************************************************
+ // *************************** EventFileExists *******************************
+ // ***************************************************************************
+ DirectoryListing list = event->getParameter(0).asDirectoryListing();
+ FileExistsWakeupEvent *event = Manager::self()->fileExistsAction(this, list.list());
+
+ if (event)
+ remoteConnection()->getClient()->wakeup(event);
+ break;
+ }
+ default: break;
+ }
+}
+
+void TransferFile::wakeup(KFTPEngine::FileExistsWakeupEvent *event)
+{
+ if (event)
+ remoteConnection()->getClient()->wakeup(event);
+}
+
+void TransferFile::slotTimerUpdate()
+{
+ // Update the current stats
+ if (!m_srcSession && !m_dstSession) {
+ m_updateTimer->stop();
+ m_updateTimer->QObject::disconnect();
+ return;
+ }
+
+ if (m_status == Running) {
+ // Get speed from connection, or use FXP extrapolation.
+ if (getTransferType() == FXP) {
+ double lastFxpSpeed = Statistics::self()->getSite(m_sourceUrl)->lastFxpSpeed();
+
+ if (lastFxpSpeed != 0.0) {
+ setSpeed(lastFxpSpeed);
+
+ if (m_completed < m_size)
+ addCompleted(getSpeed());
+ }
+ } else {
+ if (remoteConnection()->getClient()->socket()->getTransferBytes() > m_completed - m_resumed)
+ addCompleted(remoteConnection()->getClient()->socket()->getTransferBytes() - (m_completed - m_resumed));
+
+ setSpeed(remoteConnection()->getClient()->socket()->getTransferSpeed());
+ }
+ }
+
+ update();
+}
+
+void TransferFile::slotTimerDiskFree()
+{
+ // Check for disk usage
+ if (KFTPCore::Config::diskCheckSpace()) {
+ KDiskFreeSp *df = KDiskFreeSp::findUsageInfo((getDestUrl().path()));
+ connect(df, SIGNAL(foundMountPoint(const QString&, unsigned long, unsigned long, unsigned long)), this, SLOT(slotDiskFree(const QString&, unsigned long, unsigned long, unsigned long)));
+ }
+}
+
+void TransferFile::slotDiskFree(const QString &mountPoint, unsigned long, unsigned long, unsigned long kBAvail)
+{
+ if (KFTPCore::Config::diskCheckSpace()) {
+ // Is there enough free space ?
+ if (kBAvail < (unsigned long) KFTPCore::Config::diskMinFreeSpace()) {
+ QString transAbortStr = i18n("Transfer of the following files <b>has been aborted</b> because there is not enough free space left on '%1':").arg(mountPoint);
+ transAbortStr += "<br><i>";
+ transAbortStr += getSourceUrl().fileName();
+ transAbortStr += "</i>";
+ KFTPWidgets::SystemTray::self()->showBalloon(transAbortStr);
+
+ // Abort the transfer
+ abort();
+ }
+ }
+}
+
+void TransferFile::resetTransfer()
+{
+ // Unlock the sessions (they should be unlocked automaticly when the transferComplete signal
+ // is emitted, but when a transfer is a child transfer, the next transfer may need the session
+ // sooner). Also sessions should be unlocked when transfer aborts.
+ if (getStatus() != Waiting) {
+ // Disconnect signals
+ if (m_srcConnection) {
+ m_srcConnection->getClient()->eventHandler()->QObject::disconnect(this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+ m_srcConnection->QObject::disconnect(this, SLOT(slotSessionAborting()));
+ m_srcConnection->QObject::disconnect(this, SLOT(slotConnectionLost(KFTPSession::Connection*)));
+
+ m_srcConnection->remove();
+ }
+
+ if (m_dstConnection) {
+ m_dstConnection->getClient()->eventHandler()->QObject::disconnect(this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+ m_dstConnection->QObject::disconnect(this, SLOT(slotSessionAborting()));
+ m_dstConnection->QObject::disconnect(this, SLOT(slotConnectionLost(KFTPSession::Connection*)));
+
+ m_dstConnection->remove();
+ }
+ }
+
+ Transfer::resetTransfer();
+}
+
+void TransferFile::slotSessionAborting()
+{
+ if (!m_aborting)
+ abort();
+}
+
+void TransferFile::abort()
+{
+ if (!isRunning())
+ return;
+
+ Transfer::abort();
+
+ if (getStatus() == Waiting) {
+ if (m_srcSession)
+ m_srcSession->QObject::disconnect(this, SLOT(slotConnectionAvailable()));
+
+ if (m_dstSession)
+ m_dstSession->QObject::disconnect(this, SLOT(slotConnectionAvailable()));
+ }
+
+ if (m_updateTimer) {
+ m_updateTimer->stop();
+ m_updateTimer->QObject::disconnect();
+
+ delete m_updateTimer;
+ m_updateTimer = 0L;
+ }
+
+ if (m_dfTimer) {
+ m_dfTimer->stop();
+ m_dfTimer->QObject::disconnect();
+
+ delete m_dfTimer;
+ m_dfTimer = 0L;
+ }
+
+ // Abort any transfers
+ if (m_srcConnection)
+ m_srcConnection->abort();
+
+ if (m_dstConnection)
+ m_dstConnection->abort();
+
+ // Update everything
+ resetTransfer();
+
+ if (!hasParentTransfer())
+ update();
+
+ if (hasParentObject() && parentObject()->isAborting())
+ disconnect(parent());
+}
+
+}
+
+
+#include "kftptransferfile.moc"
diff --git a/kftpgrabber/src/kftptransferfile.h b/kftpgrabber/src/kftptransferfile.h
new file mode 100644
index 0000000..da47339
--- /dev/null
+++ b/kftpgrabber/src/kftptransferfile.h
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEKFTPTRANSFERFILE_H
+#define KFTPQUEUEKFTPTRANSFERFILE_H
+
+#include <qdatetime.h>
+
+#include "kftptransfer.h"
+
+namespace KFTPSession {
+ class Connection;
+}
+
+namespace KFTPEngine {
+ class Event;
+ class FileExistsWakeupEvent;
+}
+
+namespace KFTPQueue {
+
+/**
+ * This class represents a queued file transfer.
+ *
+ * @author Jernej Kos
+ */
+class TransferFile : public Transfer
+{
+Q_OBJECT
+friend class Manager;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent object
+ */
+ TransferFile(QObject *parent);
+
+ /**
+ * Wakes this transfer up after the action for the file exists situation
+ * has been decided. The event is simply relayed to the underlying socket.
+ *
+ * @param event Event instance or 0 if nothing should be delivered
+ */
+ void wakeup(KFTPEngine::FileExistsWakeupEvent *event);
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::QueueObject.
+ */
+ void execute();
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::Transfer.
+ */
+ void abort();
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::Transfer.
+ *
+ * @param source The source session
+ * @param destination The destination session
+ * @return True if the sessions are ready for immediate use
+ */
+ bool assignSessions(KFTPSession::Session *source = 0, KFTPSession::Session *destination = 0);
+private:
+ /* Update timers */
+ QTimer *m_updateTimer;
+ QTimer *m_dfTimer;
+
+ /* FXP */
+ QTime m_elapsedTime;
+
+ /**
+ * @overload
+ * Reimplemented from KFTPQueue::Transfer.
+ */
+ void resetTransfer();
+private slots:
+ void slotTimerUpdate();
+ void slotTimerDiskFree();
+
+ void slotDiskFree(const QString &mountPoint, unsigned long kBSize, unsigned long kBUsed, unsigned long kBAvail);
+
+ void slotEngineEvent(KFTPEngine::Event *event);
+ void slotSessionAborting();
+
+ void slotConnectionLost(KFTPSession::Connection *connection);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/main.cpp b/kftpgrabber/src/main.cpp
new file mode 100644
index 0000000..ea7482b
--- /dev/null
+++ b/kftpgrabber/src/main.cpp
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "mainwindow.h"
+#include "misc/config.h"
+
+#include <kuniqueapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <ksplashscreen.h>
+
+#include "kftpsession.h"
+
+static const char description[] =
+ I18N_NOOP("KFTPGrabber - an FTP client for KDE");
+
+static const char version[] = "0.8.1";
+
+static KCmdLineOptions options[] =
+{
+ { "+[url]", I18N_NOOP("An optional URL to connect to"), 0},
+ KCmdLineLastOption
+};
+
+int main(int argc, char **argv)
+{
+ KAboutData about("kftpgrabber", I18N_NOOP("KFTPGrabber"), version, description,
+ KAboutData::License_GPL, "(C) 2007, The KFTPGrabber developers", 0, "http://www.kftp.org");
+ about.addAuthor("Jernej Kos", I18N_NOOP("Lead developer"), "kostko@unimatrix-one.org");
+ about.addAuthor("Markus Brüffer", I18N_NOOP("Developer"), "markus@brueffer.de");
+
+ about.addCredit("Aris Adamantiadis", I18N_NOOP("LibSSH code"), "aris@0xbadc0de.be");
+ about.addCredit("Anthony D. Urso", I18N_NOOP("otpCalc code"));
+ about.addCredit("Kopete Developers", I18N_NOOP("KopeteBalloon popup code"), "kopete-devel@kde.org");
+ about.addCredit("KSysGuard Developers", I18N_NOOP("Traffic graph widget"), "cs@kde.org");
+ about.addCredit("Bob Ziuchkovski", I18N_NOOP("Icon design"), "ziuchkov@uiuc.edu");
+ about.addCredit("Tobias Ussing", I18N_NOOP("Testing and debugging"), "thehole@mail.seriesdb.com");
+ about.addCredit("Lee Joseph", I18N_NOOP("Testing and debugging"), "cyberspy1@hotmail.com");
+ about.addCredit("Tim Kosse", I18N_NOOP("Directory parser code"), "tim.kosse@gmx.de");
+ about.addCredit("Peter Penz", I18N_NOOP("Listview column handling code"), "peter.penz@gmx.at");
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KUniqueApplication app;
+
+ if (app.isRestored()) {
+ RESTORE(MainWindow);
+ } else {
+ MainWindow *mainWindow = 0;
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KSplashScreen *splash = 0L;
+ QString splashPath = locate("appdata", "kftpgrabber-logo.png");
+ if (!KFTPCore::Config::startMinimized() && KFTPCore::Config::showSplash()) {
+ // Show the splash screen
+ if (!splashPath.isNull()) {
+ QPixmap splashImage = QPixmap(splashPath);
+ splash = new KSplashScreen(splashImage);
+ splash->setMaximumWidth(400);
+ splash->show();
+ }
+ }
+
+ mainWindow = new MainWindow();
+ app.setMainWidget(mainWindow);
+
+ if (!KFTPCore::Config::startMinimized())
+ mainWindow->show();
+
+ // Check if an URL was passed as a command line argument
+ if (args->count() == 1) {
+ KURL remoteUrl = args->url(0);
+
+ if (!remoteUrl.isLocalFile()) {
+ if (!remoteUrl.port())
+ remoteUrl.setPort(21);
+
+ if (!remoteUrl.hasUser())
+ remoteUrl.setUser("anonymous");
+
+ if (!remoteUrl.hasPass()) {
+ if (!KFTPCore::Config::anonMail().isEmpty())
+ remoteUrl.setPass(KFTPCore::Config::anonMail());
+ else
+ remoteUrl.setPass("userlogin@anonymo.us");
+ }
+
+ KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, remoteUrl);
+ }
+ }
+
+ if (splash != 0L) {
+ splash->finish(mainWindow);
+ delete splash;
+ }
+
+ args->clear();
+ }
+
+ return app.exec();
+}
+
diff --git a/kftpgrabber/src/mainactions.cpp b/kftpgrabber/src/mainactions.cpp
new file mode 100644
index 0000000..d694ab1
--- /dev/null
+++ b/kftpgrabber/src/mainactions.cpp
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "mainactions.h"
+#include <config.h>
+
+#include "misc.h"
+#include "kftpapi.h"
+#include "kftpqueue.h"
+#include "kftpbookmarks.h"
+#include "configdialog.h"
+#include "kftpsession.h"
+
+#include "widgets/browser/view.h"
+#include "widgets/browser/detailsview.h"
+#include "widgets/browser/propsplugin.h"
+
+#include "misc/config.h"
+#include "misc/filter.h"
+
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kaboutapplication.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kpropertiesdialog.h>
+#include <kio/job.h>
+#include <kshred.h>
+#include <klineedit.h>
+
+#include <qclipboard.h>
+
+using namespace KFTPGrabberBase;
+using namespace KFTPWidgets::Browser;
+using namespace KFTPCore::Filter;
+
+MainActions::MainActions(MainWindow *parent)
+ : QObject(parent),
+ m_configDialog(0),
+ m_closeApp(false)
+{
+ // setup File menu
+ m_fileConnectAction = new KAction(i18n("Quick &Connect..."), "connect_creating", KShortcut(), mainWidget(), SLOT(slotQuickConnect()), actionCollection(), "file_quick_connect");
+ m_newSessionAction = new KActionMenu(i18n("&New Session"), "filenew", actionCollection(), "file_newsession");
+
+ KAction *leftSide = new KAction(i18n("&Left Side"), KShortcut(), this, SLOT(slotNewSessionLeft()), actionCollection());
+ KAction *rightSide = new KAction(i18n("&Right Side"), KShortcut(), this, SLOT(slotNewSessionRight()), actionCollection());
+ m_newSessionAction->insert(leftSide);
+ m_newSessionAction->insert(rightSide);
+ m_newSessionAction->setStickyMenu(true);
+ m_newSessionAction->setDelayed(false);
+ KStdAction::quit(this, SLOT(slotFileQuit()), actionCollection());
+
+ // setup Settings menu
+ //KStdAction::configureToolbars(this, SLOT(slotConfigureToolbars()), actionCollection());
+ //KStdAction::keyBindings(this, SLOT(slotKeyBindings()), actionCollection());
+ KStdAction::saveOptions(this, SLOT(slotSettingsSave()), actionCollection());
+ KStdAction::preferences(this, SLOT(slotSettingsConfig()), actionCollection());
+ mainWidget()->createStandardStatusBarAction();
+ mainWidget()->setStandardToolBarMenuEnabled(true);
+
+ // setup transfermode actions
+ m_transModeAction = new KActionMenu(i18n("&Transfer Mode (Auto)"), "binary", actionCollection(), "transfermode");
+ m_modeAscii = new KRadioAction(i18n("ASCII"), "ascii", KShortcut(), this, SLOT(slotModeAscii()), actionCollection());
+ m_modeBinary = new KRadioAction(i18n("Binary"), "binary", KShortcut(), this, SLOT(slotModeBinary()), actionCollection());
+ m_modeAuto = new KRadioAction(i18n("Auto"), KShortcut(), this, SLOT(slotModeAuto()), actionCollection());
+
+ // Set grouping so only one action can be selected
+ m_modeAscii->setExclusiveGroup("Transfer mode");
+ m_modeBinary->setExclusiveGroup("Transfer mode");
+ m_modeAuto->setExclusiveGroup("Transfer mode");
+
+ // Insert the actions into the menu
+ m_transModeAction->insert(m_modeAscii);
+ m_transModeAction->insert(m_modeBinary);
+ m_transModeAction->popupMenu()->insertSeparator();
+ m_transModeAction->insert(m_modeAuto);
+ m_transModeAction->setStickyMenu(true);
+ m_transModeAction->setDelayed(false);
+}
+
+KActionCollection *MainActions::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+KMainWindow *MainActions::mainWidget()
+{
+ return KFTPAPI::getInstance()->mainWindow();
+}
+
+void MainActions::slotModeAscii()
+{
+ m_transModeAction->setIcon("ascii");
+ m_transModeAction->setText(i18n("&Transfer Mode (ASCII)"));
+ KFTPCore::Config::self()->setGlobalMode('A');
+}
+
+void MainActions::slotModeBinary()
+{
+ m_transModeAction->setIcon("binary");
+ m_transModeAction->setText(i18n("&Transfer Mode (Binary)"));
+ KFTPCore::Config::self()->setGlobalMode('I');
+}
+
+void MainActions::slotModeAuto()
+{
+ m_transModeAction->setText(i18n("&Transfer Mode (Auto)"));
+ KFTPCore::Config::self()->setGlobalMode('X');
+}
+
+void MainActions::slotFileQuit()
+{
+ m_closeApp = true;
+ mainWidget()->close();
+}
+
+void MainActions::slotSettingsSave()
+{
+ KFTPCore::Config::self()->saveConfig();
+}
+
+void MainActions::slotSettingsConfig()
+{
+ if (!m_configDialog)
+ m_configDialog = new KFTPWidgets::ConfigDialog(mainWidget());
+
+ m_configDialog->prepareDialog();
+ m_configDialog->exec();
+}
+
+void MainActions::slotNewSessionLeft()
+{
+ KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::LeftSide, true);
+}
+
+void MainActions::slotNewSessionRight()
+{
+ KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::RightSide, true);
+}
+
+void MainActions::slotKeyBindings()
+{
+ KKeyDialog::configure(actionCollection());
+}
+
+void MainActions::slotConfigureToolbars()
+{
+ KEditToolbar dlg(actionCollection());
+
+ if (dlg.exec())
+ mainWidget()->createGUI();
+}
+
+#include "mainactions.moc"
diff --git a/kftpgrabber/src/mainactions.h b/kftpgrabber/src/mainactions.h
new file mode 100644
index 0000000..908f1ce
--- /dev/null
+++ b/kftpgrabber/src/mainactions.h
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef MAINACTIONS_H
+#define MAINACTIONS_H
+
+#include <qobject.h>
+#include <kaction.h>
+
+class MainWindow;
+class KMainWindow;
+
+namespace KFTPWidgets {
+ class SystemTray;
+ class ConfigDialog;
+}
+
+/**
+ * This class contains the actions that are used in the main window,
+ * it's toolbars and in the menu bar.
+ *
+ * @author Jernej Kos
+ */
+class MainActions : public QObject
+{
+Q_OBJECT
+friend class MainWindow;
+friend class KFTPWidgets::SystemTray;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The main window instance
+ */
+ MainActions(MainWindow *parent);
+private:
+ // File menu
+ KAction *m_fileConnectAction;
+ KActionMenu *m_newSessionAction;
+
+ // Edit menu
+ KAction *m_renameAction;
+ KAction *m_deleteAction;
+ KAction *m_propsAction;
+ KAction *m_shredAction;
+
+ KAction *m_copyAction;
+ KAction *m_pasteAction;
+
+ KActionMenu *m_filterActions;
+ KAction *m_alwaysSkipAction;
+ KAction *m_topPriorityAction;
+ KAction *m_lowPriorityAction;
+
+ // Other
+ KActionMenu *m_transModeAction;
+ KRadioAction *m_modeAscii;
+ KRadioAction *m_modeBinary;
+ KRadioAction *m_modeAuto;
+
+ KFTPWidgets::ConfigDialog *m_configDialog;
+ KActionCollection *actionCollection();
+ KMainWindow *mainWidget();
+
+ bool m_closeApp;
+public slots:
+ void slotFileQuit();
+
+ void slotNewSessionLeft();
+ void slotNewSessionRight();
+
+ void slotConfigureToolbars();
+ void slotKeyBindings();
+ void slotSettingsSave();
+ void slotSettingsConfig();
+
+ void slotModeAscii();
+ void slotModeBinary();
+ void slotModeAuto();
+};
+
+#endif
diff --git a/kftpgrabber/src/mainwindow.cpp b/kftpgrabber/src/mainwindow.cpp
new file mode 100644
index 0000000..b77f0ea
--- /dev/null
+++ b/kftpgrabber/src/mainwindow.cpp
@@ -0,0 +1,447 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qtextedit.h>
+#include <qtabwidget.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qthread.h>
+
+#include <kapplication.h>
+#include <kmdimainfrm.h>
+#include <kmdichildview.h>
+#include <kmenubar.h>
+#include <kpopupmenu.h>
+#include <ktoolbar.h>
+#include <kiconloader.h>
+#include <kdialogbase.h>
+#include <kfiledetailview.h>
+#include <kmessagebox.h>
+#include <klineeditdlg.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kio/job.h>
+#include <kpassdlg.h>
+#include <ksslpkcs12.h>
+#include <kstandarddirs.h>
+
+#include "misc.h"
+#include "kftpapi.h"
+
+// Widgets
+#include "widgets/systemtray.h"
+#include "bookmarks/sidebar.h"
+#include "bookmarks/editor.h"
+#include "bookmarks/listview.h"
+#include "logview.h"
+#include "queueview/queueview.h"
+#include "queueview/threadview.h"
+#include "widgets/quickconnect.h"
+#include "kftpserverlineedit.h"
+#include "browser/view.h"
+#include "kftpzeroconflistview.h"
+#include "trafficgraph.h"
+#include "kftptabwidget.h"
+#include "failedtransfers.h"
+
+#include "sidebar.h"
+
+// Other KFTPGrabber stuff
+#include "misc/config.h"
+#include "misc/customcommands/manager.h"
+
+#include "mainwindow.h"
+#include "mainactions.h"
+#include "kftpbookmarks.h"
+#include "kftpqueue.h"
+#include "kftpsession.h"
+#include "kftpqueueconverter.h"
+#include "kftppluginmanager.h"
+#include "engine/thread.h"
+
+using namespace KFTPGrabberBase;
+
+MainWindow::MainWindow()
+ : KMainWindow()
+{
+ // Init the API
+ KFTPAPI::getInstance()->m_mainWindow = this;
+
+ // Set the shell's ui resource file
+ setXMLFile("kftpgrabberui.rc");
+ connect(KApplication::kApplication(), SIGNAL(shutDown()), this, SLOT(appShutdown()));
+
+ // Restore size and position
+ resize(KFTPCore::Config::size());
+ move(KFTPCore::Config::position());
+ setCaption("KFTPGrabber");
+
+ KFTPCore::Config::self()->postInit();
+
+ // Load plugins
+ KFTPAPI::getInstance()->pluginManager()->loadPlugins();
+
+ // Load custom commands
+ KFTPCore::CustomCommands::Manager::self()->load();
+
+ connect(KFTPQueue::Manager::self(), SIGNAL(queueUpdate()), this, SLOT(slotUpdateStatusBar()));
+ connect(KFTPBookmarks::Manager::self(), SIGNAL(update()), this, SLOT(initBookmarkMenu()));
+ connect(KFTPCore::Config::self(), SIGNAL(configChanged()), this, SLOT(slotConfigChanged()));
+
+ m_trafficGraph = 0L;
+ m_bookmarkMenu = 0L;
+ m_zeroconfMenu = 0L;
+ m_walletMenu = 0L;
+
+ // Init the gui system
+ initTrafficGraph();
+ initMainView();
+ initStatusBar();
+ initBookmarkMenu();
+
+ // Create the actions object
+ m_actions = new MainActions(this);
+
+ // Create the systray icon
+ new KFTPWidgets::SystemTray(this);
+
+ // Create base two sessions
+ KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::LeftSide);
+ KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::RightSide);
+
+ // Load bookmarks
+ QTimer::singleShot(500, this, SLOT(slotReadBookmarks()));
+
+ // Check for the uirc file
+ if (KGlobal::dirs()->findResource("appdata", xmlFile()) == QString::null) {
+ KMessageBox::error(0, i18n("<qt>Unable to find %1 XML GUI descriptor file. Please check that you have installed the application correctly! If you have any questions please ask on %2.<br><br><b>Warning:</b> Current GUI will be incomplete!</qt>").arg(xmlFile()).arg("irc.freenode.net/#kftpgrabber"));
+ }
+
+ createGUI(0);
+
+ // Auto-save toolbar/menubar/statusbar settings
+ setAutoSaveSettings(QString::fromLatin1("MainWindow"), false);
+}
+
+void MainWindow::slotReadBookmarks()
+{
+ // Load bookmarks and custom site commands
+ KFTPBookmarks::Manager::self()->load(getStoreDir("bookmarks.xml"));
+
+ // Load the saved queue
+ KFTPQueue::Manager::self()->getConverter()->importQueue(getStoreDir("queue"));
+
+ // Update the bookmark menu
+ initBookmarkMenu();
+}
+
+void MainWindow::appShutdown()
+{
+ KFTPQueue::Manager::self()->stopAllTransfers();
+ KFTPSession::Manager::self()->disconnectAllSessions();
+
+ // Save the queueview layout
+ m_queueView->saveLayout();
+
+ // Save the config data on shutdown
+ KFTPCore::Config::self()->saveConfig();
+
+ // Save current queue
+ KFTPQueue::Manager::self()->getConverter()->exportQueue(getStoreDir("queue"));
+}
+
+bool MainWindow::queryClose()
+{
+ if(KApplication::kApplication()->sessionSaving()) {
+ m_actions->m_closeApp = true;
+ }
+
+ if (!KFTPCore::Config::exitOnClose() && KFTPCore::Config::showSystrayIcon() && !m_actions->m_closeApp) {
+ /*
+ * This code was adopted from the Konversation project
+ * copyright: (C) 2003 by Dario Abatianni, Peter Simonsson
+ * email: eisfuchs@tigress.com, psn@linux.se
+ */
+
+ // Compute size and position of the pixmap to be grabbed:
+ QPoint g = KFTPWidgets::SystemTray::self()->mapToGlobal(KFTPWidgets::SystemTray::self()->pos());
+ int desktopWidth = kapp->desktop()->width();
+ int desktopHeight = kapp->desktop()->height();
+ int tw = KFTPWidgets::SystemTray::self()->width();
+ int th = KFTPWidgets::SystemTray::self()->height();
+ int w = desktopWidth / 4;
+ int h = desktopHeight / 9;
+ int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon
+ int y = g.y() + th/2 - h/2;
+ if ( x < 0 ) x = 0; // Move the rectangle to stay in the desktop limits
+ if ( y < 0 ) y = 0;
+ if ( x + w > desktopWidth ) x = desktopWidth - w;
+ if ( y + h > desktopHeight ) y = desktopHeight - h;
+
+ // Grab the desktop and draw a circle arround the icon:
+ QPixmap shot = QPixmap::grabWindow( qt_xrootwin(), x, y, w, h );
+ QPainter painter( &shot );
+ const int MARGINS = 6;
+ const int WIDTH = 3;
+ int ax = g.x() - x - MARGINS -1;
+ int ay = g.y() - y - MARGINS -1;
+ painter.setPen( QPen( Qt::red, WIDTH ) );
+ painter.drawArc( ax, ay, tw + 2*MARGINS, th + 2*MARGINS, 0, 16*360 );
+ painter.end();
+
+ // Associate source to image and show the dialog:
+ QMimeSourceFactory::defaultFactory()->setPixmap( "systray_shot", shot );
+ KMessageBox::information( this,
+ i18n( "<p>Closing the main window will keep KFTPGrabber running in the system tray. "
+ "Use <b>Quit</b> from the <b>KFTPGrabber</b> menu to quit the application.</p>"
+ "<p><center><img source=\"systray_shot\"></center></p>" ),
+ i18n( "Docking in System Tray" ), "HideMenuBarWarning" );
+ hide();
+
+ return false;
+ }
+
+ if (KFTPCore::Config::confirmExit() && KFTPQueue::Manager::self()->getNumRunning() > 0) {
+ if (KMessageBox::questionYesNo(0, i18n("There is currently a transfer running.",
+ "There are currently %n transfers running.",
+ KFTPQueue::Manager::self()->getNumRunning()) + i18n("\nAre you sure you want to quit?"),
+ i18n("Quit")) == KMessageBox::No)
+ {
+ return false;
+ }
+ }
+
+ // Save XML bookmarks here, because the user may be prompted for an encryption password
+ KFTPBookmarks::Manager::self()->save();
+
+ return true;
+}
+
+MainWindow::~MainWindow()
+{
+ delete m_bookmarkMenu;
+ delete m_zeroconfMenu;
+ delete m_walletMenu;
+}
+
+void MainWindow::initTrafficGraph()
+{
+ // Setup traffic graph
+ m_graphTimer = new QTimer(this);
+ connect(m_graphTimer, SIGNAL(timeout()), this, SLOT(slotUpdateTrafficGraph()));
+ m_graphTimer->start(1000);
+
+ // Create and configure the traffic graph
+ m_trafficGraph = new KFTPWidgets::TrafficGraph(0, "graph");
+ m_trafficGraph->setShowLabels(true);
+
+ m_trafficGraph->addBeam(QColor(255, 0, 0));
+ m_trafficGraph->addBeam(QColor(0, 0, 255));
+
+ m_trafficGraph->repaint();
+}
+
+void MainWindow::showBookmarkEditor()
+{
+ KFTPWidgets::Bookmarks::BookmarkEditor *bookmarkEditor = new KFTPWidgets::Bookmarks::BookmarkEditor(this, "bookmark editor");
+ bookmarkEditor->exec();
+ delete bookmarkEditor;
+
+ // Update the bookmarks globaly
+ KFTPBookmarks::Manager::self()->emitUpdate();
+}
+
+void MainWindow::initBookmarkMenu()
+{
+ // Bookmarks menu
+ if (!m_bookmarkMenu)
+ m_bookmarkMenu = new KActionMenu(i18n("Bookmarks"), 0, actionCollection(), "bookmarks");
+ else
+ m_bookmarkMenu->popupMenu()->clear();
+
+ // Zeroconf menu
+ if (!m_zeroconfMenu)
+ m_zeroconfMenu = new KActionMenu(i18n("FTP Sites Near Me"));
+
+ // Wallet menu
+ if (!m_walletMenu)
+ m_walletMenu = new KActionMenu(i18n("Sites In KWallet"), loadSmallIcon("wallet_open"));
+
+ m_bookmarkMenu->popupMenu()->insertItem(loadSmallIcon("bookmark"), i18n("Edit Bookmarks..."), 1);
+ m_bookmarkMenu->popupMenu()->connectItem(1, this, SLOT(showBookmarkEditor()));
+ m_bookmarkMenu->insert(m_zeroconfMenu);
+
+ if (KFTPCore::Config::showWalletSites())
+ m_bookmarkMenu->insert(m_walletMenu);
+
+ m_bookmarkMenu->popupMenu()->insertSeparator();
+
+ // Populate the bookmarks and zeroconf menus
+ KFTPBookmarks::Manager::self()->guiPopulateBookmarksMenu(m_bookmarkMenu);
+ KFTPBookmarks::Manager::self()->guiPopulateZeroconfMenu(m_zeroconfMenu);
+
+ if (KFTPCore::Config::showWalletSites())
+ KFTPBookmarks::Manager::self()->guiPopulateWalletMenu(m_walletMenu);
+}
+
+void MainWindow::initStatusBar()
+{
+ statusBar()->insertItem(i18n("idle"), 1, 1);
+ statusBar()->setItemAlignment(1, Qt::AlignLeft);
+
+ statusBar()->insertItem(i18n("Download: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getDownloadSpeed())), 2);
+ statusBar()->insertItem(i18n("Upload: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getUploadSpeed())), 3);
+}
+
+void MainWindow::initSidebars()
+{
+ // BEGIN bottom
+ m_bottomSidebar = new KFTPWidgets::Sidebar(centralWidget(), KFTPWidgets::Sidebar::Bottom);
+
+ // Create the queue
+ m_queueView = new KFTPWidgets::QueueView(0, "queue");
+
+ // Create the failed transfers view
+ KFTPWidgets::FailedTransfers *failedTransfers = new KFTPWidgets::FailedTransfers(0, "failedtransfers");
+
+ m_bottomSidebar->addSidebar(m_queueView, i18n("Queue"), "document");
+ m_bottomSidebar->addSidebar(failedTransfers, i18n("Failed Transfers"), "cancel");
+ // END bottom
+
+ // BEGIN left
+ m_leftSidebar = new KFTPWidgets::Sidebar(m_bottomSidebar->content(), KFTPWidgets::Sidebar::Left);
+ m_leftSidebar->setVisible(KFTPCore::Config::showLeftSidebar());
+
+ // Add the bookmarks sidebar
+ m_bookmarkSidebar = new KFTPWidgets::Bookmarks::Sidebar(0, "bookmarks");
+
+ // Add the zeroconf sidebar
+ m_zeroconfSidebar = new KFTPZeroConfListView(0, "zeroconf");
+
+ m_leftSidebar->addSidebar(m_bookmarkSidebar, i18n("Bookmarks"), "bookmark");
+ m_leftSidebar->addSidebar(m_zeroconfSidebar, i18n("Sites Near Me"), "lan");
+ // END left
+}
+
+void MainWindow::initMainView()
+{
+ setCentralWidget(new QWidget(this, "qt_central_widget"));
+
+ QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget(), 0, 0, "tLayout");
+
+ // Create the sidebars
+ initSidebars();
+
+ mainLayout->addWidget(m_bottomSidebar);
+
+ QSplitter *splitter = new QSplitter(m_leftSidebar->content());
+
+ // Session layouts
+ QWidget *box_1 = new QWidget(splitter);
+ QWidget *box_2 = new QWidget(splitter);
+
+ QHBoxLayout *lay_1 = new QHBoxLayout(box_1);
+ QHBoxLayout *lay_2 = new QHBoxLayout(box_2);
+
+ KFTPTabWidget *leftTabs = new KFTPTabWidget(box_1, "TabWidgetLeft");
+ KFTPTabWidget *rightTabs = new KFTPTabWidget(box_2, "TabWidgetRight");
+
+ leftTabs->setHoverCloseButton(true);
+ rightTabs->setHoverCloseButton(true);
+
+ lay_1->addWidget(leftTabs);
+ lay_2->addWidget(rightTabs);
+
+ // Create the session manager
+ new KFTPSession::Manager(this, new QTabWidget(0, "logs"), leftTabs, rightTabs);
+
+ // Add some sidebars that can't be added before the session manager is created
+ m_bottomSidebar->addSidebar(new KFTPWidgets::ThreadView(0, "threads"), i18n("Threads"), "socket");
+ m_bottomSidebar->addSidebar(KFTPSession::Manager::self()->getStatTabs(), i18n("Log"), "info");
+ m_bottomSidebar->addSidebar(m_trafficGraph, i18n("Traffic"), "share");
+}
+
+void MainWindow::slotUpdateStatusBar()
+{
+ // Status bar
+ statusBar()->changeItem(i18n("Download: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getDownloadSpeed())), 2);
+ statusBar()->changeItem(i18n("Upload: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getUploadSpeed())), 3);
+}
+
+void MainWindow::slotUpdateTrafficGraph()
+{
+ // Update the traffic graph
+ if (m_trafficGraph) {
+ QValueList<double> trafficList;
+ trafficList.append((double) KFTPQueue::Manager::self()->getDownloadSpeed() / 1024);
+ trafficList.append((double) KFTPQueue::Manager::self()->getUploadSpeed() / 1024);
+
+ m_trafficGraph->addSample(trafficList);
+ }
+}
+
+void MainWindow::slotQuickConnect(const QString &title, const QString &host, int port)
+{
+ // Create/get the new dialog
+ KFTPWidgets::QuickConnectDialog *quickConnect = new KFTPWidgets::QuickConnectDialog(this);
+
+ quickConnect->setCaption(title);
+ quickConnect->setHost(host);
+ quickConnect->setPort(port);
+
+ if (!host.isEmpty())
+ quickConnect->setFocusToUser();
+
+ if (quickConnect->exec() == KDialogBase::Accepted) {
+ // Get the url and connect
+ KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, quickConnect->getUrl());
+
+ // Configure the client socket
+ quickConnect->setupClient(session->getClient());
+ }
+
+ delete quickConnect;
+}
+
+void MainWindow::slotConfigChanged()
+{
+ m_leftSidebar->setVisible(KFTPCore::Config::showLeftSidebar());
+}
+
+#include "mainwindow.moc"
diff --git a/kftpgrabber/src/mainwindow.h b/kftpgrabber/src/mainwindow.h
new file mode 100644
index 0000000..c173a42
--- /dev/null
+++ b/kftpgrabber/src/mainwindow.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef MAINWINDOW_H_
+#define MAINWINDOW_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qtimer.h>
+
+#include <kmainwindow.h>
+#include <kaction.h>
+
+class KFTPZeroConfListView;
+class MainActions;
+
+namespace KFTPWidgets {
+namespace Bookmarks {
+ class Sidebar;
+}
+
+ class Sidebar;
+ class TrafficGraph;
+ class QueueView;
+}
+
+/**
+ * @short Application Main Window
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class MainWindow : public KMainWindow
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ */
+ MainWindow();
+
+ /**
+ * Class destructor.
+ */
+ ~MainWindow();
+
+ /**
+ * Get main actions instance.
+ *
+ * @return A valid instance of MainActions
+ */
+ MainActions *getActions() { return m_actions; }
+protected:
+ /**
+ * Initialize the traffic graph widget.
+ */
+ void initTrafficGraph();
+
+ /**
+ * Initialize the status bar.
+ */
+ void initStatusBar();
+
+ /**
+ * Create the main user interface.
+ */
+ void initMainView();
+
+ /**
+ * Initialize sidebars.
+ */
+ void initSidebars();
+
+ /**
+ * This method gets called when the user attempts to close the
+ * application.
+ */
+ bool queryClose();
+private:
+ QTimer *m_graphTimer;
+ MainActions *m_actions;
+
+ KActionMenu *m_bookmarkMenu;
+ KActionMenu *m_zeroconfMenu;
+ KActionMenu *m_walletMenu;
+
+ KFTPWidgets::Sidebar *m_leftSidebar;
+ KFTPWidgets::Sidebar *m_bottomSidebar;
+
+ KFTPWidgets::Bookmarks::Sidebar *m_bookmarkSidebar;
+
+ KFTPWidgets::TrafficGraph *m_trafficGraph;
+
+ KFTPZeroConfListView *m_zeroconfSidebar;
+ KFTPWidgets::QueueView *m_queueView;
+public slots:
+ void initBookmarkMenu();
+ void slotQuickConnect(const QString &title = QString::null, const QString &host = QString::null, int port = 21);
+private slots:
+ void appShutdown();
+ void showBookmarkEditor();
+ void slotUpdateStatusBar();
+ void slotReadBookmarks();
+ void slotUpdateTrafficGraph();
+ void slotConfigChanged();
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/Makefile.am b/kftpgrabber/src/misc/Makefile.am
new file mode 100644
index 0000000..8250ffc
--- /dev/null
+++ b/kftpgrabber/src/misc/Makefile.am
@@ -0,0 +1,23 @@
+INCLUDES = -I$(srcdir)/.. \
+ -I$(srcdir)/../engine \
+ -I$(srcdir)/../misc/interfaces \
+ $(all_includes)
+
+METASOURCES = AUTO
+noinst_LIBRARIES = libkftpgrabbercore.a
+libkftpgrabbercore_a_SOURCES = misc.cpp kftpotpgenerator.cpp \
+ kftpapi.cpp desencryptor.cpp kftpwalletconnection.cpp \
+ kftppluginmanager.cpp \
+ kftpzeroconf.cpp config.kcfgc configbase.cpp filter.cpp \
+ filterwidgethandler.cpp
+
+libkftpgrabbercore_a_LIBADD = interfaces/libkftpinterfaces.la customcommands/libcustomcommands.a
+
+kde_kcfg_DATA = kftpgrabber.kcfg
+
+noinst_HEADERS = misc.h kftpotpgenerator.h kftpapi.h desencryptor.h \
+ kftpwalletconnection.h kftppluginmanager.h kftpzeroconf.h \
+ configbase.h filter.h filterwidgethandler.h
+
+SUBDIRS = interfaces plugins libs customcommands
+
diff --git a/kftpgrabber/src/misc/config.kcfgc b/kftpgrabber/src/misc/config.kcfgc
new file mode 100644
index 0000000..d8ca269
--- /dev/null
+++ b/kftpgrabber/src/misc/config.kcfgc
@@ -0,0 +1,7 @@
+File=kftpgrabber.kcfg
+ClassName=Config
+Inherits=ConfigBase
+NameSpace=KFTPCore
+Singleton=true
+Mutators=true
+IncludeFiles=configbase.h
diff --git a/kftpgrabber/src/misc/configbase.cpp b/kftpgrabber/src/misc/configbase.cpp
new file mode 100644
index 0000000..39d58e4
--- /dev/null
+++ b/kftpgrabber/src/misc/configbase.cpp
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "configbase.h"
+#include "config.h"
+#include "kftpapi.h"
+#include "filter.h"
+
+#include <qregexp.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kemailsettings.h>
+#include <klocale.h>
+
+namespace KFTPCore {
+
+ConfigBase::ConfigBase(const QString &fileName)
+ : QObject(),
+ KConfigSkeleton(fileName)
+{
+ m_fileExistsDownActions.setTypeText(i18n("Download"));
+ m_fileExistsUpActions.setTypeText(i18n("Upload"));
+ m_fileExistsFxpActions.setTypeText(i18n("FXP"));
+
+ m_transMode = 'I';
+}
+
+void ConfigBase::postInit()
+{
+ // Restore the actions
+ QString tmp = Config::downloadActions();
+ tmp >> m_fileExistsDownActions;
+
+ tmp = Config::uploadActions();
+ tmp >> m_fileExistsUpActions;
+
+ tmp = Config::fxpActions();
+ tmp >> m_fileExistsFxpActions;
+}
+
+void ConfigBase::saveConfig()
+{
+ // Save actions before writing
+ QString tmp;
+ tmp << m_fileExistsDownActions;
+ Config::setDownloadActions(tmp);
+
+ tmp << m_fileExistsUpActions;
+ Config::setUploadActions(tmp);
+
+ tmp << m_fileExistsFxpActions;
+ Config::setFxpActions(tmp);
+
+ // Save the window's position
+ Config::setSize(KFTPAPI::getInstance()->mainWindow()->size());
+ Config::setPosition(KFTPAPI::getInstance()->mainWindow()->pos());
+
+ // Save filters
+ Filter::Filters::self()->save();
+
+ // Write the config
+ writeConfig();
+}
+
+void ConfigBase::emitChange()
+{
+ emit configChanged();
+}
+
+char ConfigBase::ftpMode(const QString &filename)
+{
+ // Get FTP mode (binary/ascii)
+ switch (m_transMode) {
+ case 'A': return 'A'; break;
+ case 'I': return 'I'; break;
+ case 'X':
+ default: {
+ char mode = 'I';
+ QRegExp e;
+ e.setWildcard(true);
+
+ QStringList list = Config::asciiList();
+ QStringList::iterator end(list.end());
+ for (QStringList::iterator i(list.begin()); i != end; ++i) {
+ e.setPattern((*i));
+
+ if (e.exactMatch(filename)) {
+ mode = 'A';
+ break;
+ }
+ }
+
+ return mode;
+ }
+ }
+}
+
+QString ConfigBase::getGlobalMail()
+{
+ KEMailSettings kes;
+ kes.setProfile(kes.defaultProfileName());
+ return kes.getSetting(KEMailSettings::EmailAddress);
+}
+
+}
+
+#include "configbase.moc"
diff --git a/kftpgrabber/src/misc/configbase.h b/kftpgrabber/src/misc/configbase.h
new file mode 100644
index 0000000..50e1d7a
--- /dev/null
+++ b/kftpgrabber/src/misc/configbase.h
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPCORECONFIGBASE_H
+#define KFTPCORECONFIGBASE_H
+
+#include <qobject.h>
+
+#include <kconfigskeleton.h>
+
+#include "kftpfileexistsactions.h"
+
+#define PRIO_SKIP 0
+#define PRIO_NOT_FOUND -999
+
+namespace KFTPCore {
+
+/**
+ * This is a base class for KFTPGrabber's configuration. It is inherited by
+ * auto-generated KConfigXT class KFTPCore::Config that adds all the configuration
+ * options.
+ *
+ * @author Jernej Kos
+ */
+class ConfigBase : public QObject, public KConfigSkeleton
+{
+Q_OBJECT
+public:
+ ConfigBase(const QString &fileName);
+
+ /**
+ * Does some post initialization stuff that couldn't be done in the constructor due
+ * to use of Config singleton.
+ */
+ void postInit();
+
+ /**
+ * Does some pre write stuff (eg. exporting the actions).
+ */
+ void saveConfig();
+
+ /**
+ * Returns a proper mode for the requested file. If the current mode is set to AUTO
+ * the list of ascii file patterns is consulted.
+ *
+ * @param filename The filename for which the mode should be returned
+ * @return A valid FTP transfer mode
+ */
+ char ftpMode(const QString &filename);
+
+ /**
+ * Set the global transfer mode.
+ *
+ * @param mode Transfer mode
+ */
+ void setGlobalMode(char mode) { m_transMode = mode; }
+
+ /**
+ * Get the global transfer mode.
+ *
+ * @return The transfer mode currently in use
+ */
+ char getGlobalMode() { return m_transMode; }
+
+ /**
+ * Get the download actions object.
+ *
+ * @return The FileExistsActions object for download actions
+ */
+ KFTPQueue::FileExistsActions *dActions() { return &m_fileExistsDownActions; }
+
+ /**
+ * Get the upload actions object.
+ *
+ * @return The FileExistsActions object for upload actions
+ */
+ KFTPQueue::FileExistsActions *uActions() { return &m_fileExistsUpActions; }
+
+ /**
+ * Get the fxp actions object.
+ *
+ * @return The FileExistsActions object for fxp actions
+ */
+ KFTPQueue::FileExistsActions *fActions() { return &m_fileExistsFxpActions; }
+public slots:
+ /**
+ * Emits the configChanged() signal.
+ */
+ void emitChange();
+protected:
+ QString getGlobalMail();
+private:
+ KFTPQueue::FileExistsActions m_fileExistsDownActions;
+ KFTPQueue::FileExistsActions m_fileExistsUpActions;
+ KFTPQueue::FileExistsActions m_fileExistsFxpActions;
+
+ char m_transMode;
+signals:
+ void configChanged();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/customcommands/Makefile.am b/kftpgrabber/src/misc/customcommands/Makefile.am
new file mode 100644
index 0000000..8436cf4
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = -I$(srcdir)/../.. \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../../engine \
+ -I$(srcdir)/../../misc/interfaces \
+ -I$(srcdir)/../../widgets \
+ $(all_includes)
+METASOURCES = AUTO
+noinst_LIBRARIES = libcustomcommands.a
+noinst_HEADERS = handlers.h entry.h manager.h parameterentrydialog.h \
+ responsedialog.h
+libcustomcommands_a_SOURCES = handlers.cpp entry.cpp manager.cpp \
+ parameterentrydialog.cpp responsedialog.cpp
+
+shellrcdir = $(kde_datadir)/kftpgrabber
+shellrc_DATA = commands.xml
+
diff --git a/kftpgrabber/src/misc/customcommands/commands.xml b/kftpgrabber/src/misc/customcommands/commands.xml
new file mode 100644
index 0000000..82bde8e
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/commands.xml
@@ -0,0 +1,113 @@
+<commands>
+ <category name="GlFTPd">
+ <category name="Information">
+ <entry name="Show current aliases">
+ <description>Shows current user aliases.</description>
+ <command>SITE ALIAS</command>
+ <response handler="Raw" display="Window">
+ <expected code="2" />
+ </response>
+ </entry>
+
+ <entry name="Show group info" icon="info">
+ <description>Shows group information.</description>
+ <command>SITE GRP %1</command>
+ <params>
+ <param type="String">Group name</param>
+ </params>
+ <response handler="Raw" display="Window">
+ <expected code="2" />
+ </response>
+ </entry>
+
+ <entry name="Show your idle time">
+ <description>Shows your current idle time.</description>
+ <command>SITE IDLE</command>
+ <response handler="Raw" display="Window">
+ <expected code="2" />
+ </response>
+ </entry>
+
+ <entry name="Show online users">
+ <description>Shows the users that are currently online.</description>
+ <command>SITE WHO</command>
+ <response handler="Raw" display="Window">
+ <expected code="2" />
+ </response>
+ </entry>
+
+ <entry name="Show welcome screen">
+ <description>Shows the welcome screen.</description>
+ <command>SITE WELCOME</command>
+ <response handler="Raw" display="Window">
+ <expected code="2" />
+ </response>
+ </entry>
+
+ <separator />
+
+ <entry name="Set your idle time">
+ <description>Sets your maximum idle time (in seconds).</description>
+ <command>SITE IDLE %1</command>
+ <params>
+ <param type="Integer">Idle time</param>
+ </params>
+ <response handler="Substitute" display="MessageBox">
+ <expected code="2" />
+ <text>Idle time successfully changed.</text>
+ </response>
+ </entry>
+
+ <entry name="Change your password" icon="password">
+ <description>Changes your account password.</description>
+ <command>SITE PASSWD %1</command>
+ <params>
+ <param type="Password">Password</param>
+ </params>
+ <response handler="Substitute" display="MessageBox">
+ <expected code="2" />
+ <text>Password successfully changed.</text>
+ </response>
+ </entry>
+ </category>
+
+ <category name="User Management">
+ <entry name="Delete a user" icon="editdelete">
+ <description>Removes a user from the server's user database.</description>
+ <command>SITE DELUSER %1</command>
+ <params>
+ <param type="String">Username</param>
+ </params>
+ <response handler="Substitute" display="MessageBox">
+ <expected code="2" />
+ <text>User successfully removed.</text>
+ </response>
+ </entry>
+
+ <entry name="Change user's password" icon="password">
+ <description>Changes a user's password.</description>
+ <command>SITE CHPASS %1 %2</command>
+ <params>
+ <param type="String">Username</param>
+ <param type="Password">New password</param>
+ </params>
+ <response handler="Substitute" display="MessageBox">
+ <expected code="2" />
+ <text>Password successfully changed.</text>
+ </response>
+ </entry>
+
+ <entry name="Kick a user from this server">
+ <description>Terminates all connections for the specified user.</description>
+ <command>SITE KICK %1</command>
+ <params>
+ <param type="String">Username</param>
+ </params>
+ <response handler="Substitute" display="MessageBox">
+ <expected code="2" />
+ <text>User has been kicked.</text>
+ </response>
+ </entry>
+ </category>
+ </category>
+</commands> \ No newline at end of file
diff --git a/kftpgrabber/src/misc/customcommands/entry.cpp b/kftpgrabber/src/misc/customcommands/entry.cpp
new file mode 100644
index 0000000..346f3cf
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/entry.cpp
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "entry.h"
+#include "kftpsession.h"
+#include "manager.h"
+
+#include "parameterentrydialog.h"
+#include "responsedialog.h"
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+Entry::Entry(QObject *parent, const QString &name)
+ : QObject(parent),
+ m_name(name)
+{
+}
+
+void Entry::appendParameter(ParameterType type, const QString &name)
+{
+ m_params.append(Parameter(type, name));
+}
+
+void Entry::setResponseHandler(const QString &handler, QDomNode args)
+{
+ m_handler = handler;
+ m_handlerArguments = args;
+}
+
+void Entry::execute(KFTPSession::Session *session)
+{
+ // Create a dialog for parameter input
+ QString command = m_command;
+
+ if (m_params.count() > 0) {
+ ParameterEntryDialog *dialog = new ParameterEntryDialog(this, m_params);
+ if (dialog->exec() != QDialog::Accepted) {
+ delete dialog;
+ return;
+ }
+
+ command = dialog->formatCommand(command);
+ delete dialog;
+ }
+
+ // Execute the command with proper parameters
+ m_lastSession = session;
+
+ connect(session->getClient()->eventHandler(), SIGNAL(gotRawResponse(const QString&)), this, SLOT(handleResponse(const QString&)));
+ session->getClient()->raw(command);
+}
+
+void Entry::handleResponse(const QString &response)
+{
+ if (!m_lastSession)
+ return;
+
+ m_lastSession->getClient()->eventHandler()->QObject::disconnect(this);
+ m_lastSession = 0;
+
+ // Invoke the proper handler
+ QString expectedReturn = m_handlerArguments.namedItem("expected").toElement().attribute("code");
+
+ if (!response.startsWith(expectedReturn)) {
+ KMessageBox::error(0, i18n("<qt>Requested operation has failed! Response from server is:<br/><br /><b>%1</b></qt>").arg(response));
+ return;
+ }
+
+ Handlers::Handler *handler = Manager::self()->handler(m_handler);
+
+ if (!handler) {
+ KMessageBox::error(0, i18n("<qt>Handler named <b>%1</b> can't be found for response parsing!</qt>").arg(m_handler));
+ return;
+ }
+
+ QString parsed = handler->handleResponse(response, m_handlerArguments);
+
+ // Find the proper way to display the parsed response
+ switch (m_displayType) {
+ case None: return;
+ case Window: {
+ ResponseDialog *dialog = new ResponseDialog(m_name, parsed);
+ dialog->exec();
+ delete dialog;
+ break;
+ }
+ case MessageBox: {
+ KMessageBox::information(0, parsed);
+ break;
+ }
+ }
+}
+
+Entry::Parameter::Parameter()
+ : m_type(String),
+ m_name("<invalid>")
+{
+}
+
+Entry::Parameter::Parameter(ParameterType type, const QString &name)
+ : m_type(type),
+ m_name(name)
+{
+}
+
+EntryAction::EntryAction(Entry *entry, KFTPSession::Session *session)
+ : KAction(entry->name(), entry->icon()),
+ m_entryInfo(entry),
+ m_session(session)
+{
+}
+
+}
+
+}
+
+#include "entry.moc"
+
diff --git a/kftpgrabber/src/misc/customcommands/entry.h b/kftpgrabber/src/misc/customcommands/entry.h
new file mode 100644
index 0000000..e7c82c4
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/entry.h
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_CUSTOMCOMMANDSENTRY_H
+#define KFTPCORE_CUSTOMCOMMANDSENTRY_H
+
+#include <qdom.h>
+#include <qvaluelist.h>
+
+#include <kaction.h>
+
+namespace KFTPSession {
+ class Session;
+}
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+/**
+ * This class represents a single custom command entry. A tree of
+ * such objects is constructed from an XML file.
+ *
+ * @author Jernej Kos
+ */
+class Entry : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Possible parameter types.
+ */
+ enum ParameterType {
+ String,
+ Password,
+ Integer
+ };
+
+ /**
+ * Possible display types.
+ */
+ enum DisplayType {
+ None,
+ Window,
+ MessageBox
+ };
+
+ /**
+ * A single command parameter.
+ */
+ class Parameter {
+ public:
+ /**
+ * Class constructor.
+ */
+ Parameter();
+
+ /**
+ * Class constructor.
+ *
+ * @param type Parameter type
+ * @param name Parameter name
+ */
+ Parameter(ParameterType type, const QString &name);
+
+ /**
+ * Returns the parameter type.
+ */
+ ParameterType type() const { return m_type; }
+
+ /**
+ * Returns the parameter name.
+ */
+ QString name() const { return m_name; }
+ private:
+ ParameterType m_type;
+ QString m_name;
+ };
+
+ /**
+ * Class constructor.
+ *
+ * @param name Short entry name
+ */
+ Entry(QObject *parent, const QString &name);
+
+ /**
+ * Returns entry's name.
+ */
+ QString name() const { return m_name; }
+
+ /**
+ * Returns entry's description.
+ */
+ QString description() const { return m_description; }
+
+ /**
+ * Returns entry's icon name.
+ */
+ QString icon() const { return m_icon; }
+
+ /**
+ * Sets entry's description.
+ *
+ * @param description A longer entry description; can be rich text
+ */
+ void setDescription(const QString &description) { m_description = description; }
+
+ /**
+ * Set entry's icon.
+ *
+ * @param icon An icon name
+ */
+ void setIcon(const QString &icon) { m_icon = icon; }
+
+ /**
+ * Sets the raw command to be sent.
+ *
+ * @param command A valid FTP command with optional parameter placeholders
+ */
+ void setCommand(const QString &command) { m_command = command; }
+
+ /**
+ * Appends a command parameter.
+ *
+ * @param type Parameter type
+ * @param name Human readable parameter name
+ */
+ void appendParameter(ParameterType type, const QString &name);
+
+ /**
+ * Sets response display type.
+ *
+ * @param type Display type
+ */
+ void setDisplayType(DisplayType type) { m_displayType = type; }
+
+ /**
+ * Sets the response handler to use.
+ *
+ * @param handler Handler name
+ * @param args Optional argument node
+ */
+ void setResponseHandler(const QString &handler, QDomNode args);
+
+ /**
+ * Executes this entry. This will actually generate and show a proper
+ * user input dialog, execute the command with the provided parameters,
+ * pass the raw response to a selected handler and properly display
+ * the result.
+ *
+ * @param session A remote session where command should be executed
+ */
+ void execute(KFTPSession::Session *session);
+private slots:
+ void handleResponse(const QString &response);
+private:
+ QString m_name;
+ QString m_description;
+ QString m_icon;
+ QString m_command;
+ QString m_handler;
+ DisplayType m_displayType;
+
+ QValueList<Parameter> m_params;
+ QDomNode m_handlerArguments;
+
+ KFTPSession::Session *m_lastSession;
+};
+
+/**
+ * This class is a wrapper action, so a proper entry gets pulled and
+ * executed.
+ *
+ * @author Jernej Kos
+ */
+class EntryAction : public KAction {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param entry Associated entry
+ * @param session Associated session
+ */
+ EntryAction(Entry *entry, KFTPSession::Session *session);
+
+ /**
+ * Returns the associated entry instance.
+ */
+ Entry *entryInfo() const { return m_entryInfo; }
+
+ /**
+ * Returns the associated session instance.
+ */
+ KFTPSession::Session *session() const { return m_session; }
+private:
+ Entry *m_entryInfo;
+ KFTPSession::Session *m_session;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/customcommands/handlers.cpp b/kftpgrabber/src/misc/customcommands/handlers.cpp
new file mode 100644
index 0000000..8cf5349
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/handlers.cpp
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "handlers.h"
+
+#include <qdom.h>
+#include <qregexp.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+namespace Handlers {
+
+Handler::Handler(const QString &name)
+ : m_name(name)
+{
+}
+
+RawHandler::RawHandler()
+ : Handler("Raw")
+{
+}
+
+SubstituteHandler::SubstituteHandler()
+ : Handler("Substitue")
+{
+}
+
+QString SubstituteHandler::handleResponse(const QString &raw, QDomNode arguments) const
+{
+ QString text = arguments.namedItem("text").toElement().text();
+
+ if (text.contains("%1"))
+ return text.arg(raw);
+
+ return text;
+}
+
+RegexpHandler::RegexpHandler()
+ : Handler("Regexp")
+{
+}
+
+QString RegexpHandler::handleResponse(const QString &raw, QDomNode arguments) const
+{
+ QString result;
+ QRegExp e(arguments.namedItem("match").toElement().text());
+
+ if (e.exactMatch(raw.stripWhiteSpace())) {
+ result = arguments.namedItem("display").toElement().text();
+
+ for (int i = 1; i <= e.numCaptures(); i++) {
+ result.replace(QString("\\%1").arg(i), e.cap(i));
+ }
+ }
+
+ return result;
+}
+
+}
+
+}
+
+}
diff --git a/kftpgrabber/src/misc/customcommands/handlers.h b/kftpgrabber/src/misc/customcommands/handlers.h
new file mode 100644
index 0000000..f41a3de
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/handlers.h
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_CUSTOMCOMMANDS_HANDLERSHANDLERS_H
+#define KFTPCORE_CUSTOMCOMMANDS_HANDLERSHANDLERS_H
+
+#include <qstring.h>
+#include <qdom.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+namespace Handlers {
+
+/**
+ * The handler class is an abstract class which every actual handler
+ * must implement.
+ *
+ * @author Jernej Kos
+ */
+class Handler {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param name Handler name
+ */
+ Handler(const QString &name);
+
+ /**
+ * Returns the handler's name.
+ */
+ QString name() const { return m_name; }
+
+ /**
+ * This method should be implemented by actual handlers to handler the
+ * server response.
+ *
+ * @param raw Raw FTP response
+ * @param arguments Any argument nodes supplied in the XML file
+ * @return This method should return a formatted string
+ */
+ virtual QString handleResponse(const QString &raw, QDomNode arguments) const = 0;
+private:
+ QString m_name;
+};
+
+/**
+ * The Raw handler accepts no arguments and simply passes on raw data.
+ *
+ * @author Jernej Kos
+ */
+class RawHandler : public Handler {
+public:
+ /**
+ * Class constructor.
+ */
+ RawHandler();
+
+ /**
+ * @overload
+ * Reimplemented from Handler.
+ */
+ QString handleResponse(const QString &raw, QDomNode) const { return raw; }
+};
+
+/**
+ * The Substitue handler always returns a predefined value when the
+ * operation is completed successfully. %1 can be used in place of the
+ * raw data received from the server.
+ *
+ * @author Jernej Kos
+ */
+class SubstituteHandler : public Handler {
+public:
+ /**
+ * Class constructor.
+ */
+ SubstituteHandler();
+
+ /**
+ * @overload
+ * Reimplemented from Handler.
+ */
+ QString handleResponse(const QString &raw, QDomNode arguments) const;
+};
+
+/**
+ * The Regexp handler enables custom response parsing using regular
+ * expressions.
+ *
+ * @author Jernej Kos
+ */
+class RegexpHandler : public Handler {
+public:
+ /**
+ * Class constructor.
+ */
+ RegexpHandler();
+
+ /**
+ * @overload
+ * Reimplemented from Handler.
+ */
+ QString handleResponse(const QString &raw, QDomNode arguments) const;
+};
+
+}
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/customcommands/manager.cpp b/kftpgrabber/src/misc/customcommands/manager.cpp
new file mode 100644
index 0000000..1cda06a
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/manager.cpp
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "manager.h"
+#include "entry.h"
+
+#include <qfile.h>
+
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+Manager *Manager::m_self = 0;
+static KStaticDeleter<Manager> staticManagerDeleter;
+
+Manager *Manager::self()
+{
+ if (!m_self) {
+ staticManagerDeleter.setObject(m_self, new Manager());
+ }
+
+ return m_self;
+}
+
+Manager::Manager()
+ : QObject()
+{
+ // Populate the handlers list
+ m_handlers["Raw"] = new Handlers::RawHandler();
+ m_handlers["Substitute"] = new Handlers::SubstituteHandler();
+ m_handlers["Regexp"] = new Handlers::RegexpHandler();
+}
+
+Manager::~Manager()
+{
+ if (m_self == this)
+ staticManagerDeleter.setObject(m_self, 0, false);
+
+ // Destroy the handlers
+ delete static_cast<Handlers::RawHandler*>(m_handlers["Raw"]);
+ delete static_cast<Handlers::SubstituteHandler*>(m_handlers["Substitute"]);
+ delete static_cast<Handlers::RegexpHandler*>(m_handlers["Regexp"]);
+
+ m_handlers.clear();
+}
+
+void Manager::load()
+{
+ QString filename = locateLocal("appdata", "commands.xml");
+
+ if (!QFile::exists(filename)) {
+ // Copy the default command set over
+ QFile source(locate("appdata", "commands.xml"));
+ QFile destination(filename);
+
+ source.open(IO_ReadOnly);
+ destination.open(IO_WriteOnly | IO_Truncate);
+
+ destination.writeBlock(source.readAll());
+ source.close();
+ destination.close();
+ }
+
+ QFile file(filename);
+ if (!file.open(IO_ReadOnly))
+ return;
+
+ m_document.setContent(&file);
+ file.close();
+}
+
+void Manager::parseEntries(KActionMenu *parentMenu, const QDomNode &parentNode, KFTPSession::Session *session) const
+{
+ QDomNode n = parentNode.firstChild();
+
+ while (!n.isNull()) {
+ if (n.isElement()) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+ QString name = e.attribute("name");
+
+ if (tagName == "category") {
+ KActionMenu *menu = new KActionMenu(name, "folder", parentMenu);
+ parentMenu->insert(menu);
+
+ // Recurse into this category
+ parseEntries(menu, n, session);
+ } else if (tagName == "entry") {
+ Entry *entry = new Entry((Manager*) this, name);
+ entry->setDescription(n.namedItem("description").toElement().text());
+ entry->setIcon(e.attribute("icon"));
+ entry->setCommand(n.namedItem("command").toElement().text());
+
+ QDomNode p = n.namedItem("params").firstChild();
+ while (!p.isNull()) {
+ QDomElement pElement = p.toElement();
+
+ if (pElement.tagName() == "param") {
+ QString typeString = pElement.attribute("type");
+ Entry::ParameterType type = Entry::String;
+
+ if (typeString == "String")
+ type = Entry::String;
+ else if (typeString == "Password")
+ type = Entry::Password;
+ else if (typeString == "Integer")
+ type = Entry::Integer;
+
+ entry->appendParameter(type, pElement.text());
+ }
+
+ p = p.nextSibling();
+ }
+
+ QDomElement rElement = n.namedItem("response").toElement();
+ entry->setResponseHandler(rElement.attribute("handler"), rElement);
+
+ QString displayString = rElement.attribute("display");
+ Entry::DisplayType displayType = Entry::None;
+
+ if (displayString == "None")
+ displayType = Entry::None;
+ else if (displayString == "Window")
+ displayType = Entry::Window;
+ else if (displayString == "MessageBox")
+ displayType = Entry::MessageBox;
+
+ entry->setDisplayType(displayType);
+
+ // Create a new action
+ EntryAction *action = new EntryAction(entry, session);
+ connect(action, SIGNAL(activated()), this, SLOT(slotActionActivated()));
+
+ parentMenu->insert(action);
+ } else if (tagName == "separator") {
+ parentMenu->popupMenu()->insertSeparator();
+ } else {
+ KMessageBox::error(0, i18n("Unknown tag while parsing custom site commands!"));
+ }
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+Handlers::Handler *Manager::handler(const QString &name) const
+{
+ if (m_handlers.contains(name))
+ return m_handlers[name];
+
+ return 0;
+}
+
+KActionMenu *Manager::categories(const QString &name, KFTPSession::Session *session) const
+{
+ KActionMenu *actionMenu = new KActionMenu(name);
+ parseEntries(actionMenu, m_document.documentElement(), session);
+
+ return actionMenu;
+}
+
+void Manager::slotActionActivated()
+{
+ EntryAction *action = (EntryAction*) QObject::sender();
+ action->entryInfo()->execute(action->session());
+}
+
+}
+
+}
+
+#include "manager.moc"
+
diff --git a/kftpgrabber/src/misc/customcommands/manager.h b/kftpgrabber/src/misc/customcommands/manager.h
new file mode 100644
index 0000000..db5dd6d
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/manager.h
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_CUSTOMCOMMANDSMANAGER_H
+#define KFTPCORE_CUSTOMCOMMANDSMANAGER_H
+
+#include <qobject.h>
+#include <qdom.h>
+#include <qmap.h>
+
+#include <kaction.h>
+
+#include "handlers.h"
+
+namespace KFTPSession {
+ class Session;
+}
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+/**
+ * This class represents the entry manager. It parses the XML file and
+ * creates the necessary entries. This class is a singleton.
+ *
+ * @author Jernej Kos
+ */
+class Manager : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Class destructor.
+ */
+ ~Manager();
+
+ /**
+ * Returns a Manager instance.
+ */
+ static Manager *self();
+
+ /**
+ * Returns an appropriate handler.
+ *
+ * @param name Handler string identifier
+ */
+ Handlers::Handler *handler(const QString &name) const;
+
+ /**
+ * Returns a KActionMenu that can be used for displaying categories of
+ * entries to the user.
+ */
+ KActionMenu *categories(const QString &name, KFTPSession::Session *session) const;
+public slots:
+ /**
+ * Loads a valid commands XML file.
+ */
+ void load();
+protected:
+ static Manager *m_self;
+
+ /**
+ * Class constructor.
+ */
+ Manager();
+
+ /**
+ * Recursive entry parser.
+ */
+ void parseEntries(KActionMenu *parentMenu, const QDomNode &parentNode, KFTPSession::Session *session) const;
+protected slots:
+ /**
+ * This slot gets called when an EntryAction is activated.
+ */
+ void slotActionActivated();
+private:
+ KActionMenu *m_categories;
+ QDomDocument m_document;
+ QMap<QString, Handlers::Handler*> m_handlers;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/customcommands/parameterentrydialog.cpp b/kftpgrabber/src/misc/customcommands/parameterentrydialog.cpp
new file mode 100644
index 0000000..808b400
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/parameterentrydialog.cpp
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "parameterentrydialog.h"
+#include "entry.h"
+
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <knuminput.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+ParameterEntryDialog::ParameterEntryDialog(Entry *entry, QValueList<Entry::Parameter> params)
+ : KDialogBase(0, "", true, entry->name(), Ok|Cancel, Ok),
+ m_params(params)
+{
+ QFrame *mainWidget = makeMainWidget();
+ QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget);
+
+ QHBoxLayout *headerLayout = new QHBoxLayout(mainWidget);
+ QLabel *icon = new QLabel(mainWidget);
+ icon->setPixmap(DesktopIcon(entry->icon(), 32));
+ headerLayout->addWidget(icon);
+
+ QVBoxLayout *headerTextLayout = new QVBoxLayout(mainWidget);
+ headerTextLayout->addWidget(new QLabel(QString("<b>%1</b>").arg(entry->name()), mainWidget));
+ headerTextLayout->addWidget(new QLabel(entry->description(), mainWidget));
+ headerLayout->addLayout(headerTextLayout, 1);
+
+ mainLayout->addLayout(headerLayout);
+ mainLayout->addSpacing(5);
+
+ QFrame *frame = new QFrame(mainWidget);
+ frame->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
+
+ QVBoxLayout *frameLayout = new QVBoxLayout(frame);
+ frameLayout->setMargin(10);
+ mainLayout->addWidget(frame);
+
+ int num = 0;
+ QValueList<Entry::Parameter>::ConstIterator lend = params.end();
+ for (QValueList<Entry::Parameter>::ConstIterator i = params.begin(); i != lend; ++i) {
+ QHBoxLayout *layout = new QHBoxLayout(frame);
+ QWidget *entryWidget = 0;
+ QString name = QString("param_%1").arg(num++);
+
+ switch ((*i).type()) {
+ case Entry::String: entryWidget = new KLineEdit(frame, name.ascii()); break;
+ case Entry::Password: entryWidget = new KPasswordEdit(frame, name.ascii()); break;
+ case Entry::Integer: entryWidget = new KIntNumInput(frame, name.ascii()); break;
+ }
+
+ // The first widget should have focus
+ if (num == 1)
+ entryWidget->setFocus();
+
+ layout->addWidget(new QLabel((*i).name() + ":", frame));
+ layout->addStretch(1);
+ layout->addWidget(entryWidget);
+ frameLayout->addLayout(layout);
+ frameLayout->addSpacing(5);
+ }
+
+ setMaximumWidth(350);
+ resize(350, minimumHeight());
+}
+
+QString ParameterEntryDialog::formatCommand(const QString &command)
+{
+ QString tmp = command;
+
+ int num = 0;
+ QValueList<Entry::Parameter>::ConstIterator lend = m_params.end();
+ for (QValueList<Entry::Parameter>::ConstIterator i = m_params.begin(); i != lend; ++i) {
+ QObject *entryWidget = child(QString("param_%1").arg(num++).ascii());
+
+ switch ((*i).type()) {
+ case Entry::String: tmp = tmp.arg(static_cast<KLineEdit*>(entryWidget)->text()); break;
+ case Entry::Password: tmp = tmp.arg(static_cast<KPasswordEdit*>(entryWidget)->password()); break;
+ case Entry::Integer: tmp = tmp.arg(static_cast<KIntNumInput*>(entryWidget)->value()); break;
+ }
+ }
+
+ return tmp;
+}
+
+}
+
+}
+
+#include "parameterentrydialog.moc"
+
diff --git a/kftpgrabber/src/misc/customcommands/parameterentrydialog.h b/kftpgrabber/src/misc/customcommands/parameterentrydialog.h
new file mode 100644
index 0000000..3a7d242
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/parameterentrydialog.h
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_CUSTOMCOMMANDSPARAMETERENTRYDIALOG_H
+#define KFTPCORE_CUSTOMCOMMANDSPARAMETERENTRYDIALOG_H
+
+#include <kdialogbase.h>
+#include <qvaluelist.h>
+
+#include "entry.h"
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+/**
+ * A dialog for parameter entry.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class ParameterEntryDialog : public KDialogBase {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param entry An entry instance
+ * @param params Parameter list
+ */
+ ParameterEntryDialog(Entry *entry, QValueList<Entry::Parameter> params);
+
+ /**
+ * Properly replaces the parameter placeholders with actual user
+ * entered values.
+ *
+ * @param command A command string with placeholders
+ */
+ QString formatCommand(const QString &command);
+private:
+ QValueList<Entry::Parameter> m_params;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/customcommands/responsedialog.cpp b/kftpgrabber/src/misc/customcommands/responsedialog.cpp
new file mode 100644
index 0000000..11970f9
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/responsedialog.cpp
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "responsedialog.h"
+
+#include <ktextedit.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+ResponseDialog::ResponseDialog(const QString &caption, const QString &response)
+ : KDialogBase(0, "", true, caption, Ok)
+{
+ KTextEdit *contents = new KTextEdit(this);
+ setMainWidget(contents);
+
+ contents->setTextFormat(Qt::LogText);
+ contents->unsetPalette();
+ contents->append(response);
+
+ setMinimumSize(500, 300);
+}
+
+}
+
+}
+
diff --git a/kftpgrabber/src/misc/customcommands/responsedialog.h b/kftpgrabber/src/misc/customcommands/responsedialog.h
new file mode 100644
index 0000000..ab370ee
--- /dev/null
+++ b/kftpgrabber/src/misc/customcommands/responsedialog.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_CUSTOMCOMMANDSRESPONSEDIALOG_H
+#define KFTPCORE_CUSTOMCOMMANDSRESPONSEDIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KFTPCore {
+
+namespace CustomCommands {
+
+/**
+ * A dialog for displaying operation results.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class ResponseDialog : public KDialogBase {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param caption Dialog caption
+ * @param response Response text (can be rich-text)
+ */
+ ResponseDialog(const QString &caption, const QString &response);
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/desencryptor.cpp b/kftpgrabber/src/misc/desencryptor.cpp
new file mode 100644
index 0000000..d53fd5e
--- /dev/null
+++ b/kftpgrabber/src/misc/desencryptor.cpp
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "desencryptor.h"
+
+#include <kmdcodec.h>
+#include <qfile.h>
+
+namespace KFTPGrabberBase {
+
+DESEncryptor::DESEncryptor()
+{
+}
+
+void DESEncryptor::DESExec(const QString &str, int mode)
+{
+ QCString string(str.ascii());
+ m_output = "";
+
+ QByteArray in;
+ QByteArray out;
+
+ if (mode == DES_DECRYPT) {
+ // Base64 decode the output
+ KCodecs::base64Decode(string, in);
+ } else {
+ in.resize(string.size());
+ memcpy(in.data(), string.data(), in.size());
+ }
+
+ // Read 8 chars at a time and encrypt/decrypt them
+ unsigned int i = 0;
+ char *o_message = (char*) malloc(8);
+ char *o_output = (char*) malloc(8);
+
+ memset(o_message, 0, 9);
+ memset(o_output, 0, 9);
+ //out.resize(in.size() * 2);
+
+ while (i < in.size()) {
+ out.resize(out.size() + 8);
+ memcpy(o_message, in.data() + i, 8);
+ DES_ecb_encrypt((DES_cblock *) o_message, (DES_cblock*) o_output, &m_schedule, mode);
+ memcpy(out.data() + i, o_output, 8);
+
+ // Next 8 bytes
+ i += 8;
+
+ // Clear everything
+ memset(o_message, 0, 9);
+ memset(o_output, 0, 9);
+ }
+
+ free(o_message);
+ free(o_output);
+
+ if (mode == DES_ENCRYPT) {
+ // Base64 encode the output
+ m_output = KCodecs::base64Encode(out).data();
+ } else {
+ m_output = QCString(out).data();
+ }
+}
+
+void DESEncryptor::setKey(const QString &key)
+{
+ // Setup our DES key
+ DES_string_to_key(key.ascii(), &m_key);
+ DES_set_key_checked(&m_key, &m_schedule);
+}
+
+void DESEncryptor::encrypt(const QString &string)
+{
+ DESExec(string, DES_ENCRYPT);
+}
+
+void DESEncryptor::decrypt(const QString &string)
+{
+ DESExec(string, DES_DECRYPT);
+}
+
+QString DESEncryptor::output()
+{
+ return m_output;
+}
+
+}
diff --git a/kftpgrabber/src/misc/desencryptor.h b/kftpgrabber/src/misc/desencryptor.h
new file mode 100644
index 0000000..bf9ab68
--- /dev/null
+++ b/kftpgrabber/src/misc/desencryptor.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPGRABBERBASEDESENCRYPTOR_H
+#define KFTPGRABBERBASEDESENCRYPTOR_H
+
+/* OpenSSL includes */
+#include <openssl/des.h>
+
+#include <qstring.h>
+
+namespace KFTPGrabberBase {
+
+/**
+Class for encrypting text and files using OpenSSL's DES encryption.
+
+@author Jernej Kos
+*/
+class DESEncryptor{
+public:
+ DESEncryptor();
+
+ void setKey(const QString &key);
+ void encrypt(const QString &string);
+ void decrypt(const QString &string);
+
+ QString output();
+private:
+ DES_cblock m_key;
+ DES_key_schedule m_schedule;
+
+ QString m_output;
+
+ void DESExec(const QString &str, int mode);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/filter.cpp b/kftpgrabber/src/misc/filter.cpp
new file mode 100644
index 0000000..605eba7
--- /dev/null
+++ b/kftpgrabber/src/misc/filter.cpp
@@ -0,0 +1,421 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "filter.h"
+
+#include <qregexp.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <kconfig.h>
+
+namespace KFTPCore {
+
+namespace Filter {
+
+Condition::Condition(Field field, Type type, const QVariant &value)
+ : m_field(field),
+ m_type(type),
+ m_value(value)
+{
+}
+
+bool Condition::matches(const KFTPEngine::DirectoryEntry &entry) const
+{
+ bool result = false;
+ QString check;
+
+ switch (m_field) {
+ default:
+ case Filename: check = entry.filename(); break;
+ case EntryType: check = entry.type(); break;
+ case Size: check = QString::number(entry.size()); break;
+ }
+
+ switch (m_type) {
+ case None: result = false; break;
+
+ case Contains: result = (check.contains(m_value.toString()) > 0); break;
+ case ContainsNot: result = (check.contains(m_value.toString()) == 0); break;
+
+ case Is: result = (check == m_value.toString()); break;
+ case IsNot: result = (check != m_value.toString()); break;
+
+ case Matches: {
+ QRegExp r(m_value.toString());
+ result = (r.search(check) > -1);
+ break;
+ }
+ case MatchesNot: {
+ QRegExp r(m_value.toString());
+ result = (r.search(check) == -1);
+ break;
+ }
+
+ case Greater: result = (check.toULongLong() > m_value.toULongLong()); break;
+ case Smaller: result = (check.toULongLong() < m_value.toULongLong()); break;
+ }
+
+ return result;
+}
+
+ConditionChain::ConditionChain()
+ : QPtrList<Condition>(),
+ m_type(All)
+{
+ setAutoDelete(true);
+}
+
+ConditionChain::ConditionChain(Type type)
+ : QPtrList<Condition>(),
+ m_type(type)
+{
+ setAutoDelete(true);
+}
+
+bool ConditionChain::matches(const KFTPEngine::DirectoryEntry &entry) const
+{
+ if (isEmpty())
+ return false;
+
+ QPtrList<Condition>::ConstIterator le = end();
+ for (QPtrList<Condition>::ConstIterator i = begin(); i != le; ++i) {
+ bool match = (*i)->matches(entry);
+
+ if (match && m_type == Any)
+ return true;
+ else if (!match && m_type == All)
+ return false;
+ }
+
+ if (m_type == Any)
+ return false;
+
+ return true;
+}
+
+Action::Action()
+ : m_valid(false)
+{
+}
+
+Action::Action(Type type, const QVariant &value)
+ : m_valid(true),
+ m_type(type),
+ m_value(value)
+{
+}
+
+ActionChain::ActionChain()
+ : QPtrList<Action>()
+{
+ setAutoDelete(true);
+}
+
+const Action *ActionChain::getAction(Action::Type type) const
+{
+ ActionChain::ConstIterator le = end();
+ for (ActionChain::ConstIterator i = begin(); i != le; ++i)
+ if ((*i)->type() == type)
+ return (*i);
+
+ return 0;
+}
+
+Rule::Rule()
+ : m_name(QString::null),
+ m_enabled(false)
+{
+}
+
+Rule::Rule(const Rule *rule)
+ : m_name(rule->name()),
+ m_enabled(rule->isEnabled())
+{
+ // Copy conditions
+ const ConditionChain *conditionList = rule->conditions();
+ m_conditionChain.setType(conditionList->type());
+
+ ConditionChain::ConstIterator cle = conditionList->end();
+ for (ConditionChain::ConstIterator i = conditionList->begin(); i != cle; ++i) {
+ const Condition *c = (*i);
+
+ m_conditionChain.append(new Condition(c->field(), c->type(), c->value()));
+ }
+
+ // Copy actions
+ const ActionChain *actionList = rule->actions();
+
+ ActionChain::ConstIterator ale = actionList->end();
+ for (ActionChain::ConstIterator i = actionList->begin(); i != ale; ++i) {
+ const Action *a = (*i);
+
+ m_actionChain.append(new Action(a->type(), a->value()));
+ }
+}
+
+Rule::Rule(const QString &name)
+ : m_name(name),
+ m_enabled(true)
+{
+ // Add a simple condition and a simple action
+ m_conditionChain.append(new Condition(Filename, Condition::Contains, QVariant("")));
+ m_actionChain.append(new Action(Action::None, QVariant()));
+}
+
+Filters *Filters::m_self = 0;
+static KStaticDeleter<Filters> staticFiltersDeleter;
+
+Filters *Filters::self()
+{
+ if (!m_self) {
+ staticFiltersDeleter.setObject(m_self, new Filters());
+ }
+
+ return m_self;
+}
+
+Filters::Filters()
+ : QPtrList<Rule>(),
+ m_enabled(true)
+{
+ setAutoDelete(true);
+
+ // Generate human readable strings
+ m_fieldNames << i18n("Filename");
+ m_fieldNames << i18n("Entry Type");
+ m_fieldNames << i18n("Size");
+
+ m_actionNames << " ";
+ m_actionNames << i18n("Change priority");
+ m_actionNames << i18n("Skip when queuing");
+ m_actionNames << i18n("Colorize in list view");
+ m_actionNames << i18n("Hide from list view");
+ m_actionNames << i18n("Lowercase destination");
+
+ // Load the filters
+ load();
+}
+
+Filters::~Filters()
+{
+ if (m_self == this)
+ staticFiltersDeleter.setObject(m_self, 0, false);
+}
+
+void Filters::save()
+{
+ int num = 0;
+ KConfig *config = kapp->config();
+
+ config->setGroup("Filters");
+ config->writeEntry("count", count());
+
+ // Remove any existing sections
+ for (int i = 0; ; i++) {
+ QString groupName = QString("Filter #%1").arg(i);
+
+ if (config->hasGroup(groupName))
+ config->deleteGroup(groupName);
+ else
+ break;
+ }
+
+ Filters::ConstIterator le = end();
+ for (Filters::ConstIterator i = begin(); i != le; ++i, num++) {
+ const Rule *rule = (*i);
+
+ config->setGroup(QString("Filter #%1").arg(num));
+ config->writeEntry("name", rule->name());
+ config->writeEntry("enabled", rule->isEnabled());
+
+ // Write conditions
+ int cnum = 0;
+ const ConditionChain *conditions = rule->conditions();
+ config->writeEntry("conditions", conditions->count());
+ config->writeEntry("conditions-type", (int) conditions->type());
+
+ ConditionChain::ConstIterator cle = conditions->end();
+ for (ConditionChain::ConstIterator j = conditions->begin(); j != cle; ++j, cnum++) {
+ const Condition *c = (*j);
+ QString prefix = QString("condition%1-").arg(cnum);
+
+ config->writeEntry(prefix + "field", (int) c->field());
+ config->writeEntry(prefix + "type", (int) c->type());
+ config->writeEntry(prefix + "value", c->value());
+ config->writeEntry(prefix + "valueType", (int) c->value().type());
+ }
+
+ // Write actions
+ int anum = 0;
+ const ActionChain *actions = rule->actions();
+ config->writeEntry("actions", actions->count());
+
+ ActionChain::ConstIterator ale = actions->end();
+ for (ActionChain::ConstIterator j = actions->begin(); j != ale; ++j, anum++) {
+ const Action *a = (*j);
+ QString prefix = QString("action%1-").arg(anum);
+
+ config->writeEntry(prefix + "type", (int) a->type());
+ config->writeEntry(prefix + "value", a->value());
+ config->writeEntry(prefix + "valueType", (int) a->value().type());
+ }
+ }
+}
+
+void Filters::load()
+{
+ int num = 0;
+ KConfig *config = kapp->config();
+
+ config->setGroup("Filters");
+ num = config->readNumEntry("count");
+
+ for (int i = 0; i < num; i++) {
+ Rule *rule = new Rule();
+
+ config->setGroup(QString("Filter #%1").arg(i));
+ rule->setName(config->readEntry("name", i18n("Unnamed Rule")));
+ rule->setEnabled(config->readBoolEntry("enabled", true));
+
+ // Read conditions
+ ConditionChain *conditions = const_cast<ConditionChain*>(rule->conditions());
+ int cnum = config->readNumEntry("conditions");
+ conditions->setType((ConditionChain::Type) config->readNumEntry("conditions-type"));
+
+ for (int j = 0; j < cnum; j++) {
+ QString prefix = QString("condition%1-").arg(j);
+
+ conditions->append(new Condition((Field) config->readNumEntry(prefix + "field"),
+ (Condition::Type) config->readNumEntry(prefix + "type"),
+ config->readPropertyEntry(prefix + "value",
+ (QVariant::Type) config->readNumEntry(prefix + "valueType"))));
+ }
+
+ // Read actions
+ ActionChain *actions = const_cast<ActionChain*>(rule->actions());
+ int anum = config->readNumEntry("actions");
+
+ for (int j = 0; j < anum; j++) {
+ QString prefix = QString("action%1-").arg(j);
+
+ actions->append(new Action((Action::Type) config->readNumEntry(prefix + "type"),
+ config->readPropertyEntry(prefix + "value",
+ (QVariant::Type) config->readNumEntry(prefix + "valueType"))));
+ }
+
+ append(rule);
+ }
+}
+
+const ActionChain *Filters::process(const KFTPEngine::DirectoryEntry &entry) const
+{
+ if (m_enabled) {
+ QPtrList<Rule>::ConstIterator le = end();
+ for (QPtrList<Rule>::ConstIterator i = begin(); i != le; ++i) {
+ const Rule *rule = (*i);
+
+ if (rule->isEnabled() && rule->conditions()->matches(entry))
+ return rule->actions();
+ }
+ }
+
+ // Nothing has matched
+ return 0;
+}
+
+const Action *Filters::process(const KFTPEngine::DirectoryEntry &entry, QValueList<Action::Type> types) const
+{
+ const ActionChain *chain = process(entry);
+
+ if (!chain || chain->isEmpty())
+ return 0;
+
+ // Find an action that matches the filter
+ ActionChain::ConstIterator le = chain->end();
+ for (ActionChain::ConstIterator i = chain->begin(); i != le; ++i) {
+ if (types.contains((*i)->type()))
+ return (*i);
+ }
+
+ return 0;
+}
+
+const Action *Filters::process(const KFTPEngine::DirectoryEntry &entry, Action::Type filter) const
+{
+ const ActionChain *chain = process(entry);
+
+ if (!chain || chain->isEmpty())
+ return 0;
+
+ // Find an action that matches the filter
+ ActionChain::ConstIterator le = chain->end();
+ for (ActionChain::ConstIterator i = chain->begin(); i != le; ++i) {
+ if ((*i)->type() == filter)
+ return (*i);
+ }
+
+ return 0;
+}
+
+const Action *Filters::process(const KURL &url, filesize_t size, bool directory, Action::Type filter) const
+{
+ KFTPEngine::DirectoryEntry entry;
+ entry.setFilename(url.filename());
+ entry.setSize(size);
+ entry.setType(directory ? 'd' : 'f');
+
+ return process(entry, filter);
+}
+
+const ActionChain *Filters::process(const KURL &url, filesize_t size, bool directory) const
+{
+ KFTPEngine::DirectoryEntry entry;
+ entry.setFilename(url.filename());
+ entry.setSize(size);
+ entry.setType(directory ? 'd' : 'f');
+
+ return process(entry);
+}
+
+const Action *Filters::process(const KURL &url, Action::Type filter) const
+{
+ return process(url, 0, false, filter);
+}
+
+}
+
+}
diff --git a/kftpgrabber/src/misc/filter.h b/kftpgrabber/src/misc/filter.h
new file mode 100644
index 0000000..8c7ac36
--- /dev/null
+++ b/kftpgrabber/src/misc/filter.h
@@ -0,0 +1,506 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_FILTERFILTERS_H
+#define KFTPCORE_FILTERFILTERS_H
+
+#include <qvariant.h>
+#include <qvaluelist.h>
+
+#include "engine/directorylisting.h"
+
+namespace KFTPCore {
+
+namespace Filter {
+
+/**
+ * Possible filter fields.
+ */
+enum Field {
+ Filename = 0,
+ EntryType = 1,
+ Size = 2
+};
+
+/**
+ * A filter condition class of different types.
+ *
+ * @author Jernej Kos
+ */
+class Condition {
+public:
+ /**
+ * Condition type.
+ *
+ * The following types are valid:
+ * - None: this rule never validates
+ * - Contains: field contains a given substring
+ * - ContainsNot: field does not contain a given substring
+ * - Is: field is equal to the given value
+ * - IsNot: field is not equal to the given value
+ * - Matches: field matches a given regular expression
+ * - MatchesNot: field doesn't match a given regular expression
+ * - Greater: field's integer value is greater than the given value
+ * - Smaller: field's integer value is smaller than the given value
+ */
+ enum Type {
+ None = -1,
+ Contains = 0,
+ ContainsNot = 1,
+ Is = 2,
+ IsNot = 3,
+ Matches = 4,
+ MatchesNot = 5,
+ Greater = 6,
+ Smaller = 7
+ };
+
+ /**
+ * Class constructor. The constructed condition is invalid.
+ */
+ Condition() {}
+
+ /**
+ * Class constructor.
+ *
+ * @param field Field to check
+ * @param type Condition type
+ * @param value Value to check against
+ */
+ Condition(Field field, Type type, const QVariant &value);
+
+ /**
+ * Returns the field this condition operates on.
+ */
+ Field field() const { return m_field; }
+
+ /**
+ * Set condition field.
+ *
+ * @param field A valid condition field
+ */
+ void setField(Field field) { m_field = field; }
+
+ /**
+ * Returns the type of this condition.
+ */
+ Type type() const { return m_type; }
+
+ /**
+ * Set condition type.
+ *
+ * @param type A valid condition type
+ */
+ void setType(Type type) { m_type = type; }
+
+ /**
+ * Returns the value this condition validates the field with.
+ */
+ QVariant value() const { return m_value; }
+
+ /**
+ * Set condition validation value.
+ *
+ * @param value A valid validation value
+ */
+ void setValue(const QVariant &value) { m_value = value; }
+
+ /**
+ * Does the specified entry match this condition ?
+ *
+ * @param entry Directory entry to compare
+ * @return True if the entry matches this condition, false otherwise
+ */
+ bool matches(const KFTPEngine::DirectoryEntry &entry) const;
+private:
+ Field m_field;
+ Type m_type;
+ QVariant m_value;
+};
+
+/**
+ * This class represents a chain of filter conditions.
+ *
+ * @author Jernej Kos
+ */
+class ConditionChain : public QPtrList<Condition> {
+public:
+ /**
+ * Chain type.
+ *
+ * The following types are valid:
+ * - All: all conditions must match
+ * - Any: any condition can match
+ */
+ enum Type {
+ All = 0,
+ Any = 1
+ };
+
+ /**
+ * Class constructor.
+ */
+ ConditionChain();
+
+ /**
+ * Class constructor.
+ */
+ ConditionChain(Type type);
+
+ /**
+ * Returns condition chain match type.
+ */
+ Type type() const { return m_type; }
+
+ /**
+ * Set condition chain match type.
+ *
+ * @param type A valid type
+ */
+ void setType(Type type) { m_type = type; }
+
+ /**
+ * Does the specified entry match this condition chain ? The actual
+ * matching depends on chain type.
+ *
+ * @param entry Directory entry to compare
+ * @return True if the entry matches this chain, false otherwise
+ */
+ bool matches(const KFTPEngine::DirectoryEntry &entry) const;
+private:
+ Type m_type;
+};
+
+/**
+ * This class represents a single action to take.
+ */
+class Action {
+public:
+ /**
+ * Action type.
+ *
+ * These are the valid types:
+ * - Priority: when queuing files, their priority should be changed
+ * - Skip: do not queue such files
+ * - Colorize: change font color in the list view
+ * - Hide: do not display such files in the list view
+ * - Lowercase: lowercase the destination filename when queuing
+ */
+ enum Type {
+ None = 0,
+ Priority = 1,
+ Skip = 2,
+ Colorize = 3,
+ Hide = 4,
+ Lowercase = 5
+ };
+
+ /**
+ * Class constructor. The resulting action is invalid.
+ */
+ Action();
+
+ /**
+ * Class constructor.
+ *
+ * @param type Action type
+ * @param value Action parameters
+ */
+ Action(Type type, const QVariant &value);
+
+ /**
+ * Returns true if the action is valid.
+ */
+ bool isValid() const { return m_valid; }
+
+ /**
+ * Get action's type.
+ */
+ Type type() const { return m_type; }
+
+ /**
+ * Set the action type.
+ *
+ * @param type A valid action type
+ */
+ void setType(Type type) { m_type = type; }
+
+ /**
+ * Get action's parameters.
+ */
+ QVariant value() const { return m_value; }
+
+ /**
+ * Set action parameter.
+ *
+ * @param value Parameter value
+ */
+ void setValue(const QVariant &value) { m_value = value; }
+private:
+ bool m_valid;
+ Type m_type;
+ QVariant m_value;
+};
+
+/**
+ * This class represents a chain of filter actions.
+ *
+ * @author Jernej Kos
+ */
+class ActionChain : public QPtrList<Action> {
+public:
+ /**
+ * Class constructor.
+ */
+ ActionChain();
+
+ /**
+ * Get an action of the specified type.
+ *
+ * @param type Action type to search for
+ * @return A valid Action or null if there is none
+ */
+ const Action *getAction(Action::Type type) const;
+};
+
+/**
+ * This class represents a single filter rule consiting of a condition chain
+ * and an action chain.
+ *
+ * @author Jernej Kos
+ */
+class Rule {
+public:
+ /**
+ * Class constructor.
+ */
+ Rule();
+
+ /**
+ * Class copy constructor. Creates a duplicate deep copy of the provided
+ * rule.
+ *
+ * @param rule The rule to copy
+ */
+ Rule(const Rule *rule);
+
+ /**
+ * Class constructor.
+ *
+ * @param name Human readable rule name
+ */
+ Rule(const QString &name);
+
+ /**
+ * Get rule's name.
+ */
+ QString name() const { return m_name; }
+
+ /**
+ * Set rule's name.
+ */
+ void setName(const QString &name) { m_name = name; }
+
+ /**
+ * Is this rule enabled or not ?
+ *
+ * @return True if the rule is enabled, false otherwise
+ */
+ bool isEnabled() const { return m_enabled; }
+
+ /**
+ * Enable or disable this rule.
+ *
+ * @param value True if the rule is enabled, false otherwise
+ */
+ void setEnabled(bool value) { m_enabled = value; }
+
+ /**
+ * Get the condition chain reference.
+ */
+ const ConditionChain *conditions() const { return &m_conditionChain; }
+
+ /**
+ * Get the action chain reference.
+ */
+ const ActionChain *actions() const { return &m_actionChain; }
+private:
+ QString m_name;
+ bool m_enabled;
+ ConditionChain m_conditionChain;
+ ActionChain m_actionChain;
+};
+
+/**
+ * This class contains all the currently loaded rules.
+ *
+ * @author Jernej Kos
+ */
+class Filters : public QPtrList<Rule> {
+public:
+ /**
+ * Get the global rule chain.
+ */
+ static Filters *self();
+
+ /**
+ * Class destructor.
+ */
+ ~Filters();
+
+ /**
+ * Load the rules from a file.
+ */
+ void load();
+
+ /**
+ * Serialize the rules and save them to a file.
+ */
+ void save();
+
+ /**
+ * Is filtering enabled or not ?
+ *
+ * @return True if filtering is enabled, false otherwise
+ */
+ bool isEnabled() const { return m_enabled; }
+
+ /**
+ * Enable or disable filtering.
+ *
+ * @param value True if filtering is enabled, false otherwise
+ */
+ void setEnabled(bool value) { m_enabled = value; }
+
+ /**
+ * Process the specified entry and return an action chain that matched
+ * first.
+ *
+ * @param entry The entry to process
+ * @return An ActionChain reference (might be empty if nothing matched)
+ */
+ const ActionChain *process(const KFTPEngine::DirectoryEntry &entry) const;
+
+ /**
+ * Process the specified entry and return an action to use. This will
+ * go trough all loaded rules and attempt to process each one by one.
+ * The first one that succeeds is returned.
+ *
+ * @param entry The entry to process
+ * @param types Only return the action of this type
+ * @return An Action reference (might be invalid if nothing matched)
+ */
+ const Action *process(const KFTPEngine::DirectoryEntry &entry, QValueList<Action::Type> types) const;
+
+ /**
+ * Process the specified entry and return an action to use. This will
+ * go trough all loaded rules and attempt to process each one by one.
+ * The first one that succeeds is returned.
+ *
+ * @param entry The entry to process
+ * @param filter Only return the action of this type
+ * @return An Action reference (might be invalid if nothing matched)
+ */
+ const Action *process(const KFTPEngine::DirectoryEntry &entry, Action::Type filter) const;
+
+ /**
+ * This method is provided for convienience. It behaves just like the
+ * above method.
+ *
+ * @param url File's URL
+ * @param size File's size
+ * @param directory True if the entry is a directory
+ * @param filter Only return the action of this type
+ * @return An Action reference (might be invalid if nothing matched)
+ */
+ const Action *process(const KURL &url, filesize_t size, bool directory, Action::Type filter) const;
+
+ /**
+ * Process the specified entry and return an action chain that matched
+ * first.
+ *
+ * @param url File's URL
+ * @param size File's size
+ * @param directory True if the entry is a directory
+ * @return An ActionChain reference (might be invalid if nothing matched)
+ */
+ const ActionChain *process(const KURL &url, filesize_t size, bool directory) const;
+
+ /**
+ * This method is provided for convienience. It behaves just like the
+ * above method.
+ *
+ * Note that 0 will be used for filesize and this may affect the filter
+ * process!
+ *
+ * @param url File's URL
+ * @param filter Only return the action of this type
+ * @return An Action reference (might be invalid if nothing matched)
+ */
+ const Action *process(const KURL &url, Action::Type filter) const;
+
+ /**
+ * Get a human readable list of possible field names.
+ *
+ * @return A QStringList representing the field names
+ */
+ const QStringList &getFieldNames() { return m_fieldNames; }
+
+ /**
+ * Get a human readable list of possible action names
+ *
+ * @return A QStringList representing the action names
+ */
+ const QStringList &getActionNames() { return m_actionNames; }
+protected:
+ /**
+ * Class constructor.
+ */
+ Filters();
+private:
+ static Filters *m_self;
+
+ bool m_enabled;
+ ActionChain m_emptyActionChain;
+
+ QStringList m_fieldNames;
+ QStringList m_actionNames;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/filterwidgethandler.cpp b/kftpgrabber/src/misc/filterwidgethandler.cpp
new file mode 100644
index 0000000..6fa368d
--- /dev/null
+++ b/kftpgrabber/src/misc/filterwidgethandler.cpp
@@ -0,0 +1,539 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "filterwidgethandler.h"
+
+#include <qcombobox.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <kcolorbutton.h>
+
+namespace KFTPCore {
+
+namespace Filter {
+
+namespace {
+
+static const struct {
+ const Condition::Type type;
+ const char *name;
+} TextTypes[] = {
+ { Condition::Contains, I18N_NOOP("contains") },
+ { Condition::ContainsNot, I18N_NOOP("does not contain") },
+ { Condition::Is, I18N_NOOP("equals") },
+ { Condition::IsNot, I18N_NOOP("does not equal") },
+ { Condition::Matches, I18N_NOOP("matches regexp") },
+ { Condition::MatchesNot, I18N_NOOP("does not match regexp") }
+};
+
+static const int TextTypeCount = sizeof(TextTypes) / sizeof(*TextTypes);
+
+class TextWidgetHandler : public ConditionWidgetHandler
+{
+public:
+ TextWidgetHandler()
+ : ConditionWidgetHandler()
+ {
+ }
+
+ QWidget *createTypeWidget(QWidget *parent, const QObject *receiver) const
+ {
+ QComboBox *combo = new QComboBox(parent);
+
+ for (int i = 0; i < TextTypeCount; i++) {
+ combo->insertItem(i18n(TextTypes[i].name));
+ }
+
+ combo->adjustSize();
+
+ // Connect the signal
+ QObject::connect(combo, SIGNAL(activated(int)), receiver, SLOT(slotTypeChanged()));
+
+ return combo;
+ }
+
+ Condition::Type getConditionType(QWidget *widget) const
+ {
+ QComboBox *combo = static_cast<QComboBox*>(widget);
+ return TextTypes[combo->currentItem()].type;
+ }
+
+ void createValueWidgets(QWidgetStack *stack, const QObject *receiver) const
+ {
+ KLineEdit *lineEdit = new KLineEdit(stack, "textWidgetHandler_LineEdit");
+ QObject::connect(lineEdit, SIGNAL(textChanged(const QString&)), receiver, SLOT(slotValueChanged()));
+ stack->addWidget(lineEdit);
+ }
+
+ QVariant getConditionValue(QWidgetStack *values) const
+ {
+ KLineEdit *lineEdit = static_cast<KLineEdit*>(values->child("textWidgetHandler_LineEdit"));
+ return QVariant(lineEdit->text());
+ }
+
+ void update(int field, QWidgetStack *types, QWidgetStack *values) const
+ {
+ types->raiseWidget(field);
+ values->raiseWidget((QWidget*) values->child("textWidgetHandler_LineEdit"));
+ }
+
+ void setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition)
+ {
+ // Set condition type
+ const Condition::Type type = condition->type();
+ int typeIndex = 0;
+
+ for (; typeIndex < TextTypeCount; typeIndex++)
+ if (type == TextTypes[typeIndex].type)
+ break;
+
+ QComboBox *combo = static_cast<QComboBox*>(types->widget(((int) condition->field())));
+ combo->blockSignals(true);
+ combo->setCurrentItem(typeIndex);
+ combo->blockSignals(false);
+ types->raiseWidget(combo);
+
+ // Set condition value
+ KLineEdit *lineEdit = static_cast<KLineEdit*>(values->child("textWidgetHandler_LineEdit"));
+ lineEdit->blockSignals(true);
+ lineEdit->setText(condition->value().toString());
+ lineEdit->blockSignals(false);
+ values->raiseWidget(lineEdit);
+ }
+};
+
+}
+
+namespace {
+
+static const struct {
+ const Condition::Type type;
+ const char *name;
+} EntryTypes[] = {
+ { Condition::Is, I18N_NOOP("is") },
+ { Condition::IsNot, I18N_NOOP("is not") }
+};
+
+static const int EntryTypeCount = sizeof(EntryTypes) / sizeof(*EntryTypes);
+
+class EntryWidgetHandler : public ConditionWidgetHandler
+{
+public:
+ EntryWidgetHandler()
+ : ConditionWidgetHandler()
+ {
+ }
+
+ QWidget *createTypeWidget(QWidget *parent, const QObject *receiver) const
+ {
+ QComboBox *combo = new QComboBox(parent);
+
+ for (int i = 0; i < EntryTypeCount; i++) {
+ combo->insertItem(i18n(EntryTypes[i].name));
+ }
+
+ combo->adjustSize();
+
+ // Connect the signal
+ QObject::connect(combo, SIGNAL(activated(int)), receiver, SLOT(slotTypeChanged()));
+
+ return combo;
+ }
+
+ Condition::Type getConditionType(QWidget *widget) const
+ {
+ QComboBox *combo = static_cast<QComboBox*>(widget);
+ return EntryTypes[combo->currentItem()].type;
+ }
+
+ void createValueWidgets(QWidgetStack *stack, const QObject *receiver) const
+ {
+ QComboBox *combo = new QComboBox(stack, "entryWidgetHandler_Combo");
+ combo->insertItem(i18n("File"), 0);
+ combo->insertItem(i18n("Directory"), 1);
+
+ QObject::connect(combo, SIGNAL(activated(int)), receiver, SLOT(slotValueChanged()));
+ stack->addWidget(combo);
+ }
+
+ QVariant getConditionValue(QWidgetStack *values) const
+ {
+ QComboBox *combo = static_cast<QComboBox*>(values->child("entryWidgetHandler_Combo"));
+ QVariant value;
+
+ if (combo->currentItem() == 0)
+ value = QVariant(QString("f"));
+ else
+ value = QVariant(QString("d"));
+
+ return value;
+ }
+
+ void update(int field, QWidgetStack *types, QWidgetStack *values) const
+ {
+ types->raiseWidget(field);
+ values->raiseWidget((QWidget*) values->child("entryWidgetHandler_Combo"));
+ }
+
+ void setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition)
+ {
+ // Set condition type
+ const Condition::Type type = condition->type();
+ int typeIndex = 0;
+
+ for (; typeIndex < EntryTypeCount; typeIndex++)
+ if (type == EntryTypes[typeIndex].type)
+ break;
+
+ QComboBox *combo = static_cast<QComboBox*>(types->widget(((int) condition->field())));
+ combo->blockSignals(true);
+ combo->setCurrentItem(typeIndex);
+ combo->blockSignals(false);
+ types->raiseWidget(combo);
+
+ // Set condition value
+ combo = static_cast<QComboBox*>(values->child("entryWidgetHandler_Combo"));
+ combo->blockSignals(true);
+ combo->setCurrentItem(condition->value().toString() == "f" ? 0 : 1);
+ combo->blockSignals(false);
+ values->raiseWidget(combo);
+ }
+};
+
+}
+
+namespace {
+
+static const struct {
+ const Condition::Type type;
+ const char *name;
+} SizeTypes[] = {
+ { Condition::Is, I18N_NOOP("equals") },
+ { Condition::IsNot, I18N_NOOP("does not equal") },
+ { Condition::Greater, I18N_NOOP("is greater than") },
+ { Condition::Smaller, I18N_NOOP("is smaller than") }
+};
+
+static const int SizeTypeCount = sizeof(SizeTypes) / sizeof(*SizeTypes);
+
+class SizeWidgetHandler : public ConditionWidgetHandler
+{
+public:
+ SizeWidgetHandler()
+ : ConditionWidgetHandler()
+ {
+ }
+
+ QWidget *createTypeWidget(QWidget *parent, const QObject *receiver) const
+ {
+ QComboBox *combo = new QComboBox(parent);
+
+ for (int i = 0; i < SizeTypeCount; i++) {
+ combo->insertItem(i18n(SizeTypes[i].name));
+ }
+
+ combo->adjustSize();
+
+ // Connect the signal
+ QObject::connect(combo, SIGNAL(activated(int)), receiver, SLOT(slotTypeChanged()));
+
+ return combo;
+ }
+
+ Condition::Type getConditionType(QWidget *widget) const
+ {
+ QComboBox *combo = static_cast<QComboBox*>(widget);
+ return SizeTypes[combo->currentItem()].type;
+ }
+
+ void createValueWidgets(QWidgetStack *stack, const QObject *receiver) const
+ {
+ KIntNumInput *numInput = new KIntNumInput(stack, "sizeWidgetHandler_NumInput");
+ numInput->setMinValue(0);
+ numInput->setSuffix(" " + i18n("bytes"));
+
+ QObject::connect(numInput, SIGNAL(valueChanged(int)), receiver, SLOT(slotValueChanged()));
+ stack->addWidget(numInput);
+ }
+
+ QVariant getConditionValue(QWidgetStack *values) const
+ {
+ KIntNumInput *numInput = static_cast<KIntNumInput*>(values->child("sizeWidgetHandler_NumInput"));
+ return QVariant(numInput->value());
+ }
+
+ void update(int field, QWidgetStack *types, QWidgetStack *values) const
+ {
+ types->raiseWidget(field);
+ values->raiseWidget((QWidget*) values->child("sizeWidgetHandler_NumInput"));
+ }
+
+ void setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition)
+ {
+ // Set condition type
+ const Condition::Type type = condition->type();
+ int typeIndex = 0;
+
+ for (; typeIndex < SizeTypeCount; typeIndex++)
+ if (type == SizeTypes[typeIndex].type)
+ break;
+
+ QComboBox *combo = static_cast<QComboBox*>(types->widget(((int) condition->field())));
+ combo->blockSignals(true);
+ combo->setCurrentItem(typeIndex);
+ combo->blockSignals(false);
+ types->raiseWidget(combo);
+
+ // Set condition value
+ KIntNumInput *numInput = static_cast<KIntNumInput*>(values->child("sizeWidgetHandler_NumInput"));
+ numInput->blockSignals(true);
+ numInput->setValue(condition->value().toInt());
+ numInput->blockSignals(false);
+ values->raiseWidget(numInput);
+ }
+};
+
+}
+
+class EmptyActionWidgetHandler : public ActionWidgetHandler
+{
+public:
+ EmptyActionWidgetHandler()
+ : ActionWidgetHandler()
+ {
+ }
+
+ virtual QWidget *createWidget(QWidget *parent, const QObject *receiver) const
+ {
+ Q_UNUSED(receiver);
+
+ return new QWidget(parent);
+ }
+
+ QVariant getActionValue(QWidget *widget) const
+ {
+ Q_UNUSED(widget);
+
+ return QVariant(QString());
+ }
+
+ void setAction(QWidgetStack *stack, const Action *action) const
+ {
+ stack->raiseWidget((int) action->type());
+ }
+};
+
+class NoneActionWidgetHandler : public EmptyActionWidgetHandler
+{
+public:
+ NoneActionWidgetHandler()
+ : EmptyActionWidgetHandler()
+ {
+ }
+
+ QWidget *createWidget(QWidget *parent, const QObject *receiver) const
+ {
+ Q_UNUSED(receiver);
+
+ return new QLabel(i18n("Please select an action."), parent);
+ }
+};
+
+class PriorityActionWidgetHandler : public ActionWidgetHandler
+{
+public:
+ PriorityActionWidgetHandler()
+ : ActionWidgetHandler()
+ {
+ }
+
+ QWidget *createWidget(QWidget *parent, const QObject *receiver) const
+ {
+ KIntNumInput *numInput = new KIntNumInput(parent);
+ numInput->setPrefix(i18n("Priority:") + " ");
+
+ QObject::connect(numInput, SIGNAL(valueChanged(int)), receiver, SLOT(slotValueChanged()));
+ return numInput;
+ }
+
+ QVariant getActionValue(QWidget *widget) const
+ {
+ KIntNumInput *numInput = static_cast<KIntNumInput*>(widget);
+ return QVariant(numInput->value());
+ }
+
+ void setAction(QWidgetStack *stack, const Action *action) const
+ {
+ stack->raiseWidget((int) action->type());
+ KIntNumInput *numInput = static_cast<KIntNumInput*>(stack->visibleWidget());
+ numInput->setValue(action->value().toInt());
+ }
+};
+
+class ColorizeActionWidgetHandler : public ActionWidgetHandler
+{
+public:
+ ColorizeActionWidgetHandler()
+ : ActionWidgetHandler()
+ {
+ }
+
+ QWidget *createWidget(QWidget *parent, const QObject *receiver) const
+ {
+ KColorButton *colorButton = new KColorButton(parent);
+
+ QObject::connect(colorButton, SIGNAL(changed(const QColor&)), receiver, SLOT(slotValueChanged()));
+ return colorButton;
+ }
+
+ QVariant getActionValue(QWidget *widget) const
+ {
+ KColorButton *colorButton = static_cast<KColorButton*>(widget);
+ return QVariant(colorButton->color());
+ }
+
+ void setAction(QWidgetStack *stack, const Action *action) const
+ {
+ stack->raiseWidget((int) action->type());
+ KColorButton *colorButton = static_cast<KColorButton*>(stack->visibleWidget());
+ colorButton->setColor(action->value().toColor());
+ }
+};
+
+WidgetHandlerManager *WidgetHandlerManager::m_self = 0;
+static KStaticDeleter<WidgetHandlerManager> staticHandlerManagerDeleter;
+
+WidgetHandlerManager *WidgetHandlerManager::self()
+{
+ if (!m_self) {
+ staticHandlerManagerDeleter.setObject(m_self, new WidgetHandlerManager());
+ }
+
+ return m_self;
+}
+
+WidgetHandlerManager::WidgetHandlerManager()
+{
+ // Register condition handlers
+ registerConditionHandler(Filename, new TextWidgetHandler());
+ registerConditionHandler(EntryType, new EntryWidgetHandler());
+ registerConditionHandler(Size, new SizeWidgetHandler());
+
+ // Register action handlers
+ registerActionHandler(Action::None, new NoneActionWidgetHandler());
+ registerActionHandler(Action::Priority, new PriorityActionWidgetHandler());
+ registerActionHandler(Action::Skip, new EmptyActionWidgetHandler());
+ registerActionHandler(Action::Colorize, new ColorizeActionWidgetHandler());
+ registerActionHandler(Action::Hide, new EmptyActionWidgetHandler());
+ registerActionHandler(Action::Lowercase, new EmptyActionWidgetHandler());
+}
+
+WidgetHandlerManager::~WidgetHandlerManager()
+{
+ if (m_self == this)
+ staticHandlerManagerDeleter.setObject(m_self, 0, false);
+}
+
+void WidgetHandlerManager::registerConditionHandler(Field field, ConditionWidgetHandler *handler)
+{
+ m_conditionHandlers[field] = handler;
+}
+
+void WidgetHandlerManager::registerActionHandler(Action::Type type, ActionWidgetHandler *handler)
+{
+ m_actionHandlers[type] = handler;
+}
+
+void WidgetHandlerManager::createConditionWidgets(QWidgetStack *types, QWidgetStack *values, const QObject *receiver)
+{
+ ConditionHandlerMap::ConstIterator le = m_conditionHandlers.end();
+ for (ConditionHandlerMap::ConstIterator i = m_conditionHandlers.begin(); i != le; ++i) {
+ Field field = i.key();
+ const ConditionWidgetHandler *handler = i.data();
+
+ types->addWidget(handler->createTypeWidget(types, receiver), (int) field);
+ handler->createValueWidgets(values, receiver);
+ }
+}
+
+void WidgetHandlerManager::createActionWidgets(QWidgetStack *stack, const QObject *receiver)
+{
+ ActionHandlerMap::ConstIterator le = m_actionHandlers.end();
+ for (ActionHandlerMap::ConstIterator i = m_actionHandlers.begin(); i != le; ++i) {
+ Action::Type type = i.key();
+ const ActionWidgetHandler *handler = i.data();
+
+ stack->addWidget(handler->createWidget(stack, receiver), (int) type);
+ }
+}
+
+void WidgetHandlerManager::update(Field field, QWidgetStack *types, QWidgetStack *values)
+{
+ m_conditionHandlers[field]->update((int) field, types, values);
+}
+
+void WidgetHandlerManager::setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition)
+{
+ m_conditionHandlers[condition->field()]->setCondition(types, values, condition);
+}
+
+Condition::Type WidgetHandlerManager::getConditionType(Field field, QWidgetStack *types)
+{
+ return m_conditionHandlers[field]->getConditionType(types->widget((int) field));
+}
+
+QVariant WidgetHandlerManager::getConditionValue(Field field, QWidgetStack *values)
+{
+ return m_conditionHandlers[field]->getConditionValue(values);
+}
+
+void WidgetHandlerManager::setAction(QWidgetStack *stack, const Action *action)
+{
+ m_actionHandlers[action->type()]->setAction(stack, action);
+}
+
+QVariant WidgetHandlerManager::getActionValue(QWidgetStack *stack)
+{
+ QWidget *widget = stack->visibleWidget();
+ return m_actionHandlers[(Action::Type) stack->id(widget)]->getActionValue(widget);
+}
+
+}
+
+}
diff --git a/kftpgrabber/src/misc/filterwidgethandler.h b/kftpgrabber/src/misc/filterwidgethandler.h
new file mode 100644
index 0000000..5ea6376
--- /dev/null
+++ b/kftpgrabber/src/misc/filterwidgethandler.h
@@ -0,0 +1,279 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCORE_FILTERFILTERWIDGETHANDLER_H
+#define KFTPCORE_FILTERFILTERWIDGETHANDLER_H
+
+#include "filter.h"
+
+#include <qmap.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+
+namespace KFTPCore {
+
+namespace Filter {
+
+/**
+ * This is an interface that any condition widget handlers must implement
+ * in order to be registred with the manager. The handler handles several
+ * widgets at once so it should not keep any member variables.
+ *
+ * @author Jernej Kos
+ */
+class ConditionWidgetHandler {
+public:
+ /**
+ * This method has to return a new widget used for displaying condition
+ * types. It is usually a combobox with several options. The returned
+ * widget is added to the value widget stack.
+ *
+ * @param parent Widget to be used as parent for the newly created widget
+ * @param receiver Object that receives type change notifications
+ * @return A valid QWidget instance
+ */
+ virtual QWidget *createTypeWidget(QWidget *parent, const QObject *receiver) const = 0;
+
+ /**
+ * This method has to create one or several input widgets for different
+ * condition types. Each widget has to be added to the widget stack. An
+ * identifying widget name is recommended for each widget, so you can easily
+ * access it from other methods which get the value stack widget passed as
+ * an argument.
+ *
+ * @param stack Value widget stack
+ * @param receiver Object that receives value change notifications
+ */
+ virtual void createValueWidgets(QWidgetStack *stack, const QObject *receiver) const = 0;
+
+ /**
+ * Update the status of all widgets.
+ *
+ * @param field The field to display
+ * @param types Type widget stack to use
+ * @param values Value widget stack to use
+ */
+ virtual void update(int field, QWidgetStack *types, QWidgetStack *values) const = 0;
+
+ /**
+ * Extract data from internal condition representation and show it to the
+ * user using the created widgets.
+ *
+ * @param types Type widget stack to use
+ * @param values Value widget stack to use
+ * @param condition The condition representation
+ */
+ virtual void setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition) = 0;
+
+ /**
+ * This method should return the currently selected condition type.
+ *
+ * @param widget The type widget previously created
+ * @return A valid Condition::Type
+ */
+ virtual Condition::Type getConditionType(QWidget *widget) const = 0;
+
+ /**
+ * This method should return the current condition value.
+ *
+ * @param values Value widget stack to use
+ * @return A valid condition value
+ */
+ virtual QVariant getConditionValue(QWidgetStack *values) const = 0;
+};
+
+/**
+ * This is an interface that any action widget handlers must implement
+ * in order to be registred with the manager. The handler handles several
+ * widgets at once so it should not keep any member variables.
+ *
+ * @author Jernej Kos
+ */
+class ActionWidgetHandler {
+public:
+ /**
+ * This method has to return a new widget used for displaying the action
+ * value. It is usually a line edit or a similar input widget. The returned
+ * widget is added to the value widget stack.
+ *
+ * @param parent Widget to be used as parent for the newly created widget
+ * @param receiver Object that receives type change notifications
+ * @return A valid QWidget instance
+ */
+ virtual QWidget *createWidget(QWidget *parent, const QObject *receiver) const = 0;
+
+ /**
+ * Extract data from internal action representation and show it to the
+ * user using the created widgets.
+ *
+ * @param stack Value widget stack to use
+ * @param action The action representation
+ */
+ virtual void setAction(QWidgetStack *stack, const Action *action) const = 0;
+
+ /**
+ * This method should return the current action value.
+ *
+ * @param values Value widget stack to use
+ * @return A valid action value
+ */
+ virtual QVariant getActionValue(QWidget *widget) const = 0;
+};
+
+/**
+ * This class keeps a list of all registred condition and action widget
+ * handlers. It is a singleton.
+ *
+ * @author Jernej Kos
+ */
+class WidgetHandlerManager
+{
+public:
+ /**
+ * Get the global class instance.
+ */
+ static WidgetHandlerManager *self();
+
+ /**
+ * Class destructor.
+ */
+ ~WidgetHandlerManager();
+
+ /**
+ * Create widgets for all currently registred condition handlers.
+ *
+ * @param types Type widget stack to use
+ * @param value Value widget stack to use
+ * @param receiver Object that receives change notifications
+ */
+ void createConditionWidgets(QWidgetStack *types, QWidgetStack *values, const QObject *receiver);
+
+ /**
+ * Create widgets for all currently registred action handlers.
+ *
+ * @param stack Value widget stack to use
+ * @param receiver Object that receives change notifications
+ */
+ void createActionWidgets(QWidgetStack *stack, const QObject *receiver);
+
+ /**
+ * Update the specified condition widget handler.
+ *
+ * @param field New condition field
+ * @param types Type widget stack to use
+ * @param values Value widget stack to use
+ */
+ void update(Field field, QWidgetStack *types, QWidgetStack *values);
+
+ /**
+ * Extract data from internal condition representation and show it to the
+ * user using the created widgets.
+ *
+ * @param types Type widget stack to use
+ * @param values Value widget stack to use
+ * @param condition The condition representation
+ */
+ void setCondition(QWidgetStack *types, QWidgetStack *values, const Condition *condition);
+
+ /**
+ * Get the currently selected condition type.
+ *
+ * @param field Condition field
+ * @param types Type widget stack to use
+ * @return A valid Condition::Type
+ */
+ Condition::Type getConditionType(Field field, QWidgetStack *types);
+
+ /**
+ * Get the current condition value.
+ *
+ * @param field Condition field
+ * @param values Value widget stack to use
+ * @return A valid condition value
+ */
+ QVariant getConditionValue(Field field, QWidgetStack *values);
+
+ /**
+ * Extract data from internal action representation and show it to the
+ * user using the created widgets.
+ *
+ * @param stack Value widget stack to use
+ * @param action The action representation
+ */
+ void setAction(QWidgetStack *stack, const Action *action);
+
+ /**
+ * Get the current action value.
+ *
+ * @param stack Value widget stack to use
+ * @return A valid action value
+ */
+ QVariant getActionValue(QWidgetStack *stack);
+
+ /**
+ * Register a new condition handler with the manager.
+ *
+ * @param field Field that this handler handles
+ * @param handler The actual handler instance
+ */
+ void registerConditionHandler(Field field, ConditionWidgetHandler *handler);
+
+ /**
+ * Register a new action handler with the manager.
+ *
+ * @param type Action type that this handler handles
+ * @param handler The actual handler instance
+ */
+ void registerActionHandler(Action::Type type, ActionWidgetHandler *handler);
+protected:
+ /**
+ * Class constructor.
+ */
+ WidgetHandlerManager();
+private:
+ static WidgetHandlerManager *m_self;
+
+ typedef QMap<Field, ConditionWidgetHandler*> ConditionHandlerMap;
+ ConditionHandlerMap m_conditionHandlers;
+
+ typedef QMap<Action::Type, ActionWidgetHandler*> ActionHandlerMap;
+ ActionHandlerMap m_actionHandlers;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/misc/interfaces/Makefile.am b/kftpgrabber/src/misc/interfaces/Makefile.am
new file mode 100644
index 0000000..8d88141
--- /dev/null
+++ b/kftpgrabber/src/misc/interfaces/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES = -I$(top_srcdir)/kftpgrabber/src/ftp \
+ -I$(top_srcdir)/kftpgrabber/src/qsa \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libkftpinterfaces.la
+libkftpinterfaces_la_SOURCES = kftpbookmarkimportplugin.cpp
+libkftpinterfaces_la_LDFLAGS = $(all_libraries) -version-info 0:0:0 -no-undefined
+libkftpinterfaces_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS)
+
+kftpgrabberincludedir = $(includedir)/kftpgrabber
+kftpgrabberinclude_HEADERS = kftpbookmarkimportplugin.h
+
+kde_servicetypes_DATA = kftpbookmarkimportplugin.desktop
diff --git a/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.cpp b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.cpp
new file mode 100644
index 0000000..49a7322
--- /dev/null
+++ b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.cpp
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpbookmarkimportplugin.h"
+
+#include <qdir.h>
+
+KFTPBookmarkImportPlugin::KFTPBookmarkImportPlugin(QObject *parent, const char *name)
+ : KParts::Plugin(parent, name)
+{
+}
+
+
+KFTPBookmarkImportPlugin::~KFTPBookmarkImportPlugin()
+{
+}
+
+QString KFTPBookmarkImportPlugin::userPath(const QString &path)
+{
+ return QDir::homeDirPath() + "/" + path;
+}
+
+#include "kftpbookmarkimportplugin.moc"
diff --git a/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.desktop b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.desktop
new file mode 100644
index 0000000..48d124e
--- /dev/null
+++ b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.desktop
@@ -0,0 +1,29 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KFTPGrabber/BookmarkImportPlugin
+Comment=A KFTPGrabber plugin for importing bookmarks
+Comment[ar]=قابس KFTPGrabber لإستيراد علامات المواقع
+Comment[bg]=Приставка за импортиране на отметки в KFTPGrabber
+Comment[cs]=Modul KFTPGrabbera pro import záložek
+Comment[da]=Et KFTPGrabber plugin til at importere bogmærker
+Comment[de]=KFTPGrabber-Modul zum Lesezeichenimport
+Comment[el]=Ένα πρόσθετο του KFTPGrabber για την εισαγωγή σελιδοδεικτών
+Comment[es]=Un complemento de KFTPGrabber para importar marcadores
+Comment[et]=KFTPGrabberi järjehoidjate importimise plugin
+Comment[fr]=Un module KFTPGrabber pour l'importation de signets
+Comment[ga]=Breiseán KFTPGrabber le haghaidh iompórtála leabharmharcanna
+Comment[gl]=Un plugin de KFTPGrabber para importar marcadores
+Comment[it]=Un plugin per KFTPGrabber per importare i segnalibri
+Comment[ja]=KFTPGrabber ブックマークをインポートするプラグイン
+Comment[ka]=სანიშნეების იმპორტის მოდული KFTPGrabber-თვის
+Comment[lt]=KFTPGrabber įskiepis žymelių importavimui
+Comment[nl]=een KFTPGrabber-plugin voor het importeren van bladwijzers
+Comment[pt]=Um 'plugin' do KFTPGrabber para importar listas de favoritos
+Comment[pt_BR]=Um plug-in do KFTPGrabber para a importar favoritos
+Comment[sr]=Прикључак KFTPGrabber-а за увоз маркера
+Comment[sr@Latn]=Priključak KFTPGrabber-a za uvoz markera
+Comment[sv]=Ett insticksprogram för bokmärkesimport till KFTPgrabber
+Comment[tr]=Yer imlerini almak için KFTPGrabber eklentisi
+Comment[uk]=Втулок KFTPGrabber для імпортування закладок
+Comment[xx]=xxA KFTPGrabber plugin for importing bookmarksxx
+Comment[zh_CN]=导入书签的 KFTPGrabber 插件
diff --git a/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.h b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.h
new file mode 100644
index 0000000..e82a718
--- /dev/null
+++ b/kftpgrabber/src/misc/interfaces/kftpbookmarkimportplugin.h
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKIMPORTPLUGIN_H
+#define KFTPBOOKMARKIMPORTPLUGIN_H
+
+#include <kparts/plugin.h>
+
+/**
+ * This class is the base class for all bookmark import plugins.
+ *
+ * @author Jernej Kos
+ */
+class KFTPBookmarkImportPlugin : public KParts::Plugin {
+Q_OBJECT
+public:
+ KFTPBookmarkImportPlugin(QObject *parent, const char *name = 0);
+ virtual ~KFTPBookmarkImportPlugin();
+
+ /**
+ * This method should return the properly formated XML for KFTPGrabber
+ * bookmarks that is generated from the import.
+ *
+ * @return The @ref QDomDocument representation of XML
+ */
+ virtual QDomDocument getImportedXml() = 0;
+
+ /**
+ * This method should start the import procedure.
+ *
+ * @param fileName is the path to the file that will be imported
+ */
+ virtual void import(const QString &fileName) = 0;
+
+ /**
+ * This method should return the default path where the bookmarks could
+ * be located. The path must be relative to the user's home directory.
+ *
+ * @return The default path where bookmarks are located
+ */
+ virtual QString getDefaultPath() = 0;
+protected:
+ QString userPath(const QString &path);
+signals:
+ /**
+ * Progress of bookmark importing (in percent).
+ */
+ void progress(int percent);
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/kftpapi.cpp b/kftpgrabber/src/misc/kftpapi.cpp
new file mode 100644
index 0000000..e6498a2
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpapi.cpp
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpapi.h"
+#include "kftpwalletconnection.h"
+#include "kftppluginmanager.h"
+#include "kftpzeroconf.h"
+
+KFTPAPI *KFTPAPI::m_instance = 0L;
+
+KFTPAPI *KFTPAPI::getInstance()
+{
+ if (!m_instance)
+ m_instance = new KFTPAPI();
+
+ return m_instance;
+}
+
+KFTPAPI::KFTPAPI()
+{
+ m_zeroconfInterface = new KFTPZeroConf(this);
+}
+
+KFTPAPI::~KFTPAPI()
+{
+ m_instance = 0L;
+}
+
+KFTPPluginManager *KFTPAPI::pluginManager()
+{
+ return KFTPPluginManager::getInstance();
+}
+
+KFTPWalletConnection *KFTPAPI::walletConnection()
+{
+ return KFTPWalletConnection::getInstance();
+}
+
+MainWindow *KFTPAPI::mainWindow()
+{
+ return m_mainWindow;
+}
+
+KFTPZeroConf *KFTPAPI::zeroConfInterface()
+{
+ return m_zeroconfInterface;
+}
+
+#include "kftpapi.moc"
diff --git a/kftpgrabber/src/misc/kftpapi.h b/kftpgrabber/src/misc/kftpapi.h
new file mode 100644
index 0000000..3a77ab4
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpapi.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPAPI_H
+#define KFTPAPI_H
+
+#include <config.h>
+#include <qobject.h>
+
+#include "mainwindow.h"
+
+class KFTPPluginManager;
+class KFTPWalletConnection;
+class KFTPZeroConf;
+
+/**
+@author Jernej Kos
+*/
+class KFTPAPI : public QObject
+{
+Q_OBJECT
+friend class MainWindow;
+public:
+ KFTPAPI();
+ ~KFTPAPI();
+
+ static KFTPAPI *getInstance();
+
+ KFTPPluginManager *pluginManager();
+ KFTPWalletConnection *walletConnection();
+ MainWindow *mainWindow();
+
+ KFTPZeroConf *zeroConfInterface();
+private:
+ static KFTPAPI *m_instance;
+
+ MainWindow *m_mainWindow;
+ KFTPZeroConf *m_zeroconfInterface;
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/kftpgrabber.kcfg b/kftpgrabber/src/misc/kftpgrabber.kcfg
new file mode 100644
index 0000000..80cc583
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpgrabber.kcfg
@@ -0,0 +1,319 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
+<kcfg>
+ <kcfgfile name="kftpgrabberrc"/>
+
+ <!-- Includes -->
+ <include>qsize.h</include>
+ <include>qpoint.h</include>
+ <include>qdir.h</include>
+ <include>configbase.h</include>
+
+ <!-- Config groups -->
+ <group name="MainWindow">
+ <entry name="size" type="Size">
+ <default code="true">QSize(800, 500)</default>
+ <label>The size of the main window.</label>
+ </entry>
+ <entry name="position" type="Point">
+ <default code="true">QPoint(0, 0)</default>
+ <label>The position of the main window on the screen.</label>
+ </entry>
+ </group>
+
+ <group name="General">
+ <entry name="retryCount" type="Int">
+ <default>10</default>
+ <min>0</min>
+ <label>The default retry count for new sites.</label>
+ </entry>
+
+ <entry name="retryTime" type="Int">
+ <default>60</default>
+ <min>0</min>
+ <label>The default retry delay for new sites.</label>
+ </entry>
+
+ <entry name="showBalloons" type="Bool">
+ <default>true</default>
+ <label>Should a balloon be displayed when some actions complete.</label>
+ </entry>
+
+ <entry name="showBalloonWhenQueueEmpty" type="Bool">
+ <default>false</default>
+ <label>Should a balloon be displayed when all queued transfers are completed.</label>
+ </entry>
+
+ <entry name="showRetrySuccessBalloon" type="Bool">
+ <default>true</default>
+ <label>Should a balloon be displayed when a connection to the server is successfully established after retrying.</label>
+ </entry>
+
+ <entry name="confirmExit" type="Bool">
+ <default>true</default>
+ <label>Should the user confirm exit if there are transfers running.</label>
+ </entry>
+
+ <entry name="encryptBookmarks" type="Bool">
+ <default>false</default>
+ <label>Encryption status of the bookmarks file.</label>
+ </entry>
+
+ <entry name="defLocalDir" type="Path">
+ <default code="true">QDir::homeDirPath()</default>
+ <label>Default local directory.</label>
+ </entry>
+
+ <entry name="exitOnClose" type="Bool">
+ <default>false</default>
+ <label>Should the application exit when users clicks the X button.</label>
+ </entry>
+
+ <entry name="startMinimized" type="Bool">
+ <default>false</default>
+ <label>Should the application be started minimized.</label>
+ </entry>
+
+ <entry name="showSplash" type="Bool">
+ <default>true</default>
+ <label>Should the splash screen be displayed when starting the application.</label>
+ </entry>
+
+ <entry name="showSystrayIcon" type="Bool">
+ <default>true</default>
+ <label>Should the systray icon be displayed.</label>
+ </entry>
+
+ <entry name="showWalletSites" type="Bool">
+ <default>false</default>
+ <label>Should the sites from KWallet be shown among the bookmarks.</label>
+ </entry>
+
+ <entry name="confirmDisconnects" type="Bool">
+ <default>true</default>
+ <label>Should a "confirm disconnect" dialog be displayed each time a disconnect is requested.</label>
+ </entry>
+
+ <entry name="defEncoding" type="String">
+ <default>iso 8859-1</default>
+ <label>The default site encoding.</label>
+ </entry>
+
+ <entry name="recentSites" type="StringList">
+ <label>Recent sites accessed via quick connect.</label>
+ </entry>
+ </group>
+
+ <group name="Actions">
+ <entry name="downloadActions" type="String">
+ <default>4;4;4;4;4;4;4;4;4;</default>
+ </entry>
+
+ <entry name="uploadActions" type="String">
+ <default>4;4;4;4;4;4;4;4;4;</default>
+ </entry>
+
+ <entry name="fxpActions" type="String">
+ <default>4;4;4;4;4;4;4;4;4;</default>
+ </entry>
+ </group>
+
+ <group name="Filters">
+ <entry name="asciiList" type="StringList">
+ <default>.txt,.bat,.php,.asp,.htm,.html,.css,.cpp,.h,.hpp,.js,.inc,.nfo,.pl,.sh,.xml,.sql</default>
+ <label>A list of file patters where ASCII mode should be used for transfer.</label>
+ </entry>
+
+ <entry name="skipEmptyDirs" type="Bool">
+ <default>false</default>
+ <label>Should empty directories be skipped.</label>
+ </entry>
+ </group>
+
+ <group name="Log">
+ <entry name="logFont" type="Font">
+ <label>The font that should be used for the log widget.</label>
+ <default code="true">KGlobalSettings::generalFont()</default>
+ </entry>
+
+ <entry name="logCommandsColor" type="Color">
+ <default code="true">QColor(67, 170, 23)</default>
+ <label>The color of the commands sent to the server.</label>
+ </entry>
+
+ <entry name="logResponsesColor" type="Color">
+ <default code="true">QColor(0, 0, 255)</default>
+ <label>The color of the responses from the server.</label>
+ </entry>
+
+ <entry name="logMultilineColor" type="Color">
+ <default code="true">QColor(148, 188, 22)</default>
+ <label>The color of the multiline responses from the server.</label>
+ </entry>
+
+ <entry name="logErrorColor" type="Color">
+ <default code="true">QColor(255, 0, 0)</default>
+ <label>The color of the error messages.</label>
+ </entry>
+
+ <entry name="logStatusColor" type="Color">
+ <default code="true">QColor(0, 0, 0)</default>
+ <label>The color of the status messages.</label>
+ </entry>
+
+ <entry name="saveToFile" type="Bool">
+ <default>false</default>
+ <label>Should the log be written to a file as well.</label>
+ </entry>
+
+ <entry name="outputFilename" type="Path">
+ <label>The file to which the log should be written.</label>
+ </entry>
+ </group>
+
+ <group name="Transfers">
+ <entry name="activeForcePort" type="Bool">
+ <default>false</default>
+ <label>Should a port from a specified portrange be selected on active transfers.</label>
+ </entry>
+
+ <entry name="activeMinPort" type="Int">
+ <default>0</default>
+ <min>0</min>
+ <max>65536</max>
+ <label>The start of the portrange.</label>
+ </entry>
+
+ <entry name="activeMaxPort" type="Int">
+ <default>65536</default>
+ <min>0</min>
+ <max>65536</max>
+ <label>The end of the portrange.</label>
+ </entry>
+
+ <entry name="portForceIp" type="Bool">
+ <default>false</default>
+ <label>Should an IP be overriden when doing active transfers.</label>
+ </entry>
+
+ <entry name="portIp" type="String">
+ <label>The IP to be sent when overriding the PORT command.</label>
+ </entry>
+
+ <entry name="ignoreExternalIpForLan" type="Bool">
+ <default>true</default>
+ <label>Should the external IP be ignored for LAN connections.</label>
+ </entry>
+
+ <entry name="queueOnDND" type="Bool">
+ <default>false</default>
+ <label>Should the transfers be queued insted of started when using drag and drop.</label>
+ </entry>
+
+ <entry name="diskCheckSpace" type="Bool">
+ <default>false</default>
+ <label>Should kftpgrabber check for free space and abort the transfer when there is not enough free.</label>
+ </entry>
+
+ <entry name="diskCheckInterval" type="Int">
+ <default>60</default>
+ <min>30</min>
+ <label>Interval for disk checking.</label>
+ </entry>
+
+ <entry name="diskMinFreeSpace" type="Int">
+ <default>500</default>
+ <min>0</min>
+ <label>Minimum free space (in MiB) that must be available.</label>
+ </entry>
+
+ <entry name="globalMail" type="Bool">
+ <default>true</default>
+ <label>Use global KDE e-mail address for anonymous passwords.</label>
+ </entry>
+
+ <entry name="anonMail" type="String">
+ <default code="true">getGlobalMail()</default>
+ <label>The e-mail address that should be used for anonymous passwords.</label>
+ </entry>
+
+ <entry name="threadCount" type="Int">
+ <default>1</default>
+ <min>1</min>
+ <max>10</max>
+ <label>Number of threads to use when transfering.</label>
+ </entry>
+
+ <entry name="threadUsePrimary" type="Bool">
+ <default>true</default>
+ <label>Should the primary connection be used for transfers.</label>
+ </entry>
+
+ <entry name="controlTimeout" type="Int">
+ <default>60</default>
+ <min>10</min>
+ <label>Timeout (in seconds) for the control connection.</label>
+ </entry>
+
+ <entry name="dataTimeout" type="Int">
+ <default>60</default>
+ <min>10</min>
+ <max>300</max>
+ <label>Timeout (in seconds) for data transfers.</label>
+ </entry>
+
+ <entry name="downloadSpeedLimit" type="Int">
+ <default>0</default>
+ <label>Global download speed limit (kbytes/s).</label>
+ </entry>
+
+ <entry name="uploadSpeedLimit" type="Int">
+ <default>0</default>
+ <label>Global upload speed limit (kbytes/s).</label>
+ </entry>
+
+ <entry name="failedAutoRetry" type="Bool">
+ <default>false</default>
+ <label>Should failed transfers be automaticly retried.</label>
+ </entry>
+
+ <entry name="failedAutoRetryCount" type="Int">
+ <default>1</default>
+ <min>1</min>
+ <max>20</max>
+ <label>Maximum number of retries before marking transfer as failed.</label>
+ </entry>
+ </group>
+
+ <group name="Display">
+ <entry name="showTree" type="Bool">
+ <default>true</default>
+ <label>Should the directory tree be shown by default.</label>
+ </entry>
+
+ <entry name="showHiddenFiles" type="Bool">
+ <default>true</default>
+ <label>Should hidden files be shown when browsing.</label>
+ </entry>
+
+ <entry name="showSizeInBytes" type="Bool">
+ <default>false</default>
+ <label>Should the filesize be shown in bytes rather than in "human readable" form.</label>
+ </entry>
+
+ <entry name="showOwnerGroup" type="Bool">
+ <default>false</default>
+ <label>Should the owner and group be shown for each file.</label>
+ </entry>
+
+ <entry name="showDirectorySize" type="Bool">
+ <default>false</default>
+ <label>Show directory size.</label>
+ </entry>
+
+ <entry name="showLeftSidebar" type="Bool">
+ <default>false</default>
+ <label>Show left sidebar.</label>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kftpgrabber/src/misc/kftpotpgenerator.cpp b/kftpgrabber/src/misc/kftpotpgenerator.cpp
new file mode 100644
index 0000000..ea6bb73
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpotpgenerator.cpp
@@ -0,0 +1,470 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpotpgenerator.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* OpenSSL includes */
+#include <openssl/md4.h>
+#include <openssl/md5.h>
+#include <openssl/ripemd.h>
+#include <openssl/sha.h>
+
+KFTPOTPGenerator::KFTPOTPGenerator(const QString &challenge, const QString &password)
+{
+ m_password = password;
+
+ /* Parse the string and set the alg */
+ //otp-md5 498 gr4542
+ QString alg = challenge.section(' ', 0, 0);
+ if (alg == "otp-md4" || alg == "s/key")
+ m_alg = ALG_MD4;
+ else if (alg == "otp-md5")
+ m_alg = ALG_MD5;
+ else if (alg == "otp-rmd160")
+ m_alg = ALG_RMD160;
+ else if (alg == "otp-sha1")
+ m_alg = ALG_SHA1;
+ else
+ m_alg = ALG_NONE;
+
+ // Extract the actual challenge
+ m_seq = challenge.section(' ', 1, 1).toInt();
+ m_seed = challenge.section(' ', 2, 2).stripWhiteSpace().lower();
+}
+
+void KFTPOTPGenerator::genDigest(char *msg, unsigned int len)
+{
+ switch (m_alg) {
+ case ALG_MD4: genDigestMD(4, msg, len); break;
+ case ALG_MD5: genDigestMD(5, msg, len); break;
+ case ALG_RMD160: genDigestRS(0, msg, len); break;
+ case ALG_SHA1: genDigestRS(1, msg, len); break;
+ default: {
+ qDebug("[OTP] Unknown algorythm!");
+ }
+ }
+}
+
+QString KFTPOTPGenerator::generateOTP()
+{
+ if (m_alg == ALG_NONE)
+ return QString::null;
+
+ unsigned char results[9];
+ char *tmp = (char*) malloc(m_seed.length() + m_password.length());
+ strcpy(tmp, QString(m_seed + m_password).ascii());
+ genDigest(tmp, strlen(tmp));
+
+ memcpy(results, tmp, 8);
+ free(tmp);
+
+ for (unsigned short i = 0; i < m_seq; i++) {
+ genDigest((char*) results, 8);
+ }
+
+ results[8] = parity(results);
+ char *response = (char*) malloc(31);
+ sixWords(results, response);
+
+ return QString(response);
+}
+
+void KFTPOTPGenerator::genDigestMD(int type, char *msg, unsigned int len)
+{
+ unsigned char digest[16];
+ unsigned short i;
+
+ if (type == 4)
+ MD4((const unsigned char*)msg, len, digest);
+ else if (type == 5)
+ MD5((const unsigned char*)msg, len, digest);
+
+ for(i = 0; i < 8; i++)
+ digest[i] ^= digest[i+8];
+
+ memcpy(msg, digest, 8);
+}
+
+void KFTPOTPGenerator::genDigestRS(int type, char *msg, unsigned int len)
+{
+ unsigned char digest[20];
+ unsigned short i;
+
+ if (type == 0)
+ RIPEMD160((const unsigned char*)msg, len, digest);
+ else if (type == 1)
+ SHA1((const unsigned char*)msg, len, digest);
+
+ for(i = 0; i < 8; i++)
+ digest[i] ^= digest[i+8];
+
+ for(i = 0; i < 4; i++)
+ digest[i] ^= digest[i+16];
+
+ memcpy(msg, digest, 8);
+}
+
+unsigned short KFTPOTPGenerator::extract(char *s, int start, int len)
+{
+ unsigned char cl;
+ unsigned char cc;
+ unsigned char cr;
+ unsigned int x;
+
+
+ cl = s[start / 8];
+ cc = s[start / 8 + 1];
+ cr = s[start / 8 + 2];
+ x = ((int) (cl << 8 | cc) << 8 | cr);
+ x = x >> (24 - (len + (start % 8)));
+ x = (x & (0xffff >> (16 - len)));
+
+ return (unsigned short)x;
+}
+
+unsigned char KFTPOTPGenerator::parity(unsigned char *msg)
+{
+
+ unsigned short parity, i;
+ parity = 0;
+
+ for (i = 0; i < 64; i += 2) {
+ parity += extract((char*)msg, i, 2);
+ }
+
+ return (unsigned char)parity << 6;
+}
+
+unsigned char *KFTPOTPGenerator::sixWords(unsigned char *msg, char *response)
+{
+ static const char *words[2048] = {
+ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", "AGO",
+ "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN",
+ "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT",
+ "ARC", "ARE", "ARK", "ARM", "ART", "AS", "ASH", "ASK",
+ "AT", "ATE", "AUG", "AUK", "AVE", "AWE", "AWK", "AWL",
+ "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", "BAN",
+ "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN",
+ "BET", "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB",
+ "BOG", "BON", "BOO", "BOP", "BOW", "BOY", "BUB", "BUD",
+ "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY", "BYE",
+ "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", "CAW",
+ "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW",
+ "COY", "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB",
+ "DAD", "DAM", "DAN", "DAR", "DAY", "DEE", "DEL", "DEN",
+ "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP", "DO",
+ "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", "DUD",
+ "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG",
+ "EGO", "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST",
+ "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN", "FAR",
+ "FAT", "FAY", "FED", "FEE", "FEW", "FIB", "FIG", "FIN",
+ "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", "FRY",
+ "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM",
+ "GAP", "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG",
+ "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS", "GUT",
+ "GUY", "GYM", "GYP", "HA", "HAD", "HAL", "HAM", "HAN",
+ "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", "HEN",
+ "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS",
+ "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT",
+ "HOW", "HUB", "HUE", "HUG", "HUH", "HUM", "HUT", "I",
+ "ICY", "IDA", "IF", "IKE", "ILL", "INK", "INN", "IO",
+ "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", "ITS",
+ "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY",
+ "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT",
+ "JOY", "JUG", "JUT", "KAY", "KEG", "KEN", "KEY", "KID",
+ "KIM", "KIN", "KIT", "LA", "LAB", "LAC", "LAD", "LAG",
+ "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", "LEG",
+ "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP",
+ "LIT", "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU",
+ "LOW", "LOY", "LUG", "LYE", "MA", "MAC", "MAD", "MAE",
+ "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME", "MEG",
+ "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", "MOB",
+ "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD",
+ "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT",
+ "NAY", "NE", "NED", "NEE", "NET", "NEW", "NIB", "NIL",
+ "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR", "NOT",
+ "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", "OAK",
+ "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH",
+ "OIL", "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE",
+ "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW", "OWE",
+ "OWL", "OWN", "OX", "PA", "PAD", "PAL", "PAM", "PAN",
+ "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", "PEN",
+ "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN",
+ "PIT", "PLY", "PO", "POD", "POE", "POP", "POT", "POW",
+ "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT", "QUO",
+ "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", "RAY", "REB",
+ "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", "RIO",
+ "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY",
+ "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD",
+ "SAG", "SAL", "SAM", "SAN", "SAP", "SAT", "SAW", "SAY",
+ "SEA", "SEC", "SEE", "SEN", "SET", "SEW", "SHE", "SHY",
+ "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", "SLY",
+ "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA",
+ "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB",
+ "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED", "TEE",
+ "TEN", "THE", "THY", "TIC", "TIE", "TIM", "TIN", "TIP",
+ "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", "TOW",
+ "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN",
+ "UP", "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD",
+ "WAG", "WAR", "WAS", "WAY", "WE", "WEB", "WED", "WEE",
+ "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON", "WOO",
+ "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", "YEA",
+ "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE",
+ "ABUT", "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS",
+ "ADAM", "ADDS", "ADEN", "AFAR", "AFRO", "AGEE", "AHEM",
+ "AHOY", "AIDA", "AIDE", "AIDS", "AIRY", "AJAR", "AKIN",
+ "ALAN", "ALEC", "ALGA", "ALIA", "ALLY", "ALMA", "ALOE",
+ "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", "AMES", "AMID",
+ "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", "ANNA",
+ "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA",
+ "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS",
+ "ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS",
+ "AVON", "AVOW", "AWAY", "AWRY", "BABE", "BABY", "BACH",
+ "BACK", "BADE", "BAIL", "BAIT", "BAKE", "BALD", "BALE",
+ "BALI", "BALK", "BALL", "BALM", "BAND", "BANE", "BANG",
+ "BANK", "BARB", "BARD", "BARE", "BARK", "BARN", "BARR",
+ "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", "BAWD",
+ "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT",
+ "BEAU", "BECK", "BEEF", "BEEN", "BEER", "BEET", "BELA",
+ "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT",
+ "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE",
+ "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD",
+ "BITE", "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB",
+ "BLOC", "BLOT", "BLOW", "BLUE", "BLUM", "BLUR", "BOAR",
+ "BOAT", "BOCA", "BOCK", "BODE", "BODY", "BOGY", "BOHR",
+ "BOIL", "BOLD", "BOLO", "BOLT", "BOMB", "BONA", "BOND",
+ "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", "BOON",
+ "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH",
+ "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN",
+ "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK",
+ "BUDD", "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT",
+ "BUOY", "BURG", "BURL", "BURN", "BURR", "BURT", "BURY",
+ "BUSH", "BUSS", "BUST", "BUSY", "BYTE", "CADY", "CAFE",
+ "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", "CAME",
+ "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", "CART",
+ "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", "CELL",
+ "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF",
+ "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB",
+ "CHUG", "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN",
+ "CLAW", "CLAY", "CLOD", "CLOG", "CLOT", "CLUB", "CLUE",
+ "COAL", "COAT", "COCA", "COCK", "COCO", "CODA", "CODE",
+ "CODY", "COED", "COIL", "COIN", "COKE", "COLA", "COLD",
+ "COLT", "COMA", "COMB", "COME", "COOK", "COOL", "COON",
+ "COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE",
+ "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB",
+ "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT",
+ "CUNY", "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS",
+ "DADE", "DALE", "DAME", "DANA", "DANE", "DANG", "DANK",
+ "DARE", "DARK", "DARN", "DART", "DASH", "DATA", "DATE",
+ "DAVE", "DAVY", "DAWN", "DAYS", "DEAD", "DEAF", "DEAL",
+ "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", "DEER",
+ "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL",
+ "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT",
+ "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK",
+ "DOES", "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM",
+ "DOOR", "DORA", "DOSE", "DOTE", "DOUG", "DOUR", "DOVE",
+ "DOWN", "DRAB", "DRAG", "DRAM", "DRAW", "DREW", "DRUB",
+ "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL", "DUET",
+ "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", "DUST",
+ "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", "EASY",
+ "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT",
+ "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL",
+ "EMIT", "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER",
+ "EVIL", "EYED", "FACE", "FACT", "FADE", "FAIL", "FAIN",
+ "FAIR", "FAKE", "FALL", "FAME", "FANG", "FARM", "FAST",
+ "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", "FEET",
+ "FELL", "FELT", "FEND", "FERN", "FEST", "FEUD", "FIEF",
+ "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK",
+ "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE",
+ "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED",
+ "FLEW", "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE",
+ "FOAL", "FOAM", "FOGY", "FOIL", "FOLD", "FOLK", "FOND",
+ "FONT", "FOOD", "FOOL", "FOOT", "FORD", "FORE", "FORK",
+ "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL", "FRAU",
+ "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM",
+ "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE",
+ "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA",
+ "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY",
+ "GASH", "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR",
+ "GELD", "GENE", "GENT", "GERM", "GETS", "GIBE", "GIFT",
+ "GILD", "GILL", "GILT", "GINA", "GIRD", "GIRL", "GIST",
+ "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB", "GLOM",
+ "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT",
+ "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD",
+ "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB",
+ "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM",
+ "GRIN", "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK",
+ "GURU", "GUSH", "GUST", "GWEN", "GWYN", "HAAG", "HAAS",
+ "HACK", "HAIL", "HAIR", "HALE", "HALF", "HALL", "HALO",
+ "HALT", "HAND", "HANG", "HANK", "HANS", "HARD", "HARK",
+ "HARM", "HART", "HASH", "HAST", "HATE", "HATH", "HAUL",
+ "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT",
+ "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL",
+ "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS",
+ "HEWN", "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT",
+ "HIND", "HINT", "HIRE", "HISS", "HIVE", "HOBO", "HOCK",
+ "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", "HOME", "HONE",
+ "HONK", "HOOD", "HOOF", "HOOK", "HOOT", "HORN", "HOSE",
+ "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", "HUCK",
+ "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL",
+ "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE",
+ "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA",
+ "INCH", "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA",
+ "IRON", "ISLE", "ITCH", "ITEM", "IVAN", "JACK", "JADE",
+ "JAIL", "JAKE", "JANE", "JAVA", "JEAN", "JEFF", "JERK",
+ "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", "JOAN",
+ "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE",
+ "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", "JUJU",
+ "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST",
+ "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE",
+ "KEEL", "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS",
+ "KICK", "KILL", "KIND", "KING", "KIRK", "KISS", "KITE",
+ "KLAN", "KNEE", "KNEW", "KNIT", "KNOB", "KNOT", "KNOW",
+ "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", "LACE",
+ "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", "LAKE",
+ "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK",
+ "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS",
+ "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK",
+ "LEER", "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK",
+ "LESS", "LEST", "LETS", "LIAR", "LICE", "LICK", "LIED",
+ "LIEN", "LIES", "LIEU", "LIFE", "LIFT", "LIKE", "LILA",
+ "LILT", "LILY", "LIMA", "LIMB", "LIME", "LIND", "LINE",
+ "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", "LOAD",
+ "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS",
+ "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD",
+ "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE",
+ "LUCK", "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG",
+ "LURA", "LURE", "LURK", "LUSH", "LUST", "LYLE", "LYNN",
+ "LYON", "LYRA", "MACE", "MADE", "MAGI", "MAID", "MAIL",
+ "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT", "MANA",
+ "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", "MART",
+ "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", "MATH",
+ "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK",
+ "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT",
+ "MESH", "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK",
+ "MILL", "MILT", "MIMI", "MIND", "MINE", "MINI", "MINK",
+ "MINT", "MIRE", "MISS", "MIST", "MITE", "MITT", "MOAN",
+ "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", "MOLT",
+ "MONA", "MONK", "MONT", "MOOD", "MOON", "MOOR", "MOOT",
+ "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE",
+ "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK",
+ "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY",
+ "NAIL", "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY",
+ "NEAL", "NEAR", "NEAT", "NECK", "NEED", "NEIL", "NELL",
+ "NEON", "NERO", "NESS", "NEST", "NEWS", "NEWT", "NIBS",
+ "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH", "NODE",
+ "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE",
+ "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH",
+ "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY",
+ "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT",
+ "ONCE", "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY",
+ "OSLO", "OTIS", "OTTO", "OUCH", "OUST", "OUTS", "OVAL",
+ "OVEN", "OVER", "OWLY", "OWNS", "QUAD", "QUIT", "QUOD",
+ "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID", "RAIL",
+ "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE",
+ "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK",
+ "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA",
+ "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE",
+ "RIFT", "RILL", "RIME", "RING", "RINK", "RISE", "RISK",
+ "RITE", "ROAD", "ROAM", "ROAR", "ROBE", "ROCK", "RODE",
+ "ROIL", "ROLL", "ROME", "ROOD", "ROOF", "ROOK", "ROOM",
+ "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH", "ROUT",
+ "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", "RUDE", "RUDY",
+ "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH",
+ "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE",
+ "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND",
+ "SANE", "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS",
+ "SCAN", "SCAR", "SCAT", "SCOT", "SEAL", "SEAM", "SEAR",
+ "SEAT", "SEED", "SEEK", "SEEM", "SEEN", "SEES", "SELF",
+ "SELL", "SEND", "SENT", "SETS", "SEWN", "SHAG", "SHAM",
+ "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", "SHOE",
+ "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT",
+ "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE",
+ "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT",
+ "SKEW", "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM",
+ "SLAT", "SLAY", "SLED", "SLEW", "SLID", "SLIM", "SLIT",
+ "SLOB", "SLOG", "SLOT", "SLOW", "SLUG", "SLUM", "SLUR",
+ "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", "SNUG",
+ "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL",
+ "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", "SORT",
+ "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR",
+ "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN",
+ "SUCH", "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK",
+ "SURE", "SURF", "SWAB", "SWAG", "SWAM", "SWAN", "SWAT",
+ "SWAY", "SWIM", "SWUM", "TACK", "TACT", "TAIL", "TAKE",
+ "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", "TAUT",
+ "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", "TEET",
+ "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST",
+ "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN",
+ "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED",
+ "TIER", "TILE", "TILL", "TILT", "TIME", "TINA", "TINE",
+ "TINT", "TINY", "TIRE", "TOAD", "TOGO", "TOIL", "TOLD",
+ "TOLL", "TONE", "TONG", "TONY", "TOOK", "TOOL", "TOOT",
+ "TORE", "TORN", "TOTE", "TOUR", "TOUT", "TOWN", "TRAG",
+ "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", "TRIO",
+ "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK",
+ "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK",
+ "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED",
+ "USER", "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY",
+ "VASE", "VAST", "VEAL", "VEDA", "VEIL", "VEIN", "VEND",
+ "VENT", "VERB", "VERY", "VETO", "VICE", "VIEW", "VINE",
+ "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE", "WAGE",
+ "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", "WALT",
+ "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", "WARN",
+ "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY",
+ "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK",
+ "WEIR", "WELD", "WELL", "WELT", "WENT", "WERE", "WERT",
+ "WEST", "WHAM", "WHAT", "WHEE", "WHEN", "WHET", "WHOA",
+ "WHOM", "WICK", "WIFE", "WILD", "WILL", "WIND", "WINE",
+ "WING", "WINK", "WINO", "WIRE", "WISE", "WISH", "WITH",
+ "WOLF", "WONT", "WOOD", "WOOL", "WORD", "WORE", "WORK",
+ "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG",
+ "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR",
+ "YELL", "YOGA", "YOKE"
+ };
+
+ snprintf(response, 31, "%s %s %s %s %s %s",
+ words[extract((char*)msg, 0, 11)],
+ words[extract((char*)msg, 11, 11)],
+ words[extract((char*)msg, 22, 11)],
+ words[extract((char*)msg, 33, 11)],
+ words[extract((char*)msg, 44, 11)],
+ words[extract((char*)msg, 55, 11)]);
+
+ return (unsigned char*)response;
+}
diff --git a/kftpgrabber/src/misc/kftpotpgenerator.h b/kftpgrabber/src/misc/kftpotpgenerator.h
new file mode 100644
index 0000000..5292ead
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpotpgenerator.h
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPOTPGENERATOR_H
+#define KFTPOTPGENERATOR_H
+
+#include <qstring.h>
+
+enum AlgType {
+ ALG_MD4,
+ ALG_MD5,
+ ALG_RMD160,
+ ALG_SHA1,
+ ALG_NONE
+};
+
+/*
+ KFTPOTPGenerator - OTP generator for purpuse of FTP auth, based on code from
+ otpCalc copyright (C) 2001 by Anthony D. Urso
+*/
+
+/**
+@author Jernej Kos
+*/
+class KFTPOTPGenerator{
+public:
+ KFTPOTPGenerator(const QString &challenge, const QString &password);
+ QString generateOTP();
+private:
+ QString m_seed;
+ QString m_password;
+ AlgType m_alg;
+ int m_seq;
+
+ void genDigest(char *msg, unsigned int len);
+ void genDigestMD(int type, char *msg, unsigned int len);
+ void genDigestRS(int type, char *msg, unsigned int len);
+ static unsigned short extract(char *s, int start, int len);
+ unsigned char parity(unsigned char *msg);
+ unsigned char *sixWords(unsigned char *msg, char *response);
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/kftppluginmanager.cpp b/kftpgrabber/src/misc/kftppluginmanager.cpp
new file mode 100644
index 0000000..fc9dbb0
--- /dev/null
+++ b/kftpgrabber/src/misc/kftppluginmanager.cpp
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftppluginmanager.h"
+#include "kftpbookmarkimportplugin.h"
+
+KFTPPluginManager *KFTPPluginManager::m_instance = 0L;
+
+KFTPPluginManager *KFTPPluginManager::getInstance()
+{
+ if (!m_instance)
+ m_instance = new KFTPPluginManager();
+
+ return m_instance;
+}
+
+KFTPPluginManager::KFTPPluginManager(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+}
+
+void KFTPPluginManager::loadPlugins()
+{
+}
+
+KTrader::OfferList KFTPPluginManager::getImportPlugins()
+{
+ return KTrader::self()->query("KFTPGrabber/BookmarkImportPlugin");
+}
+
+KFTPBookmarkImportPlugin *KFTPPluginManager::loadImportPlugin(KService::Ptr service)
+{
+ int errCode = 0;
+
+ KFTPBookmarkImportPlugin *plugin = KParts::ComponentFactory::createInstanceFromService<KFTPBookmarkImportPlugin>(service, this, 0, QStringList(), &errCode);
+
+ if (plugin) {
+ qDebug("[PLUGIN MANAGER] Loaded '%s' plugin for bookmark import.", service->name().ascii());
+ }
+
+ return plugin;
+}
+
+#include "kftppluginmanager.moc"
diff --git a/kftpgrabber/src/misc/kftppluginmanager.h b/kftpgrabber/src/misc/kftppluginmanager.h
new file mode 100644
index 0000000..b377a62
--- /dev/null
+++ b/kftpgrabber/src/misc/kftppluginmanager.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPPLUGINMANAGER_H
+#define KFTPPLUGINMANAGER_H
+
+#include <kparts/componentfactory.h>
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+class KFTPBookmarkImportPlugin;
+
+/**
+ * This class is responsible for loading all the KFTPGrabber plugins and
+ * their communication with the rest of the application.
+ *
+ * @author Jernej Kos
+ */
+class KFTPPluginManager : public QObject
+{
+Q_OBJECT
+public:
+ KFTPPluginManager(QObject *parent = 0, const char *name = 0);
+ static KFTPPluginManager *getInstance();
+
+ /**
+ * This method will load all the plugins.
+ */
+ void loadPlugins();
+
+ /**
+ * Load bookmark import plugin.
+ *
+ * @param service The plugin KService::Ptr
+ * @return The loaded plugin or NULL if plugin can't be loaded
+ */
+ KFTPBookmarkImportPlugin *loadImportPlugin(KService::Ptr service);
+
+ /**
+ * Returns the list of all currently loaded import plugins.
+ *
+ * @return List of all laoded import plugins
+ */
+ KTrader::OfferList getImportPlugins();
+private:
+ static KFTPPluginManager *m_instance;
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/kftpwalletconnection.cpp b/kftpgrabber/src/misc/kftpwalletconnection.cpp
new file mode 100644
index 0000000..242b00d
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpwalletconnection.cpp
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <kurl.h>
+
+#include "kftpwalletconnection.h"
+
+KFTPWalletConnection *KFTPWalletConnection::m_instance = 0L;
+
+KFTPWalletConnection *KFTPWalletConnection::getInstance()
+{
+ if (!m_instance)
+ m_instance = new KFTPWalletConnection();
+
+ return m_instance;
+}
+
+KFTPWalletConnection::KFTPWalletConnection()
+ : QObject()
+{
+ m_wallet = 0L;
+ m_walletRefCount = 0;
+}
+
+
+KFTPWalletConnection::~KFTPWalletConnection()
+{
+ m_instance = 0L;
+ slotWalletClosed();
+}
+
+void KFTPWalletConnection::slotWalletClosed()
+{
+ m_walletRefCount--;
+ if (m_walletRefCount == 0) {
+ delete m_wallet;
+ m_wallet = 0L;
+ }
+}
+
+QValueList<KURL> KFTPWalletConnection::getSiteList()
+{
+ QValueList<KURL> sites;
+
+ if (!KWallet::Wallet::folderDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder())) {
+ if (!m_wallet) {
+ m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Synchronous);
+
+ if (m_wallet) {
+ m_walletRefCount++;
+ connect(m_wallet, SIGNAL(walletClosed()), this, SLOT(slotWalletClosed()));
+ }
+ }
+
+ if (!m_wallet)
+ return QValueList<KURL>();
+
+ // Get the site list from our wallet
+ m_wallet->setFolder(KWallet::Wallet::PasswordFolder());
+
+ QStringList list = m_wallet->entryList();
+ QStringList::iterator i;
+
+ for (i = list.begin(); i != list.end(); ++i) {
+ QMap<QString, QString> map;
+
+ if ((*i).startsWith("ftp-") && m_wallet->readMap(*i, map) == 0) {
+ QString name = *i;
+ name.replace("ftp-", "ftp://");
+
+ KURL siteUrl(name);
+ siteUrl.setUser(map["login"]);
+ siteUrl.setPass(map["password"]);
+
+ if (siteUrl.port() == 0)
+ siteUrl.setPort(21);
+
+ if (sites.contains(siteUrl) == 0)
+ sites.append(siteUrl);
+ }
+ }
+ }
+
+ return sites;
+}
+
+QString KFTPWalletConnection::getPassword(const QString &whatFor)
+{
+ if (!KWallet::Wallet::folderDoesNotExist(KWallet::Wallet::NetworkWallet(), QString::fromLatin1("KFTPGrabber"))) {
+ // We have our own folder
+ if (!m_wallet) {
+ m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Synchronous);
+
+ if (m_wallet) {
+ m_walletRefCount++;
+ connect(m_wallet, SIGNAL(walletClosed()), this, SLOT(slotWalletClosed()));
+ }
+ }
+
+ // Try to read the password from the wallet
+ QString pass;
+ if (m_wallet && m_wallet->setFolder(QString::fromLatin1("KFTPGrabber")) && m_wallet->readPassword(whatFor, pass) == 0) {
+ return pass;
+ }
+ }
+
+ return QString::null;
+}
+
+void KFTPWalletConnection::setPassword(const QString &whatFor, const QString &password)
+{
+ if (KWallet::Wallet::isEnabled()) {
+ if (!m_wallet) {
+ m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Synchronous);
+
+ if (m_wallet) {
+ m_walletRefCount++;
+ connect(m_wallet, SIGNAL(walletClosed()), this, SLOT(slotWalletClosed()));
+ }
+ }
+
+ if (m_wallet) {
+ // Create our folder
+ if (!m_wallet->hasFolder(QString::fromLatin1("KFTPGrabber"))) {
+ m_wallet->createFolder(QString::fromLatin1("KFTPGrabber"));
+ }
+
+ m_wallet->setFolder(QString::fromLatin1("KFTPGrabber"));
+ m_wallet->writePassword(whatFor, password);
+ }
+ }
+}
+
+#include "kftpwalletconnection.moc"
diff --git a/kftpgrabber/src/misc/kftpwalletconnection.h b/kftpgrabber/src/misc/kftpwalletconnection.h
new file mode 100644
index 0000000..782d0d3
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpwalletconnection.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWALLETCONNECTION_H
+#define KFTPWALLETCONNECTION_H
+
+#include <kwallet.h>
+#include <qobject.h>
+
+/**
+Enables communication of KFTPGrabber with KDE's wallet system (KWallet)
+
+@author Jernej Kos
+*/
+class KFTPWalletConnection : public QObject
+{
+Q_OBJECT
+public:
+ KFTPWalletConnection();
+ ~KFTPWalletConnection();
+
+ QValueList<KURL> getSiteList();
+
+ QString getPassword(const QString &whatFor);
+ void setPassword(const QString &whatFor, const QString &password);
+
+ static KFTPWalletConnection *getInstance();
+private:
+ static KFTPWalletConnection *m_instance;
+ KWallet::Wallet *m_wallet;
+ uint m_walletRefCount;
+private slots:
+ void slotWalletClosed();
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/kftpzeroconf.cpp b/kftpgrabber/src/misc/kftpzeroconf.cpp
new file mode 100644
index 0000000..39d1f63
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpzeroconf.cpp
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "kftpzeroconf.h"
+
+KFTPZeroConf::KFTPZeroConf(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+#if KDE_IS_VERSION(3,4,0)
+ m_browser = new DNSSD::ServiceBrowser("_ftp._tcp", 0, true);
+
+ connect(m_browser, SIGNAL(finished()), this, SLOT(slotServiceChanged()));
+
+ m_browser->startBrowse();
+#endif
+}
+
+
+KFTPZeroConf::~KFTPZeroConf()
+{
+}
+
+#if KDE_IS_VERSION(3,4,0)
+const QValueList<DNSSD::RemoteService::Ptr> &KFTPZeroConf::getServiceList() const
+{
+ return m_browser->services();
+}
+#endif
+
+void KFTPZeroConf::slotServiceChanged()
+{
+ emit servicesUpdated();
+}
+
+#include "kftpzeroconf.moc"
diff --git a/kftpgrabber/src/misc/kftpzeroconf.h b/kftpgrabber/src/misc/kftpzeroconf.h
new file mode 100644
index 0000000..68c9ca2
--- /dev/null
+++ b/kftpgrabber/src/misc/kftpzeroconf.h
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPZEROCONF_H
+#define KFTPZEROCONF_H
+
+#include <qobject.h>
+
+#include <kdeversion.h>
+
+#if KDE_IS_VERSION(3,4,0)
+#include <dnssd/servicebrowser.h>
+#endif
+
+/**
+ * This class provides an interface to KDNSSD (that is available from KDE 3.4).
+ *
+ * @author Jernej Kos
+ */
+class KFTPZeroConf : public QObject
+{
+Q_OBJECT
+public:
+ KFTPZeroConf(QObject *parent = 0, const char *name = 0);
+ ~KFTPZeroConf();
+
+#if KDE_IS_VERSION(3,4,0)
+ const QValueList<DNSSD::RemoteService::Ptr> &getServiceList() const;
+private:
+ DNSSD::ServiceBrowser *m_browser;
+#endif
+private slots:
+ void slotServiceChanged();
+signals:
+ void servicesUpdated();
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/libs/Makefile.am b/kftpgrabber/src/misc/libs/Makefile.am
new file mode 100644
index 0000000..b4be6a2
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/Makefile.am
@@ -0,0 +1,3 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+SUBDIRS = ssh
diff --git a/kftpgrabber/src/misc/libs/ssh/Makefile.am b/kftpgrabber/src/misc/libs/ssh/Makefile.am
new file mode 100644
index 0000000..91a0031
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+noinst_LIBRARIES = libssh.a
+libssh_a_SOURCES = auth.c base64.c buffer.c channels.c client.c connect.c\
+ crypt.c dh.c error.c gzip.c kex.c keyfiles.c keys.c misc.c options.c\
+ packet.c sftp.c string.c wrapper.c
+noinst_HEADERS = crypto.h libssh.h sftp.h ssh2.h
+AM_CFLAGS = -w
diff --git a/kftpgrabber/src/misc/libs/ssh/auth.c b/kftpgrabber/src/misc/libs/ssh/auth.c
new file mode 100644
index 0000000..88ac7ec
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/auth.c
@@ -0,0 +1,597 @@
+/* auth.c deals with authentication methods */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include "priv.h"
+#include "ssh2.h"
+#include <string.h>
+#include <netdb.h>
+
+static int ask_userauth(SSH_SESSION *session){
+ if(session->auth_service_asked)
+ return 0;
+ else {
+ if(ssh_service_request(session,"ssh-userauth"))
+ return -1;
+ else
+ session->auth_service_asked++;
+ }
+ return 0;
+
+}
+
+static void burn(char *ptr){
+ if(ptr)
+ memset(ptr,'X',strlen(ptr));
+}
+
+static void string_burn(STRING *s){
+ memset(s->string,'X',string_len(s));
+}
+
+static int wait_auth_status(SSH_SESSION *session,int kbdint){
+ int err=SSH_AUTH_ERROR;
+ int cont=1;
+ STRING *can_continue;
+ u8 partial=0;
+ char *c_cont;
+ while(cont){
+ if(packet_read(session))
+ break;
+ if(packet_translate(session))
+ break;
+ switch(session->in_packet.type){
+ case SSH2_MSG_USERAUTH_FAILURE:
+ can_continue=buffer_get_ssh_string(session->in_buffer);
+ if(!can_continue || buffer_get_u8(session->in_buffer,&partial)!=1 ){
+ ssh_set_error(session,SSH_INVALID_DATA,"invalid SSH_MSG_USERAUTH_FAILURE message");
+ return SSH_AUTH_ERROR;
+ }
+ c_cont=string_to_char(can_continue);
+ if(partial){
+ err=SSH_AUTH_PARTIAL;
+ ssh_set_error(session,SSH_NO_ERROR,"partial success, authentications that can continue : %s",c_cont);
+ }
+ else {
+ err=SSH_AUTH_DENIED;
+ ssh_set_error(session,SSH_REQUEST_DENIED,"Access denied. authentications that can continue : %s",c_cont);
+ }
+ free(can_continue);
+ free(c_cont);
+ cont=0;
+ break;
+ case SSH2_MSG_USERAUTH_PK_OK:
+ /* SSH monkeys have defined the same number for both */
+ /* SSH_MSG_USERAUTH_PK_OK and SSH_MSG_USERAUTH_INFO_REQUEST */
+ /* which is not really smart; */
+ /*case SSH2_MSG_USERAUTH_INFO_REQUEST: */
+ if(kbdint){
+ err=SSH_AUTH_INFO;
+ cont=0;
+ break;
+ }
+ /* continue through success */
+ case SSH2_MSG_USERAUTH_SUCCESS:
+ err=SSH_AUTH_SUCCESS;
+ cont=0;
+ break;
+ case SSH2_MSG_USERAUTH_BANNER:
+ {
+ STRING *banner=buffer_get_ssh_string(session->in_buffer);
+ if(!banner){
+ ssh_say(1,"The banner message was invalid. continuing though\n");
+ break;
+ }
+ ssh_say(2,"Received a message banner\n");
+ if(session->banner)
+ free(session->banner); /* erase the older one */
+ session->banner=banner;
+ break;
+ }
+ default:
+ packet_parse(session);
+ break;
+ }
+ }
+ return err;
+}
+
+/* use the "none" authentication question */
+
+int ssh_userauth_none(SSH_SESSION *session,char *username){
+ STRING *user;
+ STRING *service;
+ STRING *method;
+ if(!username)
+ if(!(username=session->options->username)){
+ if(options_default_username(session->options))
+ return SSH_AUTH_ERROR;
+ else
+ username=session->options->username;
+ }
+ if(ask_userauth(session))
+ return SSH_AUTH_ERROR;
+ user=string_from_char(username);
+ method=string_from_char("none");
+ service=string_from_char("ssh-connection");
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
+ buffer_add_ssh_string(session->out_buffer,user);
+ buffer_add_ssh_string(session->out_buffer,service);
+ buffer_add_ssh_string(session->out_buffer,method);
+ free(service);
+ free(method);
+ free(user);
+ packet_send(session);
+ return wait_auth_status(session,0);
+}
+
+int ssh_userauth_offer_pubkey(SSH_SESSION *session, char *username,int type, STRING *publickey){
+ STRING *user;
+ STRING *service;
+ STRING *method;
+ STRING *algo;
+ int err=SSH_AUTH_ERROR;
+ if(!username)
+ if(!(username=session->options->username)){
+ if(options_default_username(session->options))
+ return SSH_AUTH_ERROR;
+ else
+ username=session->options->username;
+ }
+ if(ask_userauth(session))
+ return SSH_AUTH_ERROR;
+ user=string_from_char(username);
+ service=string_from_char("ssh-connection");
+ method=string_from_char("publickey");
+ algo=string_from_char(ssh_type_to_char(type));
+
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
+ buffer_add_ssh_string(session->out_buffer,user);
+ buffer_add_ssh_string(session->out_buffer,service);
+ buffer_add_ssh_string(session->out_buffer,method);
+ buffer_add_u8(session->out_buffer,0);
+ buffer_add_ssh_string(session->out_buffer,algo);
+ buffer_add_ssh_string(session->out_buffer,publickey);
+ packet_send(session);
+ err=wait_auth_status(session,0);
+ free(user);
+ free(method);
+ free(service);
+ free(algo);
+ return err;
+}
+
+int ssh_userauth_pubkey(SSH_SESSION *session, char *username, STRING *publickey, PRIVATE_KEY *privatekey){
+ STRING *user;
+ STRING *service;
+ STRING *method;
+ STRING *algo;
+ STRING *sign;
+ int err=SSH_AUTH_ERROR;
+ if(!username)
+ if(!(username=session->options->username)){
+ if(options_default_username(session->options))
+ return err;
+ else
+ username=session->options->username;
+ }
+ if(ask_userauth(session))
+ return err;
+ user=string_from_char(username);
+ service=string_from_char("ssh-connection");
+ method=string_from_char("publickey");
+ algo=string_from_char(ssh_type_to_char(privatekey->type));
+
+
+ /* we said previously the public key was accepted */
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
+ buffer_add_ssh_string(session->out_buffer,user);
+ buffer_add_ssh_string(session->out_buffer,service);
+ buffer_add_ssh_string(session->out_buffer,method);
+ buffer_add_u8(session->out_buffer,1);
+ buffer_add_ssh_string(session->out_buffer,algo);
+ buffer_add_ssh_string(session->out_buffer,publickey);
+ sign=ssh_do_sign(session,session->out_buffer,privatekey);
+ if(sign){
+ buffer_add_ssh_string(session->out_buffer,sign);
+ free(sign);
+ packet_send(session);
+ err=wait_auth_status(session,0);
+ }
+ free(user);
+ free(service);
+ free(method);
+ free(algo);
+ return err;
+}
+
+int ssh_userauth_password(SSH_SESSION *session,char *username,char *password){
+ STRING *user;
+ STRING *service;
+ STRING *method;
+ STRING *password_s;
+ int err;
+ if(!username)
+ if(!(username=session->options->username)){
+ if(options_default_username(session->options))
+ return SSH_AUTH_ERROR;
+ else
+ username=session->options->username;
+ }
+ if(ask_userauth(session))
+ return SSH_AUTH_ERROR;
+ user=string_from_char(username);
+ service=string_from_char("ssh-connection");
+ method=string_from_char("password");
+ password_s=string_from_char(password);
+
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
+ buffer_add_ssh_string(session->out_buffer,user);
+ buffer_add_ssh_string(session->out_buffer,service);
+ buffer_add_ssh_string(session->out_buffer,method);
+ buffer_add_u8(session->out_buffer,0);
+ buffer_add_ssh_string(session->out_buffer,password_s);
+ free(user);
+ free(service);
+ free(method);
+ memset(password_s,0,strlen(password)+4);
+ free(password_s);
+ packet_send(session);
+ err=wait_auth_status(session,0);
+ return err;
+}
+
+static char *keys_path[]={NULL,"%s/.ssh/identity","%s/.ssh/id_dsa","%s/.ssh/id_rsa",NULL};
+static char *pub_keys_path[]={NULL,"%s/.ssh/identity.pub","%s/.ssh/id_dsa.pub","%s/.ssh/id_rsa.pub",NULL};
+
+/* this function initialy was in the client */
+/* but the fools are the ones who never change mind */
+int ssh_userauth_autopubkey(SSH_SESSION *session, const char *passphrase){
+ int count=1; /* bypass identity */
+ int type=0;
+ int err;
+ STRING *pubkey;
+ char *privkeyfile=NULL;
+ PRIVATE_KEY *privkey;
+ char *id=NULL;
+ /* always testing none */
+ err=ssh_userauth_none(session,NULL);
+ if(err==SSH_AUTH_ERROR || err==SSH_AUTH_SUCCESS){
+ return err;
+ }
+ if(session->options->identity){
+ ssh_say(2,"Trying identity file %s\n",session->options->identity);
+ keys_path[0]=session->options->identity;
+ /* let's hope alloca exists */
+ id=malloc(strlen(session->options->identity)+1 + 4);
+ sprintf(id,"%s.pub",session->options->identity);
+ pub_keys_path[0]=id;
+ count =0;
+ }
+ while((pubkey=publickey_from_next_file(session,pub_keys_path,keys_path, &privkeyfile,&type,&count))){
+ err=ssh_userauth_offer_pubkey(session,NULL,type,pubkey);
+ if(err==SSH_AUTH_ERROR){
+ if(id){
+ pub_keys_path[0]=NULL;
+ keys_path[0]=NULL;
+ free(id);
+ }
+ free(pubkey);
+ return err;
+ } else
+ if(err != SSH_AUTH_SUCCESS){
+ ssh_say(2,"Public key refused by server\n");
+ free(pubkey);
+ continue;
+ }
+ /* pubkey accepted by server ! */
+ privkey=privatekey_from_file(session,privkeyfile,type,passphrase);
+ if (!privkey) {
+ free(pubkey);
+
+ if (passphrase == NULL) {
+ return -666;
+ }
+
+ ssh_say(0, "Private key decryption failed with the provided password (%s).\n", ssh_get_error(session));
+ continue;
+ }
+ err=ssh_userauth_pubkey(session,NULL,pubkey,privkey);
+ if(err==SSH_AUTH_ERROR){
+ if(id){
+ pub_keys_path[0]=NULL;
+ keys_path[0]=NULL;
+ free(id);
+ }
+ free(pubkey);
+ private_key_free(privkey);
+ return err;
+ } else
+ if(err != SSH_AUTH_SUCCESS){
+ ssh_say(0,"Weird : server accepted our public key but refused the signature\nit might be a bug of libssh\n");
+ free(pubkey);
+ private_key_free(privkey);
+ continue;
+ }
+ /* auth success */
+ ssh_say(1,"Authentication using %s success\n",privkeyfile);
+ free(pubkey);
+ private_key_free(privkey);
+ free(privkeyfile);
+ if(id){
+ pub_keys_path[0]=NULL;
+ keys_path[0]=NULL;
+ free(id);
+ }
+ return SSH_AUTH_SUCCESS;
+ }
+ ssh_say(1,"Tried every public key, none matched\n");
+ ssh_set_error(session,SSH_NO_ERROR,"no public key matched");
+ if(id){
+ pub_keys_path[0]=NULL;
+ keys_path[0]=NULL;
+ free(id);
+ }
+
+ return SSH_AUTH_DENIED;
+}
+
+static struct ssh_kbdint *kbdint_new(){
+ struct ssh_kbdint *kbd=malloc(sizeof (struct ssh_kbdint));
+ memset(kbd,0,sizeof(*kbd));
+ return kbd;
+}
+
+
+static void kbdint_free(struct ssh_kbdint *kbd){
+ int i,n=kbd->nprompts;
+ if(kbd->name)
+ free(kbd->name);
+ if(kbd->instruction)
+ free(kbd->instruction);
+ if(kbd->prompts){
+ for(i=0;i<n;++i){
+ burn(kbd->prompts[i]);
+ free(kbd->prompts[i]);
+ }
+ free(kbd->prompts);
+ }
+ if(kbd->answers){
+ for(i=0;i<n;++i){
+ burn(kbd->answers[i]);
+ free(kbd->answers[i]);
+ }
+ free(kbd->answers);
+ }
+ if(kbd->echo){
+ free(kbd->echo);
+ }
+ free(kbd);
+}
+
+static void kbdint_clean(struct ssh_kbdint *kbd){
+ int i,n=kbd->nprompts;
+ if(kbd->name){
+ free(kbd->name);
+ kbd->name=NULL;
+ }
+ if(kbd->instruction){
+ free(kbd->instruction);
+ kbd->instruction=NULL;
+ }
+ if(kbd->prompts){
+ for(i=0;i<n;++i){
+ burn(kbd->prompts[i]);
+ free(kbd->prompts[i]);
+ }
+ free(kbd->prompts);
+ kbd->prompts=NULL;
+ }
+ if(kbd->answers){
+ for(i=0;i<n;++i){
+ burn(kbd->answers[i]);
+ free(kbd->answers[i]);
+ }
+ free(kbd->answers);
+ kbd->answers=NULL;
+ }
+ if(kbd->echo){
+ free(kbd->echo);
+ kbd->echo=NULL;
+ }
+ kbd->nprompts=0;
+}
+
+/* this function sends the first packet as explained in section 3.1
+ * of the draft */
+static int kbdauth_init(SSH_SESSION *session,
+ char *user, char *submethods){
+ STRING *user_s=string_from_char(user);
+ STRING *submethods_s=(submethods ? string_from_char(submethods): string_from_char(""));
+ STRING *service=string_from_char("ssh-connection");
+ STRING *method=string_from_char("keyboard-interactive");
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
+ buffer_add_ssh_string(session->out_buffer,user_s);
+ buffer_add_ssh_string(session->out_buffer,service);
+ buffer_add_ssh_string(session->out_buffer,method);
+ buffer_add_u32(session->out_buffer,0); /* language tag */
+ buffer_add_ssh_string(session->out_buffer,submethods_s);
+ free(user_s);
+ free(service);
+ free(method);
+ free(submethods_s);
+ if(packet_send(session))
+ return SSH_AUTH_ERROR;
+ return wait_auth_status(session,1);
+}
+
+static int kbdauth_info_get(SSH_SESSION *session){
+ STRING *name; /* name of the "asking" window showed to client */
+ STRING *instruction;
+ STRING *tmp;
+ u32 nprompts;
+ int i;
+ name=buffer_get_ssh_string(session->in_buffer);
+ instruction=buffer_get_ssh_string(session->in_buffer);
+ tmp=buffer_get_ssh_string(session->in_buffer);
+ buffer_get_u32(session->in_buffer,&nprompts);
+ if(!name || !instruction || !tmp){
+ if(name)
+ free(name);
+ if(instruction)
+ free(instruction);
+ /* tmp must be empty if we got here */
+ ssh_set_error(session,SSH_FATAL,"Invalid USERAUTH_INFO_REQUEST msg");
+ return SSH_AUTH_ERROR;
+ }
+ if(tmp)
+ free(tmp); /* no use */
+ if(!session->kbdint)
+ session->kbdint=kbdint_new();
+ else
+ kbdint_clean(session->kbdint);
+ session->kbdint->name=string_to_char(name);
+ free(name);
+ session->kbdint->instruction=string_to_char(instruction);
+ free(instruction);
+ nprompts=ntohl(nprompts);
+ if(nprompts>KBDINT_MAX_PROMPT){
+ ssh_set_error(session,SSH_FATAL,"Too much prompt asked from server: %lu(0x%.8lx)",nprompts,nprompts);
+ return SSH_AUTH_ERROR;
+ }
+ session->kbdint->nprompts=nprompts;
+ session->kbdint->prompts=malloc(nprompts*sizeof(char *));
+ memset(session->kbdint->prompts,0,nprompts*sizeof(char *));
+ session->kbdint->echo=malloc(nprompts);
+ memset(session->kbdint->echo,0,nprompts);
+ for(i=0;i<nprompts;++i){
+ tmp=buffer_get_ssh_string(session->in_buffer);
+ buffer_get_u8(session->in_buffer,&session->kbdint->echo[i]);
+ if(!tmp){
+ ssh_set_error(session,SSH_FATAL,"Short INFO_REQUEST packet");
+ return SSH_AUTH_ERROR;
+ }
+ session->kbdint->prompts[i]=string_to_char(tmp);
+ free(tmp);
+ }
+ return SSH_AUTH_INFO; /* we are not auth. but we parsed the packet */
+}
+
+/* sends challenge back to the server */
+static int kbdauth_send(SSH_SESSION *session) {
+ STRING *answer;
+ int i;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ buffer_add_u32(session->out_buffer,htonl(session->kbdint->nprompts));
+ for(i=0;i<session->kbdint->nprompts;++i){
+ if(session->kbdint->answers[i])
+ answer=string_from_char(session->kbdint->answers[i]);
+ else
+ answer=string_from_char("");
+ buffer_add_ssh_string(session->out_buffer,answer);
+ string_burn(answer);
+ free(answer);
+ }
+ if(packet_send(session))
+ return SSH_AUTH_ERROR;
+ return wait_auth_status(session,1);
+}
+
+/* the heart of the whole keyboard interactive login */
+int ssh_userauth_kbdint(SSH_SESSION *session,char *user,char *submethods){
+ int err;
+ if( !session->kbdint){
+ /* first time we call. we must ask for a challenge */
+ if(!user)
+ if(!(user=session->options->username)){
+ if(options_default_username(session->options))
+ return SSH_AUTH_ERROR;
+ else
+ user=session->options->username;
+ }
+ if(ask_userauth(session))
+ return SSH_AUTH_ERROR;
+ err=kbdauth_init(session,user,submethods);
+ if(err!=SSH_AUTH_INFO)
+ return err; /* error or first try success */
+ err=kbdauth_info_get(session);
+ if(err==SSH_AUTH_ERROR){
+ kbdint_free(session->kbdint);
+ session->kbdint=NULL;
+ }
+ return err;
+ }
+ /* if we are at this point, it's because session->kbdint exists */
+ /* it means the user has set some informations there we need to send *
+ * the server. and then we need to ack the status (new questions or ok *
+ * pass in */
+ err=kbdauth_send(session);
+ kbdint_free(session->kbdint);
+ session->kbdint=NULL;
+ if(err!=SSH_AUTH_INFO)
+ return err;
+ err=kbdauth_info_get(session);
+ if(err==SSH_AUTH_ERROR){
+ kbdint_free(session->kbdint);
+ session->kbdint=NULL;
+ }
+ return err;
+}
+
+int ssh_userauth_kbdint_getnprompts(SSH_SESSION *session){
+ return session->kbdint->nprompts;
+}
+
+char *ssh_userauth_kbdint_getname(SSH_SESSION *session){
+ return session->kbdint->name;
+}
+
+char *ssh_userauth_kbdint_getinstruction(SSH_SESSION *session){
+ return session->kbdint->instruction;
+}
+
+char *ssh_userauth_kbdint_getprompt(SSH_SESSION *session, int i,
+ char *echo){
+ if(i > session->kbdint->nprompts)
+ return NULL;
+ if(echo)
+ *echo=session->kbdint->echo[i];
+ return session->kbdint->prompts[i];
+}
+
+void ssh_userauth_kbdint_setanswer(SSH_SESSION *session, unsigned int i, char *answer){
+ if (i>session->kbdint->nprompts)
+ return;
+ if(!session->kbdint->answers){
+ session->kbdint->answers=malloc(sizeof(char*)*session->kbdint->nprompts);
+ memset(session->kbdint->answers,0,sizeof(char *) * session->kbdint->nprompts);
+ }
+ if(session->kbdint->answers[i]){
+ burn(session->kbdint->answers[i]);
+ free(session->kbdint->answers[i]);
+ }
+ session->kbdint->answers[i]=strdup(answer);
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/base64.c b/kftpgrabber/src/misc/libs/ssh/base64.c
new file mode 100644
index 0000000..19db420
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/base64.c
@@ -0,0 +1,210 @@
+/* base64 contains the needed support for base64 alphabet system, */
+/* as described in RFC1521 */
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+/* just the dirtiest part of code i ever made */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "priv.h"
+static char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/" ;
+
+/* transformations */
+#define SET_A(n,i) do { n |= (i&63) <<18; } while (0)
+#define SET_B(n,i) do { n |= (i&63) <<12; } while (0)
+#define SET_C(n,i) do { n |= (i&63) << 6; } while (0)
+#define SET_D(n,i) do { n |= (i&63); } while (0)
+
+#define GET_A(n) ((n & 0xff0000) >> 16)
+#define GET_B(n) ((n & 0xff00) >> 8)
+#define GET_C(n) (n & 0xff)
+
+static int _base64_to_bin(unsigned char dest[3], char *source,int num);
+static int get_equals(char *string);
+
+/* first part : base 64 to binary */
+
+/* base64_to_bin translates a base64 string into a binary one. important, if something went wrong (ie incorrect char)*/
+/* it returns NULL */
+BUFFER *base64_to_bin(char *source){
+ int len;
+ int equals;
+ BUFFER *buffer=buffer_new();
+ unsigned char block[3];
+
+ /* get the number of equals signs, which mirrors the padding */
+ equals=get_equals(source);
+ if(equals>2){
+ buffer_free(buffer);
+ return NULL;
+ }
+
+ len=strlen(source);
+ while(len>4){
+ if(_base64_to_bin(block,source,3)){
+ buffer_free(buffer);
+ return NULL;
+ }
+ buffer_add_data(buffer,block,3);
+ len-=4;
+ source+=4;
+ }
+ /* depending of the number of bytes resting, there are 3 possibilities (from the rfc) */
+ switch(len){
+/* (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded output will be
+ an integral multiple of 4 characters with no "=" padding */
+ case 4:
+ if(equals!=0){
+ buffer_free(buffer);
+ return NULL;
+ }
+ if(_base64_to_bin(block,source,3)){
+ buffer_free(buffer);
+ return NULL;
+ }
+ buffer_add_data(buffer,block,3);
+ return buffer;
+/*(2) the final quantum of encoding input is exactly 8 bits; here, the final
+ unit of encoded output will be two characters followed by two "="
+ padding characters */
+ case 2:
+ if(equals!=2){
+ buffer_free(buffer);
+ return NULL;
+ }
+ if(_base64_to_bin(block,source,1)){
+ buffer_free(buffer);
+ return NULL;
+ }
+ buffer_add_data(buffer,block,1);
+ return buffer;
+/* the final quantum of encoding input is
+ exactly 16 bits; here, the final unit of encoded output will be three
+ characters followed by one "=" padding character */
+ case 3:
+ if(equals!=1){
+ buffer_free(buffer);
+ return NULL;
+ }
+ if(_base64_to_bin(block,source,2)){
+ buffer_free(buffer);
+ return NULL;
+ }
+ buffer_add_data(buffer,block,2);
+ return buffer;
+ default:
+ /* 4,3,2 are the only padding size allowed */
+ buffer_free(buffer);
+ return NULL;
+ }
+ return NULL;
+}
+
+#define BLOCK(letter,n) do { ptr=strchr(alphabet,source[n]);\
+ if(!ptr) return -1;\
+ i=ptr-alphabet;\
+ SET_##letter(*block,i);\
+ } while(0)
+/* returns 0 if ok, -1 if not (ie invalid char into the stuff) */
+static int to_block4(unsigned long *block, char *source,int num){
+ char *ptr;
+ unsigned int i;
+ *block=0;
+ if(num<1)
+ return 0;
+ BLOCK(A,0); /* 6 bits */
+ BLOCK(B,1); /* 12 */
+ if(num<2)
+ return 0;
+ BLOCK(C,2); /* 18 */
+ if(num < 3)
+ return 0;
+ BLOCK(D,3); /* 24 */
+ return 0;
+}
+
+/* num = numbers of final bytes to be decoded */
+static int _base64_to_bin(unsigned char dest[3], char *source,int num){
+ unsigned long block;
+ if(to_block4(&block,source,num))
+ return -1;
+ dest[0]=GET_A(block);
+ dest[1]=GET_B(block);
+ dest[2]=GET_C(block);
+ return 0;
+}
+
+/* counts the number of "=" signs, and replace them by zeroes */
+static int get_equals(char *string){
+ char *ptr=string;
+ int num=0;
+ while((ptr=strchr(ptr,'='))){
+ num++;
+ *ptr=0;
+ ptr++;
+ }
+
+ return num;
+}
+
+/* thanks sysk for debugging my mess :) */
+#define BITS(n) ((1<<n)-1)
+static void _bin_to_base64(unsigned char *dest, unsigned char source[3], int len){
+ switch (len){
+ case 1:
+ dest[0]=alphabet[(source[0]>>2)];
+ dest[1]=alphabet[((source[0] & BITS(2)) << 4)];
+ dest[2]='=';
+ dest[3]='=';
+ break;
+ case 2:
+ dest[0]=alphabet[source[0]>>2];
+ dest[1]=alphabet[(source[1]>>4) | ((source[0] & BITS(2)) << 4)];
+ dest[2]=alphabet[(source[1]&BITS(4)) << 2];
+ dest[3]='=';
+ break;
+ case 3:
+ dest[0]=alphabet[(source[0]>>2)];
+ dest[1]=alphabet[(source[1]>>4) | ((source[0] & BITS(2)) << 4)];
+ dest[2]=alphabet[ (source[2] >> 6) | (source[1]&BITS(4)) << 2];
+ dest[3]=alphabet[source[2]&BITS(6)];
+ break;
+ }
+}
+
+char *bin_to_base64(unsigned char *source, int len){
+ int flen=len + (3 - (len %3)); /* round to upper 3 multiple */
+ char *buffer;
+ char *ptr;
+ flen=(4 * flen)/3 + 1 ;
+ ptr=buffer=malloc(flen);
+ while(len>0){
+ _bin_to_base64(ptr,source,len>3?3:len);
+ ptr+=4;
+ source +=3;
+ len -=3;
+ }
+ ptr[0]=0;
+ return buffer;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/buffer.c b/kftpgrabber/src/misc/libs/ssh/buffer.c
new file mode 100644
index 0000000..8d54e3c
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/buffer.c
@@ -0,0 +1,161 @@
+/* buffer.c */
+/* Well, buffers */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include "priv.h"
+BUFFER *buffer_new(){
+ BUFFER *buffer=malloc(sizeof(BUFFER));
+ memset(buffer,0,sizeof(BUFFER));
+ return buffer;
+ }
+
+void buffer_free(BUFFER *buffer){
+ if(buffer->data){
+ memset(buffer->data,0,buffer->allocated); /* burn the data */
+ free(buffer->data);
+ }
+ free(buffer);
+ }
+
+void buffer_reinit(BUFFER *buffer){
+ memset(buffer->data,0,buffer->used);
+ buffer->used=0;
+ buffer->pos=0;
+}
+
+static void realloc_buffer(BUFFER *buffer,int needed){
+ needed=(needed+0x7f) & ~0x7f;
+ buffer->data=realloc(buffer->data,needed);
+ buffer->allocated=needed;
+}
+
+void buffer_add_data(BUFFER *buffer,void *data,int len){
+ if(buffer->allocated < buffer->used+len)
+ realloc_buffer(buffer,buffer->used+len);
+ memcpy(buffer->data+buffer->used,data,len);
+ buffer->used+=len;
+ }
+
+void buffer_add_ssh_string(BUFFER *buffer,STRING *string){
+ u32 len=ntohl(string->size);
+ buffer_add_data(buffer,string,len+sizeof(u32));
+ }
+
+void buffer_add_u32(BUFFER *buffer,u32 data){
+ buffer_add_data(buffer,&data,sizeof(data));
+}
+
+void buffer_add_u64(BUFFER *buffer,u64 data){
+ buffer_add_data(buffer,&data,sizeof(data));
+}
+
+void buffer_add_u8(BUFFER *buffer,u8 data){
+ buffer_add_data(buffer,&data,sizeof(u8));
+}
+
+void buffer_add_data_begin(BUFFER *buffer, void *data, int len){
+ if(buffer->allocated < buffer->used + len)
+ realloc_buffer(buffer,buffer->used+len);
+ memmove(buffer->data+len,buffer->data,buffer->used);
+ memcpy(buffer->data,data,len);
+ buffer->used+=len;
+}
+
+void buffer_add_buffer(BUFFER *buffer, BUFFER *source){
+ buffer_add_data(buffer,buffer_get(source),buffer_get_len(source));
+}
+
+void *buffer_get(BUFFER *buffer){
+ return buffer->data;
+}
+
+void *buffer_get_rest(BUFFER *buffer){
+ return buffer->data+buffer->pos;
+}
+
+int buffer_get_len(BUFFER *buffer){
+ return buffer->used;
+}
+
+int buffer_get_rest_len(BUFFER *buffer){
+ return buffer->used - buffer->pos;
+}
+
+int buffer_pass_bytes(BUFFER *buffer,int len){
+ if(buffer->used < buffer->pos+len)
+ return 0;
+ buffer->pos+=len;
+ /* if the buffer is empty after having passed the whole bytes into it, we can clean it */
+ if(buffer->pos==buffer->used){
+ buffer->pos=0;
+ buffer->used=0;
+ }
+ return len;
+}
+
+int buffer_pass_bytes_end(BUFFER *buffer,int len){
+ if(buffer->used < buffer->pos + len)
+ return 0;
+ buffer->used-=len;
+ return len;
+}
+
+int buffer_get_data(BUFFER *buffer, void *data, int len){
+ if(buffer->pos+len>buffer->used)
+ return 0; /*no enough data in buffer */
+ memcpy(data,buffer->data+buffer->pos,len);
+ buffer->pos+=len;
+ return len; /* no yet support for partial reads (is it really needed ?? ) */
+}
+
+int buffer_get_u8(BUFFER *buffer, u8 *data){
+ return buffer_get_data(buffer,data,sizeof(u8));
+}
+
+int buffer_get_u32(BUFFER *buffer, u32 *data){
+ return buffer_get_data(buffer,data,sizeof(u32));
+}
+
+int buffer_get_u64(BUFFER *buffer, u64 *data){
+ return buffer_get_data(buffer,data,sizeof(u64));
+}
+
+STRING *buffer_get_ssh_string(BUFFER *buffer){
+ u32 stringlen;
+ u32 hostlen;
+ STRING *str;
+ if(buffer_get_u32(buffer,&stringlen)==0)
+ return NULL;
+ hostlen=ntohl(stringlen);
+ /* verify if there is enough space in buffer to get it */
+ if(buffer->pos+hostlen>buffer->used)
+ return 0; /* it is indeed */
+ str=string_new(hostlen);
+ if(buffer_get_data(buffer,str->string,hostlen)!=hostlen){
+ ssh_say(0,"buffer_get_ssh_string: oddish : second test failed when first was successful. len=%d",hostlen);
+ free(str);
+ return NULL;
+ }
+ return str;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/channels.c b/kftpgrabber/src/misc/libs/ssh/channels.c
new file mode 100644
index 0000000..9dd94d4
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/channels.c
@@ -0,0 +1,691 @@
+/* channels.c */
+/* It has support for ... ssh channels */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "priv.h"
+#include "ssh2.h"
+#define WINDOWLIMIT 1024
+#define WINDOWBASE 32000
+static void channel_default_bufferize(CHANNEL *channel, void *data, int len, int is_stderr);
+static CHANNEL *new_channel(SSH_SESSION *session){
+ CHANNEL *channel=malloc(sizeof(CHANNEL));
+ memset(channel,0,sizeof(CHANNEL));
+ channel->session=session;
+ if(!session->channels){
+ session->channels=channel;
+ channel->next=channel->prev=channel;
+ return channel;
+ }
+ channel->next=session->channels;
+ channel->prev=session->channels->prev;
+ channel->next->prev=channel;
+ channel->prev->next=channel;
+ return channel;
+}
+
+static u32 channel_new_id(SSH_SESSION *session){
+ u32 ret=session->maxchannel;
+ session->maxchannel++;
+ return ret;
+}
+
+static CHANNEL *channel_open(SSH_SESSION *session,char *type_c,int window,
+int maxpacket,BUFFER *payload){
+ CHANNEL *channel=new_channel(session);
+ STRING *type=string_from_char(type_c);
+ u32 foo;
+ int err;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_OPEN);
+ channel->local_channel=channel_new_id(session);
+ channel->local_maxpacket=maxpacket;
+ channel->local_window=window;
+ ssh_say(2,"creating a channel %d with %d window and %d max packet\n",channel->local_channel,
+ window,maxpacket);
+ buffer_add_ssh_string(session->out_buffer,type);
+ buffer_add_u32(session->out_buffer,htonl(channel->local_channel));
+ buffer_add_u32(session->out_buffer,htonl(channel->local_window));
+ buffer_add_u32(session->out_buffer,htonl(channel->local_maxpacket));
+ free(type);
+ if(payload)
+ buffer_add_buffer(session->out_buffer,payload);
+ packet_send(session);
+ ssh_say(2,"Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d\n",type_c,channel->local_channel);
+ err=packet_wait(session,SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,1);
+ switch(session->in_packet.type){
+ case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
+ buffer_get_u32(session->in_buffer,&foo);
+ if(channel->local_channel!=ntohl(foo)){
+ ssh_set_error(session,SSH_INVALID_DATA,"server answered with sender chan num %d instead of given %d",
+ ntohl(foo),channel->local_channel);
+ channel_free(channel);
+ return NULL;
+ }
+ buffer_get_u32(session->in_buffer,&foo);
+ channel->remote_channel=ntohl(foo);
+ buffer_get_u32(session->in_buffer,&foo);
+ channel->remote_window=ntohl(foo);
+ buffer_get_u32(session->in_buffer,&foo);
+ channel->remote_maxpacket=ntohl(foo);
+ ssh_say(3,"Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d\n",
+ channel->local_channel,channel->remote_channel);
+ ssh_say(3,"Remote window : %ld, maxpacket : %ld\n",
+ channel->remote_window, channel->remote_maxpacket);
+ channel->open=1;
+ return channel;
+ case SSH2_MSG_CHANNEL_OPEN_FAILURE:
+ {
+ u32 code;
+ STRING *error_s;
+ char *error;
+ buffer_get_u32(session->in_buffer,&foo);
+ buffer_get_u32(session->in_buffer,&code);
+ error_s=buffer_get_ssh_string(session->in_buffer);
+ error=string_to_char(error_s);
+ ssh_set_error(session,SSH_REQUEST_DENIED,"Channel opening failure : channel %d error (%d) %s",
+ channel->local_channel,ntohl(code),error);
+ free(error);
+ free(error_s);
+ channel_free(channel);
+ return NULL;
+ }
+ default:
+ ssh_say(0,"Received unknown packet %d\n",session->in_packet.type);
+ channel_free(channel);
+ return NULL;
+ }
+ return NULL;
+}
+
+static CHANNEL *find_local_channel(SSH_SESSION *session,u32 num){
+ /* we assume we are always the local */
+ CHANNEL *initchan,*channel;
+ initchan=session->channels;
+ if(!initchan)
+ return NULL;
+ for(channel=initchan;channel->local_channel!=num;channel=channel->next){
+ if(channel->next==initchan)
+ return NULL;
+ }
+ return channel;
+}
+
+static void grow_window(SSH_SESSION *session, CHANNEL *channel){
+ u32 new_window=WINDOWBASE;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
+ buffer_add_u32(session->out_buffer,htonl(new_window));
+ packet_send(session);
+ ssh_say(3,"growing window (channel %d:%d) to %d bytes\n",
+ channel->local_channel,channel->remote_channel,
+ channel->local_window + new_window);
+ channel->local_window+=new_window;
+}
+
+static CHANNEL *channel_from_msg(SSH_SESSION *session){
+ u32 chan;
+ CHANNEL *channel;
+ if (buffer_get_u32(session->in_buffer,&chan)!=sizeof(u32)){
+ ssh_set_error(session,SSH_FATAL,"Getting channel from message : short read");
+ return NULL;
+ }
+ channel=find_local_channel(session,ntohl(chan));
+ if(!channel)
+ ssh_set_error(session,SSH_FATAL,"Server specified invalid channel %d",ntohl(chan));
+ return channel;
+}
+
+static void channel_rcv_change_window(SSH_SESSION *session){
+ u32 bytes;
+ CHANNEL *channel;
+ int err;
+ channel=channel_from_msg(session);
+ if(!channel)
+ ssh_say(0,"%s\n",ssh_get_error(session));
+ err = buffer_get_u32(session->in_buffer,&bytes);
+ if(!channel || err!= sizeof(u32)){
+ ssh_say(1,"Error getting a window adjust message : invalid packet\n");
+ return;
+ }
+ bytes=ntohl(bytes);
+ ssh_say(3,"Adding %d bytes to channel (%d:%d) (from %d bytes)\n",bytes,
+ channel->local_channel,channel->remote_channel,channel->remote_window);
+ channel->remote_window+=bytes;
+}
+
+/* is_stderr is set to 1 if the data are extended, ie stderr */
+static void channel_rcv_data(SSH_SESSION *session,int is_stderr){
+ STRING *str;
+ CHANNEL *channel;
+ channel=channel_from_msg(session);
+ if(!channel){
+ ssh_say(0,"%s",ssh_get_error(session));
+ return;
+ }
+ if(is_stderr){
+ u32 ignore;
+ /* uint32 data type code. we can ignore it */
+ buffer_get_u32(session->in_buffer,&ignore);
+ }
+ str=buffer_get_ssh_string(session->in_buffer);
+
+ if(!str){
+ ssh_say(0,"Invalid data packet !\n");
+ return;
+ }
+ ssh_say(3,"adding %d bytes data in %d\n",string_len(str),is_stderr);
+ /* what shall we do in this case ? let's accept it anyway */
+ if(string_len(str)>channel->local_window)
+ ssh_say(0,"Data packet too big for our window(%d vs %d)",string_len(str),channel->local_window);
+ if(!is_stderr){
+ /* stdout */
+ if(channel->write_fct){
+ channel->write_fct(channel,str->string,string_len(str),channel->userarg);
+ } else {
+ channel_default_bufferize(channel,str->string,string_len(str),is_stderr);
+ }
+ } else {
+ /* stderr */
+ if(channel->write_err_fct){
+ channel->write_err_fct(channel,str->string,string_len(str),channel->userarg);
+ } else {
+ channel_default_bufferize(channel,str->string,string_len(str),is_stderr);
+ }
+ }
+ if(string_len(str)>=channel->local_window)
+ channel->local_window-=string_len(str);
+ else
+ channel->local_window=0; /* buggy remote */
+ if(channel->local_window < WINDOWLIMIT)
+ grow_window(session,channel); /* i wonder if this is the correct place to do that */
+ free(str);
+}
+
+static void channel_rcv_eof(SSH_SESSION *session){
+ CHANNEL *channel;
+ channel=channel_from_msg(session);
+ if(!channel){
+ ssh_say(0,"%s\n",ssh_get_error(session));
+ return;
+ }
+ ssh_say(2,"Received eof on channel (%d:%d)\n",channel->local_channel,
+ channel->remote_channel);
+/* channel->remote_window=0; */
+ channel->remote_eof=1;
+}
+
+static void channel_rcv_close(SSH_SESSION *session){
+ CHANNEL *channel;
+ channel=channel_from_msg(session);
+ if(!channel){
+ ssh_say(0,"%s\n",ssh_get_error(session));
+ return;
+ }
+ ssh_say(2,"Received close on channel (%d:%d)\n",channel->local_channel,
+ channel->remote_channel);
+ channel->open=0;
+ if(!channel->remote_eof)
+ ssh_say(2,"Remote host not polite enough to send an eof before close\n");
+ channel->remote_eof=1;
+}
+
+static void channel_rcv_request(SSH_SESSION *session){
+ STRING *request_s;
+ char *request;
+ u32 status;
+ CHANNEL *channel=channel_from_msg(session);
+ if(!channel){
+ ssh_say(1,"%s\n",ssh_get_error(session));
+ return;
+ }
+ request_s=buffer_get_ssh_string(session->in_buffer);
+ if(!request_s){
+ ssh_say(0,"Invalid MSG_CHANNEL_REQUEST\n");
+ return;
+ }
+ buffer_get_u8(session->in_buffer,(u8 *)&status);
+ request=string_to_char(request_s);
+ if(!strcmp(request,"exit-status")){
+ buffer_get_u32(session->in_buffer,&status);
+ status=ntohl(status);
+/* XXX do something with status, we might need it */
+ free(request_s);
+ free(request);
+ return ;
+ }
+ if(!strcmp(request,"exit-signal")){
+ STRING *signal_s;
+ char *signal;
+ char *core="(core dumped)";
+ u8 i;
+ signal_s=buffer_get_ssh_string(session->in_buffer);
+ if(!signal_s){
+ ssh_say(0,"Invalid MSG_CHANNEL_REQUEST\n");
+ free(request_s);
+ free(request);
+ return;
+ }
+ signal=string_to_char(signal_s);
+ buffer_get_u8(session->in_buffer,&i);
+ if(!i)
+ core="";
+ ssh_say(0,"Remote connection closed by signal SIG%s %s\n",signal,core);
+ free(signal_s);
+ free(signal);
+ free(request_s);
+ free(request);
+ return;
+ }
+ ssh_say(0,"Unknown request %s\n",request);
+ free(request_s);
+ free(request);
+}
+
+/* channel_handle is called by wait_packet, ie, when there is channel informations to handle . */
+void channel_handle(SSH_SESSION *session, int type){
+ ssh_say(3,"Channel_handle(%d)\n",type);
+ switch(type){
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ channel_rcv_change_window(session);
+ break;
+ case SSH2_MSG_CHANNEL_DATA:
+ channel_rcv_data(session,0);
+ break;
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ channel_rcv_data(session,1);
+ break;
+ case SSH2_MSG_CHANNEL_EOF:
+ channel_rcv_eof(session);
+ break;
+ case SSH2_MSG_CHANNEL_CLOSE:
+ channel_rcv_close(session);
+ break;
+ case SSH2_MSG_CHANNEL_REQUEST:
+ channel_rcv_request(session);
+ break;
+ default:
+ ssh_say(0,"Unexpected message %d\n",type);
+ }
+}
+
+/* when data has been received from the ssh server, it can be applied to the known
+ user function, with help of the callback, or inserted here */
+/* XXX is the window changed ? */
+static void channel_default_bufferize(CHANNEL *channel, void *data, int len, int is_stderr){
+ ssh_say(3,"placing %d bytes into channel buffer (stderr=%d)\n",len,is_stderr);
+ if(!is_stderr){
+ /* stdout */
+ if(!channel->stdout_buffer)
+ channel->stdout_buffer=buffer_new();
+ buffer_add_data(channel->stdout_buffer,data,len);
+ } else {
+ /* stderr */
+ if(!channel->stderr_buffer)
+ channel->stderr_buffer=buffer_new();
+ buffer_add_data(channel->stderr_buffer,data,len);
+ }
+}
+
+
+/* --8<-- PUBLIC INTERFACE BEGINS HERE -8<-----8< --- */
+
+/* deprecated */
+CHANNEL *open_session_channel(SSH_SESSION *session,int window,int maxpacket){
+ CHANNEL *chan=channel_open(session,"session",window,maxpacket,NULL);
+ return chan;
+}
+
+CHANNEL *channel_open_session(SSH_SESSION *session){
+ return open_session_channel(session,64000,32000);
+}
+
+/* tcpip forwarding */
+CHANNEL *channel_open_forward(SSH_SESSION *session,char *remotehost, int remoteport, char *sourcehost, int localport){
+ CHANNEL *chan;
+ BUFFER *payload=buffer_new();
+ STRING *str=string_from_char(remotehost);
+ buffer_add_ssh_string(payload,str);
+ free(str);
+ str=string_from_char(sourcehost);
+ buffer_add_u32(payload,htonl(remoteport));
+ buffer_add_ssh_string(payload,str);
+ free(str);
+ buffer_add_u32(payload,htonl(localport));
+ chan=channel_open(session,"direct-tcpip",64000,32000,payload);
+ buffer_free(payload);
+ return chan;
+}
+
+
+void channel_free(CHANNEL *channel){
+ SSH_SESSION *session=channel->session;
+ if(session->alive && channel->open)
+ channel_close(channel);
+ /* handle the "my channel is first on session list" case */
+ if(session->channels==channel)
+ session->channels=channel->next;
+ /* handle the "my channel is the only on session list" case */
+ if(channel->next == channel){
+ session->channels=NULL;
+ } else {
+ channel->prev->next=channel->next;
+ channel->next->prev=channel->prev;
+ }
+ if(channel->stdout_buffer)
+ buffer_free(channel->stdout_buffer);
+ if(channel->stderr_buffer)
+ buffer_free(channel->stderr_buffer);
+ /* debug trick to catch use after frees */
+ memset(channel,'X',sizeof(CHANNEL));
+ free(channel);
+}
+
+int channel_send_eof(CHANNEL *channel){
+ SSH_SESSION *session=channel->session;
+ int ret;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_EOF);
+ buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
+ ret=packet_send(session);
+ ssh_say(1,"Sent a EOF on client channel (%d:%d)\n",channel->local_channel,
+ channel->remote_channel);
+ channel->local_eof=1;
+ return ret;
+}
+
+int channel_close(CHANNEL *channel){
+ SSH_SESSION *session=channel->session;
+ int ret=0;
+ if(!channel->local_eof)
+ ret=channel_send_eof(channel);
+ if(ret)
+ return ret;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_CLOSE);
+ buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
+ ret=packet_send(session);
+ ssh_say(1,"Sent a close on client channel (%d:%d)\n",channel->local_channel,
+ channel->remote_channel);
+ if(!ret)
+ channel->open =0;
+ return ret;
+}
+
+/* Blocking write */
+/* The exact len is written */
+int channel_write(CHANNEL *channel ,void *data,int len){
+ SSH_SESSION *session=channel->session;
+ int effectivelen;
+ int origlen=len;
+ if(channel->local_eof){
+ ssh_set_error(session,SSH_REQUEST_DENIED,"Can't write to channel %d:%d"
+ " after EOF was sent",channel->local_channel,channel->remote_channel);
+ return -1;
+ }
+ while(len >0){
+ if(channel->remote_window<len){
+ ssh_say(2,"Remote window is %d bytes. going to write %d bytes\n",
+ channel->remote_window,len);
+ ssh_say(2,"Waiting for a growing window message...\n");
+ /* wonder what happens when the channel window is zero */
+ while(channel->remote_window==0){
+ /* parse every incoming packet */
+ packet_wait(channel->session,0,0);
+ }
+ effectivelen=len>channel->remote_window?channel->remote_window:len;
+ } else
+ effectivelen=len;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_DATA);
+ buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
+ buffer_add_u32(session->out_buffer,htonl(effectivelen));
+ buffer_add_data(session->out_buffer,data,effectivelen);
+ packet_send(session);
+ ssh_say(2,"channel_write wrote %d bytes\n",effectivelen);
+ channel->remote_window-=effectivelen;
+ len -= effectivelen;
+ data+=effectivelen;
+ }
+ return origlen;
+}
+
+int channel_is_open(CHANNEL *channel){
+ return (channel->open!=0);
+}
+
+
+static int channel_request(CHANNEL *channel,char *request, BUFFER *buffer,int reply){
+ STRING *request_s=string_from_char(request);
+ SSH_SESSION *session=channel->session;
+ int err;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_REQUEST);
+ buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
+ buffer_add_ssh_string(session->out_buffer,request_s);
+ buffer_add_u8(session->out_buffer,reply?1:0);
+ if(buffer)
+ buffer_add_data(session->out_buffer,buffer_get(buffer),buffer_get_len(buffer));
+ packet_send(session);
+ ssh_say(3,"Sent a SSH_MSG_CHANNEL_REQUEST %s\n",request);
+ free(request_s);
+ if(!reply)
+ return 0;
+ err=packet_wait(session,SSH2_MSG_CHANNEL_SUCCESS,1);
+ if(err)
+ if(session->in_packet.type==SSH2_MSG_CHANNEL_FAILURE){
+ ssh_say(2,"%s channel request failed\n",request);
+ ssh_set_error(session,SSH_REQUEST_DENIED,"Channel request %s failed",request);
+ }
+ else
+ ssh_say(3,"Received an unexpected %d message\n",session->in_packet.type);
+ else
+ ssh_say(3,"Received a SUCCESS\n");
+ return err;
+}
+
+int channel_request_pty_size(CHANNEL *channel, char *terminal, int col, int row){
+ STRING *term=string_from_char(terminal);
+ BUFFER *buffer=buffer_new();
+ int err;
+ buffer_add_ssh_string(buffer,term);
+ buffer_add_u32(buffer,htonl(col));
+ buffer_add_u32(buffer,htonl(row));
+ buffer_add_u32(buffer,0);
+ buffer_add_u32(buffer,0);
+/* a 0byte string */
+ buffer_add_u32(buffer,htonl(1));
+ buffer_add_u8(buffer,0);
+ free(term);
+ err=channel_request(channel,"pty-req",buffer,1);
+ buffer_free(buffer);
+ return err;
+}
+
+int channel_request_pty(CHANNEL *channel){
+ return channel_request_pty_size(channel,"xterm",80,24);
+}
+
+int channel_change_pty_size(CHANNEL *channel,int cols,int rows){
+ BUFFER *buffer=buffer_new();
+ int err;
+ /*buffer_add_u8(buffer,0);*/
+ buffer_add_u32(buffer,htonl(cols));
+ buffer_add_u32(buffer,htonl(rows));
+ buffer_add_u32(buffer,0);
+ buffer_add_u32(buffer,0);
+ err=channel_request(channel,"window-change",buffer,0);
+ buffer_free(buffer);
+ return err;
+}
+
+int channel_request_shell(CHANNEL *channel){
+ int err=channel_request(channel,"shell",NULL,1);
+ return err;
+}
+
+int channel_request_subsystem(CHANNEL *channel, char *system){
+ BUFFER* buffer=buffer_new();
+ int ret;
+ STRING *subsystem=string_from_char(system);
+ buffer_add_ssh_string(buffer,subsystem);
+ free(subsystem);
+ ret=channel_request(channel,"subsystem",buffer,1);
+ buffer_free(buffer);
+ return ret;
+}
+
+int channel_request_sftp( CHANNEL *channel){
+ return channel_request_subsystem(channel, "sftp");
+}
+
+
+int channel_request_env(CHANNEL *channel,char *name, char *value){
+ BUFFER *buffer=buffer_new();
+ int ret;
+ STRING *string=string_from_char(name);
+ buffer_add_ssh_string(buffer,string);
+ free(string);
+ string=string_from_char(value);
+ buffer_add_ssh_string(buffer,string);
+ free(string);
+ ret=channel_request(channel,"env",buffer,1);
+ buffer_free(buffer);
+ return ret;
+}
+
+int channel_request_exec(CHANNEL *channel, char *cmd){
+ BUFFER *buffer=buffer_new();
+ int ret;
+ STRING *command=string_from_char(cmd);
+ buffer_add_ssh_string(buffer,command);
+ free(command);
+ ret=channel_request(channel,"exec",buffer,1);
+ buffer_free(buffer);
+ return ret;
+}
+
+int channel_set_write_handler(CHANNEL *chan,
+ void (*write_fct)(CHANNEL *channel, void *data, int len, void *userdefined),void *user){
+ chan->write_fct=write_fct;
+ chan->userarg=user;
+ return 0;
+}
+
+int channel_set_stderr_write_handler(CHANNEL *chan,
+ void (*write_err_fct)(CHANNEL *channel, void *data, int len, void *userdefined),void *user){
+ chan->write_err_fct=write_err_fct;
+ chan->userarg=user;
+ return 0;
+}
+
+
+/* reads into a channel and put result into buffer */
+/* returns number of bytes read, 0 if eof or such and -1 in case of error */
+/* if bytes != 0, the exact number of bytes are going to be read */
+int channel_read(CHANNEL *channel, BUFFER *buffer,int bytes,int is_stderr){
+ BUFFER *stdbuf=NULL;
+ int len;
+ buffer_reinit(buffer);
+ /* maybe i should always set a buffer to avoid races between channel_default_bufferize and channel_read */
+ if(channel->write_fct){
+ ssh_set_error(channel->session,SSH_INVALID_REQUEST,"Specified channel hasn't got a default buffering system\n");
+ return -1;
+ }
+ if(is_stderr){
+ if(!channel->stderr_buffer)
+ channel->stderr_buffer=buffer_new();
+ stdbuf=channel->stderr_buffer;
+ } else {
+ if(!channel->stdout_buffer)
+ channel->stdout_buffer=buffer_new();
+ stdbuf=channel->stdout_buffer;
+ }
+ /* block reading if asked bytes=0 */
+ while((buffer_get_rest_len(stdbuf)==0) || (buffer_get_rest_len(stdbuf) < bytes)){
+ if(channel->remote_eof && buffer_get_rest_len(stdbuf)==0)
+ return 0;
+ if(channel->remote_eof)
+ break; /* return the resting bytes in buffer */
+ if(packet_read(channel->session)||packet_translate(channel->session))
+ return -1;
+ packet_parse(channel->session);
+ }
+
+ if(bytes==0){
+ /* write the ful buffer informations */
+ buffer_add_data(buffer,buffer_get_rest(stdbuf),buffer_get_rest_len(stdbuf));
+ buffer_reinit(stdbuf);
+ } else {
+ len=buffer_get_rest_len(stdbuf);
+ len= (len>bytes?bytes:len); /* read bytes bytes if len is greater, everything otherwise */
+ buffer_add_data(buffer,buffer_get_rest(stdbuf),len);
+ buffer_pass_bytes(stdbuf,len);
+ }
+ return buffer_get_len(buffer);
+}
+
+/* returns the number of bytes available, 0 if nothing is currently available, -1 if error */
+int channel_poll(CHANNEL *channel, int is_stderr){
+ BUFFER *buffer;
+ if(is_stderr){
+ buffer=channel->stderr_buffer;
+ if(!buffer)
+ buffer=channel->stderr_buffer=buffer_new();
+ } else {
+ buffer=channel->stdout_buffer;
+ if(!buffer)
+ buffer=channel->stdout_buffer=buffer_new();
+ }
+ while(buffer_get_len(buffer)==0){
+ if(ssh_fd_poll(channel->session)){
+ if(packet_read(channel->session)||packet_translate(channel->session))
+ return -1;
+ packet_parse(channel->session);
+ } else
+ return 0; /* nothing is available has said fd_poll */
+ }
+ return buffer_get_len(buffer);
+}
+
+/* nonblocking read on the specified channel. it will return <=len bytes of data read
+ atomicly. */
+int channel_read_nonblocking(CHANNEL *channel, char *dest, int len, int is_stderr){
+ int to_read=channel_poll(channel,is_stderr);
+ int lu;
+ BUFFER *buffer=buffer_new();
+ if(to_read<=0){
+ buffer_free(buffer);
+ return to_read; /* may be an error code */
+ }
+ if(to_read>len)
+ to_read=len;
+ lu=channel_read(channel,buffer,to_read,is_stderr);
+ memcpy(dest,buffer_get(buffer),lu>=0?lu:0);
+ buffer_free(buffer);
+ return lu;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/client.c b/kftpgrabber/src/misc/libs/ssh/client.c
new file mode 100644
index 0000000..a7a7569
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/client.c
@@ -0,0 +1,261 @@
+/* client.c file */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include "priv.h"
+#include "ssh2.h"
+static void ssh_cleanup(SSH_SESSION *session);
+#define set_status(opt,status) do {\
+ if (opt->connect_status_function) \
+ opt->connect_status_function(opt->connect_status_arg, status); \
+ } while (0)
+/* simply gets a banner from a socket */
+char *ssh_get_banner(SSH_SESSION *session){
+ char buffer[128];
+ int i = 0;
+ while (i < 127) {
+ if(read(session->fd, &buffer[i], 1)<=0){
+ ssh_set_error(session,SSH_CONNECTION_LOST,"Remote host closed connection");
+ return NULL;
+ }
+ if (buffer[i] == '\r')
+ buffer[i] = 0;
+ if (buffer[i] == '\n') {
+ buffer[i] = 0;
+ return strdup(buffer);
+ }
+ i++;
+ }
+ ssh_set_error(NULL,SSH_FATAL,"Too large banner");
+ return NULL;
+}
+
+/* ssh_send_banner sends a SSH banner to the server */
+/* TODO select a banner compatible with server version */
+/* switch SSH1/1.5/2 */
+/* and quit when the server is SSH1 only */
+
+void ssh_send_banner(SSH_SESSION *session){
+ char *banner=CLIENTBANNER ;
+ char buffer[128];
+ if(session->options->clientbanner)
+ banner=session->options->clientbanner;
+ session->clientbanner=strdup(banner);
+ snprintf(buffer,128,"%s\r\n",session->clientbanner);
+ write(session->fd,buffer,strlen(buffer));
+}
+
+
+int dh_handshake(SSH_SESSION *session){
+ STRING *e,*f,*pubkey,*signature;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
+ dh_generate_x(session);
+ dh_generate_e(session);
+ e=dh_get_e(session);
+ buffer_add_ssh_string(session->out_buffer,e);
+ packet_send(session);
+ free(e);
+ if(packet_wait(session,SSH2_MSG_KEXDH_REPLY,1))
+ return -1;
+ pubkey=buffer_get_ssh_string(session->in_buffer);
+ if(!pubkey){
+ ssh_set_error(NULL,SSH_FATAL,"No public key in packet");
+ return -1;
+ }
+ dh_import_pubkey(session,pubkey);
+ f=buffer_get_ssh_string(session->in_buffer);
+ if(!f){
+ ssh_set_error(NULL,SSH_FATAL,"No F number in packet");
+ return -1;
+ }
+ dh_import_f(session,f);
+ free(f);
+ if(!(signature=buffer_get_ssh_string(session->in_buffer))){
+ ssh_set_error(NULL,SSH_FATAL,"No signature in packet");
+ return -1;
+ }
+
+ dh_build_k(session);
+ packet_wait(session,SSH2_MSG_NEWKEYS,1);
+ ssh_say(2,"Got SSH_MSG_NEWKEYS\n");
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
+ packet_send(session);
+ ssh_say(2,"SSH_MSG_NEWKEYS sent\n");
+ make_sessionid(session);
+ /* set the cryptographic functions for the next crypto (it is needed for generate_session_keys for key lenghts) */
+ if(crypt_set_algorithms(session))
+ return -1;
+ generate_session_keys(session);
+ /* verify the host's signature. XXX do it sooner */
+ if(signature_verify(session,signature)){
+ free(signature);
+ return -1;
+ }
+ free(signature); /* forget it for now ... */
+ /* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
+ if(session->current_crypto)
+ crypto_free(session->current_crypto);
+ /* XXX later, include a function to change keys */
+ session->current_crypto=session->next_crypto;
+ session->next_crypto=crypto_new();
+ return 0;
+}
+
+int ssh_service_request(SSH_SESSION *session,char *service){
+ STRING *service_s;
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_SERVICE_REQUEST);
+ service_s=string_from_char(service);
+ buffer_add_ssh_string(session->out_buffer,service_s);
+ free(service_s);
+ packet_send(session);
+ ssh_say(3,"Sent SSH_MSG_SERVICE_REQUEST (service %s)\n",service);
+ if(packet_wait(session,SSH2_MSG_SERVICE_ACCEPT,1)){
+ ssh_set_error(session,SSH_INVALID_DATA,"did not receive SERVICE_ACCEPT");
+ return -1;
+ }
+ ssh_say(3,"Received SSH_MSG_SERVICE_ACCEPT (service %s)\n",service);
+ return 0;
+}
+
+SSH_SESSION *ssh_connect(SSH_OPTIONS *options){
+ SSH_SESSION *session;
+ int fd;
+ if(!options){
+ ssh_set_error(NULL,SSH_FATAL,"Null argument given to ssh_connect !");
+ return NULL;
+ }
+ ssh_crypto_init();
+ if(options->fd==-1 && !options->host){
+ ssh_set_error(NULL,SSH_FATAL,"Hostname required");
+ return NULL;
+ }
+ if(options->fd != -1)
+ fd=options->fd;
+ else
+ fd=ssh_connect_host(options->host,options->bindaddr,options->port,
+ options->timeout,options->timeout_usec);
+ if(fd<0)
+ return NULL;
+ set_status(options,0.2);
+ session=ssh_session_new();
+ session->fd=fd;
+ session->alive=1;
+ session->options=options;
+ if(!(session->serverbanner=ssh_get_banner(session))){
+ ssh_cleanup(session);
+ return NULL;
+ }
+ set_status(options,0.4);
+ ssh_say(2,"banner : %s\n",session->serverbanner);
+ ssh_send_banner(session);
+ set_status(options,0.5);
+ if(ssh_get_kex(session,0)){
+ ssh_disconnect(session);
+ return NULL;
+ }
+ set_status(options,0.6);
+ list_kex(&session->server_kex);
+ if(set_kex(session)){
+ ssh_disconnect(session);
+ return NULL;
+ }
+ send_kex(session,0);
+ set_status(options,0.8);
+ if(dh_handshake(session)){
+ ssh_disconnect(session);
+ return NULL;
+ }
+ set_status(options,1.0);
+ session->connected=1;
+ return session;
+}
+
+static void ssh_cleanup(SSH_SESSION *session){
+ int i;
+ if(session->serverbanner)
+ free(session->serverbanner);
+ if(session->clientbanner)
+ free(session->clientbanner);
+ if(session->in_buffer)
+ buffer_free(session->in_buffer);
+ if(session->out_buffer)
+ buffer_free(session->out_buffer);
+ if(session->banner)
+ free(session->banner);
+ if(session->options)
+ options_free(session->options);
+ if(session->current_crypto)
+ crypto_free(session->current_crypto);
+ if(session->next_crypto)
+ crypto_free(session->next_crypto);
+
+ /* delete all channels */
+ while(session->channels)
+ channel_free(session->channels);
+ if(session->client_kex.methods)
+ for(i=0;i<10;i++)
+ if(session->client_kex.methods[i])
+ free(session->client_kex.methods[i]);
+ if(session->server_kex.methods)
+ for(i=0;i<10;++i)
+ if(session->server_kex.methods[i])
+ free(session->server_kex.methods[i]);
+ free(session->client_kex.methods);
+ free(session->server_kex.methods);
+ memset(session,'X',sizeof(SSH_SESSION)); /* burn connection, it could hangs sensitive datas */
+ free(session);
+}
+
+char *ssh_get_issue_banner(SSH_SESSION *session){
+ if(!session->banner)
+ return NULL;
+ return string_to_char(session->banner);
+}
+
+void ssh_disconnect(SSH_SESSION *session){
+ STRING *str;
+ if(session->fd!= -1) {
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_DISCONNECT);
+ buffer_add_u32(session->out_buffer,htonl(SSH2_DISCONNECT_BY_APPLICATION));
+ str=string_from_char("Bye Bye");
+ buffer_add_ssh_string(session->out_buffer,str);
+ free(str);
+ packet_send(session);
+ close(session->fd);
+ session->fd=-1;
+ }
+ session->alive=0;
+ ssh_cleanup(session);
+}
+
+const char *ssh_copyright(){
+ return LIBSSH_VERSION " (c) 2003-2004 Aris Adamantiadis (aris@0xbadc0de.be)"
+ " Distributed under the LGPL, please refer to COPYING file for informations"
+ " about your rights" ;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/connect.c b/kftpgrabber/src/misc/libs/ssh/connect.c
new file mode 100644
index 0000000..ba117f2
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/connect.c
@@ -0,0 +1,295 @@
+/* connect.c */
+/* it handles connections to ssh servers */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include "priv.h"
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+#ifndef HAVE_GETHOSTBYNAME
+#ifndef HAVE_GETHOSTBYADDR
+#error "your system doesn't have gethostbyname nor gethostbyaddr"
+#endif
+#endif
+#define FIRST_CHANNEL 42
+static void sock_set_nonblocking(int sock) {
+ fcntl(sock,F_SETFL,O_NONBLOCK);
+}
+static void sock_set_blocking(int sock){
+ fcntl(sock,F_SETFL,0);
+}
+
+/* connect_host connects to an IPv4 (or IPv6) host */
+/* specified by its IP address or hostname. */
+/* output is the file descriptor, <0 if failed. */
+
+int ssh_connect_host(const char *host, const char *bind_addr, int port,long timeout, long usec){
+ struct sockaddr_in sa;
+ struct sockaddr_in bindsa;
+ struct hostent *hp=NULL;
+ static int count=0; /* for reentrencity */
+ int s;
+ while(++count>1)
+ --count;
+#ifdef HAVE_GETHOSTBYADDR
+ hp=gethostbyaddr(host,4,AF_INET);
+#endif
+#ifdef HAVE_GETHOSTBYNAME
+ if(!hp)
+ hp=gethostbyname(host);
+#endif
+ if(!hp){
+ --count;
+ ssh_set_error(NULL,SSH_FATAL,"Failed to resolve hostname %s (%s)",host,hstrerror(h_errno));
+ return -1;
+ }
+ memset(&sa,0,sizeof(sa));
+ memcpy(&sa.sin_addr,hp->h_addr,hp->h_length);
+ sa.sin_family=hp->h_addrtype;
+ sa.sin_port=htons((unsigned short)port);
+ --count;
+
+ if(bind_addr){
+ ssh_say(2,"resolving %s\n",bind_addr);
+ hp=NULL;
+ while(++count>1)
+ --count;
+#ifdef HAVE_GETHOSTBYADDR
+ hp=gethostbyaddr(bind_addr,4,AF_INET);
+#endif
+#ifdef HAVE_GETHOSTBYNAME
+ if(!hp)
+ hp=gethostbyname(bind_addr);
+#endif
+ if(!hp){
+ --count;
+ ssh_set_error(NULL,SSH_FATAL,"Failed to resolve bind address %s (%s)",bind_addr,hstrerror(h_errno));
+ return -1;
+ }
+ }
+ memset(&bindsa,0,sizeof(bindsa));
+ /* create socket */
+ s=socket(sa.sin_family,SOCK_STREAM,0);
+ if(s<0){
+ if(bind_addr)
+ --count;
+ ssh_set_error(NULL,SSH_FATAL,"socket : %s",strerror(errno));
+ return s;
+ }
+
+ if(bind_addr){
+ memcpy(&bindsa.sin_addr,hp->h_addr,hp->h_length);
+ bindsa.sin_family=hp->h_addrtype;
+ --count;
+ if(bind(s,(struct sockaddr *)&bindsa,sizeof(bindsa))<0){
+ ssh_set_error(NULL,SSH_FATAL,"Binding local address : %s",strerror(errno));
+ close(s);
+ return -1;
+ }
+ }
+ if(timeout){
+ struct timeval to;
+ fd_set set;
+ int ret=0;
+ int len=sizeof(ret);
+ to.tv_sec=timeout;
+ to.tv_usec=usec;
+ sock_set_nonblocking(s);
+ connect(s,(struct sockaddr* )&sa,sizeof(sa));
+ FD_ZERO(&set);
+ FD_SET(s,&set);
+ ret=select(s+1,NULL,&set,NULL,&to);
+ if(ret==0){
+ /* timeout */
+ ssh_set_error(NULL,SSH_FATAL,"Timeout while connecting to %s:%d",host,port);
+ close(s);
+ return -1;
+ }
+ if(ret<0){
+ ssh_set_error(NULL,SSH_FATAL,"Select error : %s",strerror(errno));
+ close(s);
+ return -1;
+ }
+ /* get connect(2) return code. zero means no error */
+ getsockopt(s,SOL_SOCKET,SO_ERROR,&ret,&len);
+ if (ret!=0){
+ ssh_set_error(NULL,SSH_FATAL,"Connecting : %s",strerror(ret));
+ close(s);
+ return -1;
+ }
+ /* s is connected ? */
+ ssh_say(3,"socket connected with timeout\n");
+ sock_set_blocking(s);
+ return s;
+ }
+ if(connect(s,(struct sockaddr *)&sa,sizeof(sa))< 0){
+ close(s);
+ ssh_set_error(NULL,SSH_FATAL,"connect: %s",strerror(errno));
+ return -1;
+ }
+ return s;
+}
+
+/* connection_new() returns a newly allocated SSH_SESSION structure pointer */
+SSH_SESSION *ssh_session_new() {
+ SSH_SESSION *conn=malloc(sizeof (SSH_SESSION));
+ memset(conn,0,sizeof(SSH_SESSION));
+ conn->next_crypto=crypto_new();
+ conn->maxchannel=FIRST_CHANNEL;
+ return conn;
+}
+
+
+/* returns 1 if bytes are available on the stream, 0 instead */
+int ssh_fd_poll(SSH_SESSION *session){
+#ifdef HAVE_POLL
+ struct pollfd fdset;
+#else
+ struct timeval sometime;
+ fd_set descriptor;
+#endif
+ if(session->datatoread)
+ return(session->datatoread);
+#ifdef HAVE_POLL
+ fdset.fd=session->fd;
+ fdset.events=POLLHUP|POLLIN|POLLPRI;
+ fdset.revents=0;
+ if(poll(&fdset,1,0)==0)
+ return 0;
+ if(fdset.revents & (POLLHUP|POLLIN|POLLPRI))
+ return (session->datatoread=1);
+ return 0;
+#elif HAVE_SELECT
+
+ /* Set to return immediately (no blocking) */
+ sometime.tv_sec = 0;
+ sometime.tv_usec = 0;
+
+ /* Set up descriptor */
+ FD_ZERO(&descriptor);
+ FD_SET(session->fd, &descriptor);
+
+ /* Make the call, and listen for errors */
+ if (select(session->fd + 1, &descriptor, NULL, NULL, &sometime) < 0) {
+ ssh_set_error(NULL,SSH_FATAL, "select: %s", strerror(errno));
+ return -1;
+ }
+ session->datatoread=FD_ISSET(session->fd,&descriptor);
+ return session->datatoread;
+#else
+#error This system does not have poll() or select(), so ssh_fd_poll() will not work correctly
+ return 0;
+#endif
+}
+
+/* this function is a complete wrapper for the select syscall. it does more than wrapping ... */
+int ssh_select(CHANNEL **channels,CHANNEL **outchannels, int maxfd, fd_set *readfds, struct timeval *timeout){
+ struct timeval zerotime;
+ fd_set localset,localset2;
+ int rep;
+ int i,j;
+ int set;
+
+ zerotime.tv_sec=0;
+ zerotime.tv_usec=0;
+ /* first, poll the maxfd file descriptors from the user with a zero-second timeout. they have the bigger priority */
+ if(maxfd>0){
+ memcpy(&localset,readfds, sizeof(fd_set));
+ rep=select(maxfd,&localset,NULL,NULL,&zerotime);
+ /* catch the eventual errors */
+ if(rep==-1)
+ return -1;
+ }
+ j=0;
+ /* polls every channel. */
+ for(i=0;channels[i];i++){
+ if(channel_poll(channels[i],0)>0){
+ outchannels[j]=channels[i];
+ j++;
+ } else
+ if(channel_poll(channels[i],1)>0){
+ outchannels[j]=channels[i];
+ j++;
+ }
+ }
+ outchannels[j]=NULL;
+ /* look into the localset for active fd */
+ set=0;
+ for(i=0;(i<maxfd) && !set;i++)
+ if(FD_ISSET(i,&localset))
+ set=1;
+ /* j!=0 means a channel has data */
+ if( (j!=0) || (set!=0)){
+ if(maxfd>0)
+ memcpy(readfds,&localset,sizeof(fd_set));
+ return 0;
+ }
+ /* at this point, not any channel had any data ready for reading, nor any fd had data for reading */
+ memcpy(&localset,readfds,sizeof(fd_set));
+ for(i=0;channels[i];i++){
+ if(channels[i]->session->alive){
+ FD_SET(channels[i]->session->fd,&localset);
+ if(channels[i]->session->fd>maxfd-1)
+ maxfd=channels[i]->session->fd+1;
+ }
+ }
+ do {
+ rep=select(maxfd,&localset,NULL,NULL,timeout);
+ } while (rep==-1 && errno==EINTR);
+ if(rep==-1){
+ /* was the error due to a libssh's Channel or from a closed descriptor from the user ? user closed descriptors have been
+ caught in the first select and not closed since that moment. that case shouldn't occur at all */
+ return -1;
+ }
+ /* set the data_to_read flag on each session */
+ for(i=0;channels[i];i++)
+ if(FD_ISSET(channels[i]->session->fd,&localset))
+ channels[i]->session->datatoread=1;
+
+ /* now, test each channel */
+ j=0;
+ for(i=0;channels[i];i++){
+ if(FD_ISSET(channels[i]->session->fd,&localset))
+ if((channel_poll(channels[i],0)>0) || (channel_poll(channels[i],1)>0)){
+ outchannels[j]=channels[i];
+ j++;
+ }
+ }
+ outchannels[j]=NULL;
+ FD_ZERO(&localset2);
+ for(i=0;i<maxfd;i++)
+ if(FD_ISSET(i,readfds) && FD_ISSET(i,&localset))
+ FD_SET(i,&localset2);
+ memcpy(readfds,&localset2,sizeof(fd_set));
+ return 0;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/crypt.c b/kftpgrabber/src/misc/libs/ssh/crypt.c
new file mode 100644
index 0000000..312c411
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/crypt.c
@@ -0,0 +1,100 @@
+/* crypt.c */
+/* it just contains the shit necessary to make blowfish-cbc work ... */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/blowfish.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+#include <netdb.h>
+#include "priv.h"
+#include "crypto.h"
+
+u32 packet_decrypt_len(SSH_SESSION *session, char *crypted){
+ u32 *decrypted;
+ if(session->current_crypto)
+ packet_decrypt(session,crypted,session->current_crypto->in_cipher->blocksize);
+ decrypted=(u32 *)crypted;
+ ssh_say(3,"size decrypted : %lx\n",ntohl(*decrypted));
+ return ntohl(*decrypted);
+}
+
+int packet_decrypt(SSH_SESSION *session, void *data,u32 len){
+ struct crypto_struct *crypto=session->current_crypto->in_cipher;
+ char *out=malloc(len);
+ ssh_say(3,"Decrypting %d bytes data\n",len);
+ crypto->set_decrypt_key(crypto,session->current_crypto->decryptkey);
+ crypto->cbc_decrypt(crypto,data,out,len,session->current_crypto->decryptIV);
+ memcpy(data,out,len);
+ memset(out,0,len);
+ free(out);
+ return 0;
+}
+
+char * packet_encrypt(SSH_SESSION *session,void *data,u32 len){
+ struct crypto_struct *crypto;
+ HMAC_CTX *ctx;
+ char *out;
+ int finallen;
+ u32 seq=ntohl(session->send_seq);
+ if(!session->current_crypto)
+ return NULL; /* nothing to do here */
+ crypto= session->current_crypto->out_cipher;
+ ssh_say(3,"seq num = %d, len = %d\n",session->send_seq,len);
+ crypto->set_encrypt_key(crypto,session->current_crypto->encryptkey);
+ out=malloc(len);
+ ctx=hmac_init(session->current_crypto->encryptMAC,20,HMAC_SHA1);
+ hmac_update(ctx,(unsigned char *)&seq,sizeof(u32));
+ hmac_update(ctx,data,len);
+ hmac_final(ctx,session->current_crypto->hmacbuf,&finallen);
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("mac :",data,len);
+ if(finallen!=20)
+ printf("Final len is %d\n",finallen);
+ ssh_print_hexa("packet hmac",session->current_crypto->hmacbuf,20);
+#endif
+ crypto->cbc_encrypt(crypto,data,out,len,session->current_crypto->encryptIV);
+ memcpy(data,out,len);
+ memset(out,0,len);
+ free(out);
+ return session->current_crypto->hmacbuf;
+}
+
+int packet_hmac_verify(SSH_SESSION *session,BUFFER *buffer,char *mac){
+ HMAC_CTX *ctx;
+ unsigned char hmacbuf[EVP_MAX_MD_SIZE];
+ int len;
+ u32 seq=htonl(session->recv_seq);
+ ctx=hmac_init(session->current_crypto->decryptMAC,20,HMAC_SHA1);
+ hmac_update(ctx,(unsigned char *)&seq,sizeof(u32));
+ hmac_update(ctx,buffer_get(buffer),buffer_get_len(buffer));
+ hmac_final(ctx,hmacbuf,&len);
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("received mac",mac,len);
+ ssh_print_hexa("Computed mac",hmacbuf,len);
+ ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(u32));
+#endif
+ return memcmp(mac,hmacbuf,len);
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/crypto.h b/kftpgrabber/src/misc/libs/ssh/crypto.h
new file mode 100644
index 0000000..83061e4
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/crypto.h
@@ -0,0 +1,47 @@
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+/* Crypto.h is an include file for internal structures of libssh */
+/* It hasn't to be into the final development set of files (and btw the filename would cause problems on most systems) */
+/* Openssl has (really) stupid defines */
+#ifdef set_key
+#undef set_key
+#endif
+#ifdef cbc_encrypt
+#undef cbc_encrypt
+#endif
+#ifdef cbc_decrypt
+#undef cbc_decrypt
+#endif
+#ifdef des_set_key
+#undef des_set_key
+#endif
+struct crypto_struct {
+ char *name; /* ssh name of the algorithm */
+ unsigned int blocksize; /* blocksize of the algo */
+ unsigned int keylen; /* length of the key structure */
+ void *key; /* a key buffer allocated for the algo */
+ unsigned int keysize; /* bytes of key used. != keylen */
+ void (*set_encrypt_key)(struct crypto_struct *cipher, void *key); /* sets the new key for immediate use */
+ void (*set_decrypt_key)(struct crypto_struct *cipher, void *key);
+ void (*cbc_encrypt)(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV);
+ void (*cbc_decrypt)(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV);
+};
+
diff --git a/kftpgrabber/src/misc/libs/ssh/dh.c b/kftpgrabber/src/misc/libs/ssh/dh.c
new file mode 100644
index 0000000..0a1b557
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/dh.c
@@ -0,0 +1,411 @@
+/* dh.c */
+/* this file contains usefull stuff for Diffie helman algorithm against SSH 2 */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+/* Let us resume the dh protocol. */
+/* Each side computes a private prime number, x at client side, y at server side. */
+/* g and n are two numbers common to every ssh software. */
+/* client's public key (e) is calculated by doing */
+/* e = g^x mod p */
+/* client sents e to the server . */
+/* the server computes his own public key, f */
+/* f = g^y mod p */
+/* it sents it to the client */
+/* the common key K is calculated by the client by doing */
+/* k = f^x mod p */
+/* the server does the same with the client public key e */
+/* k' = e^y mod p */
+/* if everything went correctly, k and k' are equal */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include "priv.h"
+
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <string.h>
+#include "crypto.h"
+static unsigned char p_value[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#define P_LEN 128 /* Size in bytes of the p number */
+
+static unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */
+static bignum g;
+static bignum p;
+
+/* maybe it might be enhanced .... */
+/* XXX Do it. */
+void ssh_get_random(void *where, int len){
+ static int rndfd=0;
+ if(!rndfd){
+ rndfd=open("/dev/urandom",O_RDONLY);
+ if(rndfd<0){
+ fprintf(stderr,"Can't open /dev/urandom\n");
+ exit(-1);
+ }
+ }
+ read(rndfd,where,len);
+}
+
+/* it inits the values g and p which are used for DH key agreement */
+void ssh_crypto_init(){
+ static int init=0;
+ if(!init){
+ g=bignum_new();
+ bignum_set_word(g,g_int);
+ p=bignum_new();
+ bignum_bin2bn(p_value,P_LEN,p);
+ init++;
+ }
+}
+
+/* prints the bignum on stderr */
+void ssh_print_bignum(char *which,bignum num){
+ char *hex;
+ fprintf(stderr,"%s value: ",which);
+ hex=bignum_bn2hex(num);
+ fprintf(stderr,"%s\n",hex);
+ free(hex);
+}
+
+void ssh_print_hexa(char *descr,unsigned char *what, int len){
+ int i;
+ printf("%s : ",descr);
+ for(i=0;i<len-1;i++)
+ printf("%.2hhx:",what[i]);
+ printf("%.2hhx\n",what[i]);
+}
+
+void dh_generate_x(SSH_SESSION *session){
+ session->next_crypto->x=bignum_new();
+ bignum_rand(session->next_crypto->x,128,0,-1);
+ /* not harder than this */
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("x",session->next_crypto->x);
+#endif
+}
+
+void dh_generate_e(SSH_SESSION *session){
+ bignum_CTX ctx=bignum_ctx_new();
+ session->next_crypto->e=bignum_new();
+ bignum_mod_exp(session->next_crypto->e,g,session->next_crypto->x,p,ctx);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("e",session->next_crypto->e);
+#endif
+ bignum_ctx_free(ctx);
+}
+
+
+STRING *make_bignum_string(bignum num){
+ STRING *ptr;
+ int pad=0;
+ int len=bignum_num_bytes(num);
+ int bits=bignum_num_bits(num);
+ int finallen;
+ /* remember if the fist bit is set, it is considered as a negative number. so 0's must be appended */
+ if(!(bits%8) && bignum_is_bit_set(num,bits-1))
+ pad++;
+ ssh_say(3,"%d bits, %d bytes, %d padding\n",bits,len,pad);
+ ptr=malloc(4 + len + pad);
+ ptr->size=htonl(len+pad);
+ if(pad)
+ ptr->string[0]=0;
+ finallen=bignum_bn2bin(num,ptr->string+pad);
+ return ptr;
+}
+
+bignum make_string_bn(STRING *string){
+ int len=ntohl(string->size);
+ ssh_say(3,"Importing a %d bits,%d bytes object ...\n",len*8,len);
+ return bignum_bin2bn(string->string,len,NULL);
+}
+
+STRING *dh_get_e(SSH_SESSION *session){
+ return make_bignum_string(session->next_crypto->e);
+}
+
+void dh_import_pubkey(SSH_SESSION *session,STRING *pubkey_string){
+ session->next_crypto->server_pubkey=pubkey_string;
+}
+
+void dh_import_f(SSH_SESSION *session,STRING *f_string){
+ session->next_crypto->f=make_string_bn(f_string);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("f",session->next_crypto->f);
+#endif
+}
+
+void dh_build_k(SSH_SESSION *session){
+ bignum_CTX ctx=bignum_ctx_new();
+ session->next_crypto->k=bignum_new();
+ bignum_mod_exp(session->next_crypto->k,session->next_crypto->f,session->next_crypto->x,p,ctx);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("shared secret key",session->next_crypto->k);
+#endif
+ bignum_ctx_free(ctx);
+}
+
+static void sha_add(STRING *str,SHACTX *ctx){
+ sha1_update(ctx,str,string_len(str)+4);
+}
+
+void make_sessionid(SSH_SESSION *session){
+ SHACTX *ctx;
+ STRING *num,*str;
+ int len;
+ ctx=sha1_init();
+
+ str=string_from_char(session->clientbanner);
+ sha_add(str,ctx);
+ free(str);
+
+ str=string_from_char(session->serverbanner);
+ sha_add(str,ctx);
+ free(str);
+
+ buffer_add_u32(session->in_hashbuf,0);
+ buffer_add_u8(session->in_hashbuf,0);
+ buffer_add_u32(session->out_hashbuf,0);
+ buffer_add_u8(session->out_hashbuf,0);
+
+ len=ntohl(buffer_get_len(session->out_hashbuf));
+ sha1_update(ctx,&len,4);
+
+ sha1_update(ctx,buffer_get(session->out_hashbuf),buffer_get_len(session->out_hashbuf));
+ buffer_free(session->out_hashbuf);
+ session->out_hashbuf=NULL;
+
+ len=ntohl(buffer_get_len(session->in_hashbuf));
+ sha1_update(ctx,&len,4);
+
+ sha1_update(ctx,buffer_get(session->in_hashbuf),buffer_get_len(session->in_hashbuf));
+ buffer_free(session->in_hashbuf);
+ session->in_hashbuf=NULL;
+ sha1_update(ctx,session->next_crypto->server_pubkey,len=(string_len(session->next_crypto->server_pubkey)+4));
+ num=make_bignum_string(session->next_crypto->e);
+ sha1_update(ctx,num,len=(string_len(num)+4));
+ free(num);
+ num=make_bignum_string(session->next_crypto->f);
+ sha1_update(ctx,num,len=(string_len(num)+4));
+ free(num);
+ num=make_bignum_string(session->next_crypto->k);
+ sha1_update(ctx,num,len=(string_len(num)+4));
+ free(num);
+ sha1_final(session->next_crypto->session_id,ctx);
+
+#ifdef DEBUG_CRYPTO
+ printf("Session hash : ");
+ ssh_print_hexa("session id",session->next_crypto->session_id,SHA_DIGEST_LENGTH);
+#endif
+}
+
+void hashbufout_add_cookie(SSH_SESSION *session){
+ session->out_hashbuf=buffer_new();
+ buffer_add_u8(session->out_hashbuf,20);
+ buffer_add_data(session->out_hashbuf,session->client_kex.cookie,16);
+}
+
+
+void hashbufin_add_cookie(SSH_SESSION *session,unsigned char *cookie){
+ session->in_hashbuf=buffer_new();
+ buffer_add_u8(session->in_hashbuf,20);
+ buffer_add_data(session->in_hashbuf,cookie,16);
+}
+
+static void generate_one_key(STRING *k,char session_id[SHA_DIGEST_LENGTH],char output[SHA_DIGEST_LENGTH],char letter){
+ SHACTX *ctx=sha1_init();
+ sha1_update(ctx,k,string_len(k)+4);
+ sha1_update(ctx,session_id,SHA_DIGEST_LENGTH);
+ sha1_update(ctx,&letter,1);
+ sha1_update(ctx,session_id,SHA_DIGEST_LENGTH);
+ sha1_final(output,ctx);
+}
+
+void generate_session_keys(SSH_SESSION *session){
+ STRING *k_string;
+ SHACTX *ctx;
+ k_string=make_bignum_string(session->next_crypto->k);
+
+ /* IV */
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptIV,'A');
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptIV,'B');
+
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptkey,'C');
+
+ /* some ciphers need more than 20 bytes of input key */
+ if(session->next_crypto->out_cipher->keylen > SHA_DIGEST_LENGTH*8){
+ ctx=sha1_init();
+ sha1_update(ctx,k_string,string_len(k_string)+4);
+ sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LENGTH);
+ sha1_update(ctx,session->next_crypto->encryptkey,SHA_DIGEST_LENGTH);
+ sha1_final(session->next_crypto->encryptkey+SHA_DIGEST_LEN,ctx);
+ }
+
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptkey,'D');
+
+ if(session->next_crypto->in_cipher->keylen > SHA_DIGEST_LENGTH*8){
+ ctx=sha1_init();
+ sha1_update(ctx,k_string,string_len(k_string)+4);
+ sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LENGTH);
+ sha1_update(ctx,session->next_crypto->decryptkey,SHA_DIGEST_LENGTH);
+ sha1_final(session->next_crypto->decryptkey+SHA_DIGEST_LEN,ctx);
+ }
+
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptMAC,'E');
+ generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptMAC,'F');
+
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("client->server IV",session->next_crypto->encryptIV,SHA_DIGEST_LENGTH);
+ ssh_print_hexa("server->client IV",session->next_crypto->decryptIV,SHA_DIGEST_LENGTH);
+ ssh_print_hexa("encryption key",session->next_crypto->encryptkey,16);
+ ssh_print_hexa("decryption key",session->next_crypto->decryptkey,16);
+ ssh_print_hexa("Encryption MAC",session->next_crypto->encryptMAC,SHA_DIGEST_LENGTH);
+ ssh_print_hexa("Decryption MAC",session->next_crypto->decryptMAC,20);
+#endif
+ free(k_string);
+}
+
+int ssh_get_pubkey_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]){
+ STRING *pubkey=session->current_crypto->server_pubkey;
+ MD5CTX *ctx;
+ int len=string_len(pubkey);
+
+ ctx=md5_init();
+ md5_update(ctx,pubkey->string,len);
+ md5_final(hash,ctx);
+ return MD5_DIGEST_LEN;
+}
+
+int pubkey_get_hash(SSH_SESSION *session, char hash[MD5_DIGEST_LEN]){
+ return ssh_get_pubkey_hash(session,hash);
+}
+
+STRING *ssh_get_pubkey(SSH_SESSION *session){
+ return string_copy(session->current_crypto->server_pubkey);
+}
+
+/* XXX i doubt it is still needed, or may need some fix */
+static int match(char *group,char *object){
+ char *ptr,*saved;
+ char *end;
+ ptr=strdup(group);
+ saved=ptr;
+ while(1){
+ end=strchr(ptr,',');
+ if(end)
+ *end=0;
+ if(!strcmp(ptr,object)){
+ free(saved);
+ return 0;
+ }
+ if(end)
+ ptr=end+1;
+ else{
+ free(saved);
+ return -1;
+ }
+ }
+ /* not reached */
+ return 1;
+}
+
+int sig_verify(PUBLIC_KEY *pubkey, SIGNATURE *signature, char *digest){
+ int valid=0;
+ char hash[SHA_DIGEST_LENGTH];
+ sha1(digest,SHA_DIGEST_LENGTH,hash);
+ switch(pubkey->type){
+ case TYPE_DSS:
+ valid=DSA_do_verify(hash,SHA_DIGEST_LENGTH,signature->dsa_sign,
+ pubkey->dsa_pub);
+ if(valid==1)
+ return 0;
+ if(valid==-1){
+ ssh_set_error(NULL,SSH_INVALID_DATA,"DSA error : %s",ERR_error_string(ERR_get_error(),NULL));
+ return -1;
+ }
+ ssh_set_error(NULL,SSH_NO_ERROR,"Invalid DSA signature");
+ return -1;
+ case TYPE_RSA:
+ case TYPE_RSA1:
+ valid=RSA_verify(NID_sha1,hash,SHA_DIGEST_LENGTH,
+ signature->rsa_sign->string,string_len(signature->rsa_sign),pubkey->rsa_pub);
+ if(valid==1)
+ return 0;
+ if(valid==-1){
+ ssh_set_error(NULL,SSH_INVALID_DATA,"RSA error : %s",ERR_error_string(ERR_get_error(),NULL));
+ return -1;
+ }
+ ssh_set_error(NULL,SSH_NO_ERROR,"Invalid RSA signature");
+ return -1;
+ default:
+ ssh_set_error(NULL,SSH_INVALID_DATA,"Unknown public key type");
+ return -1;
+ }
+return -1;
+}
+
+
+int signature_verify(SSH_SESSION *session,STRING *signature){
+ PUBLIC_KEY *pubkey;
+ SIGNATURE *sign;
+ int err;
+ if(session->options->dont_verify_hostkey){
+ ssh_say(1,"Host key wasn't verified\n");
+ return 0;
+ }
+ pubkey=publickey_from_string(session->next_crypto->server_pubkey);
+ if(!pubkey)
+ return -1;
+ if(session->options->wanted_methods[KEX_HOSTKEY]){
+ if(match(session->options->wanted_methods[KEX_HOSTKEY],pubkey->type_c)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Public key from server (%s) doesn't match user preference (%s)",
+ pubkey->type,session->options->wanted_methods[KEX_HOSTKEY]);
+ publickey_free(pubkey);
+ return -1;
+ }
+ }
+ sign=signature_from_string(signature,pubkey,pubkey->type);
+ if(!sign){
+ ssh_set_error((session->connected?session:NULL),SSH_INVALID_DATA,"Invalid signature blob");
+ publickey_free(pubkey);
+ return -1;
+ }
+ ssh_say(1,"Going to verify a %s type signature\n",pubkey->type_c);
+ err=sig_verify(pubkey,sign,session->next_crypto->session_id);
+ signature_free(sign);
+ session->next_crypto->server_pubkey_type=pubkey->type_c;
+ publickey_free(pubkey);
+ return err;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/error.c b/kftpgrabber/src/misc/libs/ssh/error.c
new file mode 100644
index 0000000..bbff5b2
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/error.c
@@ -0,0 +1,67 @@
+/* error.c */
+/* it does contain error processing functions */
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "priv.h"
+
+static char error_buffer[ERROR_BUFFERLEN];
+static int error_code;
+static int verbosity;
+
+/* ssh_set_error registers an error with a description. the error code is the class of error, and description is obvious.*/
+void ssh_set_error(SSH_SESSION *session,enum ssh_error code,char *descr,...){
+ va_list va;
+ va_start(va,descr);
+ vsnprintf(session?session->error_buffer : error_buffer,ERROR_BUFFERLEN,descr,va);
+ va_end(va);
+ if(session)
+ session->error_code=code;
+ else
+ error_code=code;
+}
+
+char *ssh_get_error(SSH_SESSION *session){
+ if(session)
+ return session->error_buffer;
+ else
+ return error_buffer;
+}
+
+enum ssh_error ssh_error_code(SSH_SESSION *session){
+ if(session)
+ return session->error_code;
+ else
+ return error_code;
+}
+
+void ssh_say(int priority, char *format,...){
+ va_list va;
+ va_start(va,format);
+ if(priority <= verbosity)
+ vfprintf(stderr,format,va);
+ va_end(va);
+}
+
+void ssh_set_verbosity(int num){
+ verbosity=num;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/gzip.c b/kftpgrabber/src/misc/libs/ssh/gzip.c
new file mode 100644
index 0000000..1003b50
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/gzip.c
@@ -0,0 +1,140 @@
+/* gzip.c */
+/* include hooks for compression of packets */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+#include "priv.h"
+#ifdef HAVE_LIBZ
+#undef NO_GZIP
+#else
+#define NO_GZIP
+#endif
+
+#ifndef NO_GZIP
+#include <zlib.h>
+#include <string.h>
+#define BLOCKSIZE 4092
+
+static z_stream *initcompress(SSH_SESSION *session,int level){
+ z_stream *stream=malloc(sizeof(z_stream));
+ int status;
+ memset(stream,0,sizeof(z_stream));
+ status=deflateInit(stream,level);
+ if (status!=0)
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"status %d inititalising zlib deflate",status);
+ return stream;
+}
+
+BUFFER *gzip_compress(SSH_SESSION *session,BUFFER *source,int level){
+ BUFFER *dest;
+ static unsigned char out_buf[BLOCKSIZE];
+ void *in_ptr=buffer_get(source);
+ unsigned long in_size=buffer_get_len(source);
+ unsigned long len;
+ int status;
+ z_stream *zout=session->current_crypto->compress_out_ctx;
+ if(!zout)
+ zout=session->current_crypto->compress_out_ctx=initcompress(session,level);
+ dest=buffer_new();
+ zout->next_out=out_buf;
+ zout->next_in=in_ptr;
+ zout->avail_in=in_size;
+ do {
+ zout->avail_out=BLOCKSIZE;
+ status=deflate(zout,Z_PARTIAL_FLUSH);
+ if(status !=0){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"status %d deflating zlib packet",status);
+ return NULL;
+ }
+ len=BLOCKSIZE-zout->avail_out;
+ buffer_add_data(dest,out_buf,len);
+ zout->next_out=out_buf;
+ } while (zout->avail_out == 0);
+
+ return dest;
+}
+
+int compress_buffer(SSH_SESSION *session,BUFFER *buf){
+ BUFFER *dest=gzip_compress(session,buf,9);
+ if(!dest)
+ return -1;
+ buffer_reinit(buf);
+ buffer_add_data(buf,buffer_get(dest),buffer_get_len(dest));
+ buffer_free(dest);
+ return 0;
+}
+
+/* decompression */
+
+static z_stream *initdecompress(SSH_SESSION *session){
+ z_stream *stream=malloc(sizeof(z_stream));
+ int status;
+ memset(stream,0,sizeof(z_stream));
+ status=inflateInit(stream);
+ if (status!=0){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Status = %d initiating inflate context !",status);
+ free(stream);
+ stream=NULL;
+ }
+ return stream;
+}
+
+BUFFER *gzip_decompress(SSH_SESSION *session,BUFFER *source){
+ BUFFER *dest;
+ static unsigned char out_buf[BLOCKSIZE];
+ void *in_ptr=buffer_get_rest(source);
+ unsigned long in_size=buffer_get_rest_len(source);
+ unsigned long len;
+ int status;
+ z_stream *zin=session->current_crypto->compress_in_ctx;
+ if(!zin)
+ zin=session->current_crypto->compress_in_ctx=initdecompress(session);
+ dest=buffer_new();
+ zin->next_out=out_buf;
+ zin->next_in=in_ptr;
+ zin->avail_in=in_size;
+ do {
+ zin->avail_out=BLOCKSIZE;
+ status=inflate(zin,Z_PARTIAL_FLUSH);
+ if(status !=Z_OK){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"status %d inflating zlib packet",status);
+ buffer_free(dest);
+ return NULL;
+ }
+ len=BLOCKSIZE-zin->avail_out;
+ buffer_add_data(dest,out_buf,len);
+ zin->next_out=out_buf;
+ } while (zin->avail_out == 0);
+
+ return dest;
+}
+
+int decompress_buffer(SSH_SESSION *session,BUFFER *buf){
+ BUFFER *dest=gzip_decompress(session,buf);
+ buffer_reinit(buf);
+ if(!dest){
+ return -1; /* failed */
+ }
+ buffer_reinit(buf);
+ buffer_add_data(buf,buffer_get(dest),buffer_get_len(dest));
+ buffer_free(dest);
+ return 0;
+}
+
+#endif /* NO_GZIP */
diff --git a/kftpgrabber/src/misc/libs/ssh/kex.c b/kftpgrabber/src/misc/libs/ssh/kex.c
new file mode 100644
index 0000000..ae2c871
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/kex.c
@@ -0,0 +1,264 @@
+/* kex.c is used well, in key exchange :-) */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <string.h>
+#include <stdlib.h>
+#include "priv.h"
+#include "ssh2.h"
+#ifdef HAVE_OPENSSL_BLOWFISH_H
+#define BLOWFISH "blowfish-cbc"
+#else
+#define BLOWFISH ""
+#endif
+#ifdef HAVE_OPENSSL_AES_H
+#define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
+#else
+#define AES ""
+#endif
+#ifdef HAVE_LIBZ
+#define ZLIB "none,zlib"
+#else
+#define ZLIB "none"
+#endif
+char *default_methods[]={
+ "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH, AES BLOWFISH,
+ "hmac-sha1","hmac-sha1","none","none","","",NULL };
+char *supported_methods[]={
+ "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH,AES BLOWFISH,
+ "hmac-sha1","hmac-sha1",ZLIB,ZLIB,"","",NULL };
+/* descriptions of the key exchange packet */
+char *ssh_kex_nums[]={
+ "kex algos","server host key algo","encryption client->server","encryption server->client",
+ "mac algo client->server","mac algo server->client","compression algo client->server",
+ "compression algo server->client","languages client->server","languages server->client",NULL};
+
+/* tokenize will return a token of strings delimited by ",". the first element has to be freed */
+static char **tokenize(char *chain){
+ char **tokens;
+ int n=1;
+ int i=0;
+ char *ptr=chain=strdup(chain);
+ while(*ptr){
+ if(*ptr==','){
+ n++;
+ *ptr=0;
+ }
+ ptr++;
+ }
+ /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
+ tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
+ ptr=chain;
+ for(i=0;i<n;i++){
+ tokens[i]=ptr;
+ while(*ptr)
+ ptr++; /* find a zero */
+ ptr++; /* then go one step further */
+ }
+ tokens[i]=NULL;
+ return tokens;
+}
+
+/* same as tokenize(), but with spaces instead of ',' */
+char **space_tokenize(char *chain){
+ char **tokens;
+ int n=1;
+ int i=0;
+ char *ptr=chain=strdup(chain);
+ while(*ptr==' ')
+ ++ptr; /* skip initial spaces */
+ while(*ptr){
+ if(*ptr==' '){
+ n++; /* count one token per word */
+ *ptr=0;
+ while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
+ *(ptr++)=0;
+ }
+ }
+ ptr++;
+ }
+ /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
+ tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
+ ptr=chain; /* we don't pass the initial spaces because the "chain" pointer is needed by the caller */
+ /* function to free the tokens. */
+ for(i=0;i<n;i++){
+ tokens[i]=ptr;
+ if(i!=n-1){
+ while(*ptr)
+ ptr++; /* find a zero */
+ while(!*(ptr+1))
+ ++ptr; /* if the zero is followed by other zeros, go through them */
+ ptr++; /* then go one step further */
+ }
+ }
+ tokens[i]=NULL;
+ return tokens;
+}
+
+/* find_matching gets 2 parameters : a list of available objects (in_d), separated by colons,*/
+/* and a list of prefered objects (what_d) */
+/* it will return a strduped pointer on the first prefered object found in the available objects list */
+
+static char *find_matching(char *in_d, char *what_d){
+ char ** tok_in, **tok_what;
+ int i_in, i_what;
+ char *ret;
+
+ if( ! (in_d && what_d))
+ return NULL; /* don't deal with null args */
+ ssh_say(3,"find_matching(\"%s\",\"%s\") = ",in_d,what_d);
+ tok_in=tokenize(in_d);
+ tok_what=tokenize(what_d);
+ for(i_in=0; tok_in[i_in]; ++i_in){
+ for(i_what=0; tok_what[i_what] ; ++i_what){
+ if(!strcmp(tok_in[i_in],tok_what[i_what])){
+ /* match */
+ ssh_say(3,"\"%s\"\n",tok_in[i_in]);
+ ret=strdup(tok_in[i_in]);
+ /* free the tokens */
+ free(tok_in[0]);
+ free(tok_what[0]);
+ free(tok_in);
+ free(tok_what);
+ return ret;
+ }
+ }
+ }
+ ssh_say(3,"NULL\n");
+ free(tok_in[0]);
+ free(tok_what[0]);
+ free(tok_in);
+ free(tok_what);
+ return NULL;
+}
+
+int ssh_get_kex(SSH_SESSION *session,int server_kex ){
+ STRING *str;
+ char *strings[10];
+ int i;
+ if(packet_wait(session,SSH2_MSG_KEXINIT,1))
+ return -1;
+ if(buffer_get_data(session->in_buffer,session->server_kex.cookie,16)!=16){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"get_kex(): no cookie in packet");
+ return -1;
+ }
+ hashbufin_add_cookie(session,session->server_kex.cookie);
+ memset(strings,0,sizeof(char *)*10);
+ for(i=0;i<10;++i){
+ str=buffer_get_ssh_string(session->in_buffer);
+ if(!str)
+ break;
+ if(str){
+ buffer_add_ssh_string(session->in_hashbuf,str);
+ strings[i]=string_to_char(str);
+ free(str);
+ } else
+ strings[i]=NULL;
+ }
+ /* copy the server kex info into an array of strings */
+ if(server_kex){
+ session->client_kex.methods=malloc( 10 * sizeof(char **));
+ for(i=0;i<10;++i)
+ session->client_kex.methods[i]=strings[i];
+ } else { /* client */
+ session->server_kex.methods=malloc( 10 * sizeof(char **));
+ for(i=0;i<10;++i)
+ session->server_kex.methods[i]=strings[i];
+ }
+ return 0;
+}
+
+void list_kex(KEX *kex){
+ int i=0;
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("session cookie",kex->cookie,16);
+#endif
+ for(i=0;i<10;i++){
+ ssh_say(2,"%s : %s\n",ssh_kex_nums[i],kex->methods[i]);
+ }
+}
+
+/* set_kex basicaly look at the option structure of the session and set the output kex message */
+/* it must be aware of the server kex message */
+/* it can fail if option is null, not any user specified kex method matches the server one, if not any default kex matches */
+
+int set_kex(SSH_SESSION *session){
+ KEX *server = &session->server_kex;
+ KEX *client=&session->client_kex;
+ SSH_OPTIONS *options=session->options;
+ int i;
+ char *wanted;
+ /* the client might ask for a specific cookie to be sent. useful for server debugging */
+ if(options->wanted_cookie)
+ memcpy(client->cookie,options->wanted_cookie,16);
+ else
+ ssh_get_random(client->cookie,16);
+ client->methods=malloc(10 * sizeof(char **));
+ memset(client->methods,0,10*sizeof(char **));
+ for (i=0;i<10;i++){
+ if(!(wanted=options->wanted_methods[i]))
+ wanted=default_methods[i];
+ client->methods[i]=find_matching(server->methods[i],wanted);
+ if(!client->methods[i] && i < KEX_LANG_C_S){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"kex error : did not find one of algos %s in list %s for %s",
+ wanted,server->methods[i],ssh_kex_nums[i]);
+ return -1;
+ } else {
+ if(i>=KEX_LANG_C_S && !client->methods[i])
+ client->methods[i]=strdup(""); /* we can safely do that for languages */
+ }
+ }
+ return 0;
+}
+
+/* this function only sends the predefined set of kex methods */
+void send_kex(SSH_SESSION *session, int server_kex){
+ STRING *str;
+ int i=0;
+ KEX *kex=(server_kex ? &session->server_kex : &session->client_kex);
+ packet_clear_out(session);
+ buffer_add_u8(session->out_buffer,SSH2_MSG_KEXINIT);
+ buffer_add_data(session->out_buffer,kex->cookie,16);
+ hashbufout_add_cookie(session);
+ list_kex(kex);
+ for(i=0;i<10;i++){
+ str=string_from_char(kex->methods[i]);
+ buffer_add_ssh_string(session->out_hashbuf,str);
+ buffer_add_ssh_string(session->out_buffer,str);
+ free(str);
+ }
+ i=0;
+ buffer_add_u8(session->out_buffer,0);
+ buffer_add_u32(session->out_buffer,0);
+ packet_send(session);
+}
+
+/* returns 1 if at least one of the name algos is in the default algorithms table */
+int verify_existing_algo(int algo, char *name){
+ char *ptr;
+ if(algo>9 || algo <0)
+ return -1;
+ ptr=find_matching(supported_methods[algo],name);
+ if(ptr){
+ free(ptr);
+ return 1;
+ }
+ return 0;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/keyfiles.c b/kftpgrabber/src/misc/libs/ssh/keyfiles.c
new file mode 100644
index 0000000..660155e
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/keyfiles.c
@@ -0,0 +1,341 @@
+/* keyfiles.c */
+/* This part of the library handles private and public key files needed for publickey authentication,*/
+/* as well as servers public hashes verifications and certifications. Lot of code here handles openssh */
+/* implementations (key files aren't standardized yet). */
+
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <openssl/pem.h>
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include "priv.h"
+#define MAXLINESIZE 80
+
+static int default_get_password(char *buf, int size,int rwflag, char *descr){
+ char *pass;
+ char buffer[256];
+ int len;
+ snprintf(buffer,256,"Please enter passphrase for %s",descr);
+ pass=getpass(buffer);
+ snprintf(buf,size,"%s",buffer);
+ len=strlen(buf);
+ memset(pass,0,strlen(pass));
+ return len;
+}
+
+/* in case the passphrase has been given in parameter */
+static int get_password_specified(char *buf,int size, int rwflag, char *password){
+ snprintf(buf,size,"%s",password);
+ return strlen(buf);
+}
+
+/* TODO : implement it to read both DSA and RSA at once */
+PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){
+ FILE *file=fopen(filename,"r");
+ PRIVATE_KEY *privkey;
+ DSA *dsa=NULL;
+ RSA *rsa=NULL;
+
+ if (!file) {
+ ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
+ return NULL;
+ }
+
+ if (!passphrase) {
+ return NULL;
+ }
+
+ /* Add all encryption algorythms to OpenSSL or this will fail */
+ OpenSSL_add_all_algorithms();
+
+ if (type == TYPE_DSS) {
+ dsa = PEM_read_DSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
+
+ fclose(file);
+ if(!dsa) {
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s : %s",filename,ERR_error_string(ERR_get_error(),NULL));
+ return NULL;
+ }
+ } else if (type == TYPE_RSA) {
+ rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
+
+ fclose(file);
+ if(!rsa){
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s : %s",filename,ERR_error_string(ERR_get_error(),NULL));
+ return NULL;
+ }
+ } else {
+ ssh_set_error(session,SSH_FATAL,"Invalid private key type %d",type);
+ return NULL;
+ }
+
+ privkey = malloc(sizeof(PRIVATE_KEY));
+ privkey->type = type;
+ privkey->dsa_priv = dsa;
+ privkey->rsa_priv = rsa;
+
+ return privkey;
+}
+
+void private_key_free(PRIVATE_KEY *prv){
+ if(prv->dsa_priv)
+ DSA_free(prv->dsa_priv);
+ if(prv->rsa_priv)
+ RSA_free(prv->rsa_priv);
+ memset(prv,0,sizeof(PRIVATE_KEY));
+ free(prv);
+}
+
+STRING *publickey_from_file(char *filename,int *_type){
+ BUFFER *buffer;
+ int type;
+ STRING *str;
+ char buf[4096]; /* noone will have bigger keys that that */
+ /* where have i head that again ? */
+ int fd=open(filename,O_RDONLY);
+ int r;
+ char *ptr;
+ if(fd<0){
+ ssh_set_error(NULL,SSH_INVALID_REQUEST,"nonexistent public key file");
+ return NULL;
+ }
+ if(read(fd,buf,8)!=8){
+ close(fd);
+ ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
+ return NULL;
+ }
+ buf[7]=0;
+ if(!strcmp(buf,"ssh-dss"))
+ type=TYPE_DSS;
+ else if (!strcmp(buf,"ssh-rsa"))
+ type=TYPE_RSA;
+ else {
+ close(fd);
+ ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
+ return NULL;
+ }
+ r=read(fd,buf,sizeof(buf)-1);
+ close(fd);
+ if(r<=0){
+ ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
+ return NULL;
+ }
+ buf[r]=0;
+ ptr=strchr(buf,' ');
+ if(ptr)
+ *ptr=0; /* eliminates the garbage at end of file */
+ buffer=base64_to_bin(buf);
+ if(buffer){
+ str=string_new(buffer_get_len(buffer));
+ string_fill(str,buffer_get(buffer),buffer_get_len(buffer));
+ buffer_free(buffer);
+ if(_type)
+ *_type=type;
+ return str;
+ } else {
+ ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
+ return NULL; /* invalid file */
+ }
+}
+
+
+/* why recursing ? i'll explain. on top, publickey_from_next_file will be executed until NULL returned */
+/* we can't return null if one of the possible keys is wrong. we must test them before getting over */
+STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path,
+ char **privkeyfile,int *type,int *count){
+ static char *home=NULL;
+ char public[256];
+ char private[256];
+ char *priv;
+ char *pub;
+ STRING *pubkey;
+ if(!home)
+ home=ssh_get_user_home_dir();
+ if(home==NULL) {
+ ssh_set_error(session,SSH_FATAL,"User home dir impossible to guess");
+ return NULL;
+ }
+ ssh_set_error(session,SSH_NO_ERROR,"no public key matched");
+ if((pub=pub_keys_path[*count])==NULL)
+ return NULL;
+ if((priv=keys_path[*count])==NULL)
+ return NULL;
+ ++*count;
+ /* are them readable ? */
+ snprintf(public,256,pub,home);
+ ssh_say(2,"Trying to open %s\n",public);
+ if(!ssh_file_readaccess_ok(public)){
+ ssh_say(2,"Failed\n");
+ return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
+ }
+ snprintf(private,256,priv,home);
+ ssh_say(2,"Trying to open %s\n",private);
+ if(!ssh_file_readaccess_ok(private)){
+ ssh_say(2,"Failed\n");
+ return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
+ }
+ ssh_say(2,"Okay both files ok\n");
+ /* ok, we are sure both the priv8 and public key files are readable : we return the public one as a string,
+ and the private filename in arguments */
+ pubkey=publickey_from_file(public,type);
+ if(!pubkey){
+ ssh_say(2,"Wasn't able to open public key file %s : %s\n",public,ssh_get_error(session));
+ return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
+ }
+ *privkeyfile=realloc(*privkeyfile,strlen(private)+1);
+ strcpy(*privkeyfile,private);
+ return pubkey;
+}
+
+#define FOUND_OTHER ( (void *)-1)
+#define FILE_NOT_FOUND ((void *)-2)
+/* will return a token array containing [host,]ip keytype key */
+/* NULL if no match was found, FOUND_OTHER if the match is on an other */
+/* type of key (ie dsa if type was rsa) */
+static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){
+ FILE *file=fopen(filename,"r");
+ char buffer[4096];
+ char *ptr;
+ char **tokens;
+ char **ret=NULL;
+ if(!file)
+ return FILE_NOT_FOUND;
+ while(fgets(buffer,sizeof(buffer),file)){
+ ptr=strchr(buffer,'\n');
+ if(ptr) *ptr=0;
+ if((ptr=strchr(buffer,'\r'))) *ptr=0;
+ if(!buffer[0])
+ continue; /* skip empty lines */
+ tokens=space_tokenize(buffer);
+ if(!tokens[0] || !tokens[1] || !tokens[2]){
+ /* it should have exactly 3 tokens */
+ free(tokens[0]);
+ free(tokens);
+ continue;
+ }
+ if(tokens[3]){
+ /* 3 tokens only, not four */
+ free(tokens[0]);
+ free(tokens);
+ continue;
+ }
+ ptr=tokens[0];
+ while(*ptr==' ')
+ ptr++; /* skip the initial spaces */
+ /* we allow spaces or ',' to follow the hostname. It's generaly an IP */
+ /* we don't care about ip, if the host key match there is no problem with ip */
+ if(strncasecmp(ptr,hostname,strlen(hostname))==0){
+ if(ptr[strlen(hostname)]==' ' || ptr[strlen(hostname)]=='\0'
+ || ptr[strlen(hostname)]==','){
+ if(strcasecmp(tokens[1],type)==0){
+ fclose(file);
+ return tokens;
+ } else {
+ ret=FOUND_OTHER;
+ }
+ }
+ }
+ /* not the good one */
+ free(tokens[0]);
+ free(tokens);
+ }
+ fclose(file);
+ /* we did not find */
+ return ret;
+}
+
+/* public function to test if the server is known or not */
+int ssh_is_server_known(SSH_SESSION *session){
+ char *pubkey_64;
+ BUFFER *pubkey_buffer;
+ STRING *pubkey=session->current_crypto->server_pubkey;
+ char **tokens;
+ options_default_known_hosts_file(session->options);
+ if(!session->options->host){
+ ssh_set_error(session,SSH_FATAL,"Can't verify host in known hosts if the hostname isn't known");
+ return SSH_SERVER_ERROR;
+ }
+ tokens=ssh_parse_knownhost(session->options->known_hosts_file,
+ session->options->host,session->current_crypto->server_pubkey_type);
+ if(tokens==NULL)
+ return SSH_SERVER_NOT_KNOWN;
+ if(tokens==FOUND_OTHER)
+ return SSH_SERVER_FOUND_OTHER;
+ if(tokens==FILE_NOT_FOUND){
+ ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : file %s not found",session->options->known_hosts_file);
+ return SSH_SERVER_ERROR;
+ }
+ /* ok we found some public key in known hosts file. now un-base64it */
+ /* Some time, we may verify the IP address did not change. I honestly think */
+ /* it's not an important matter as IP address are known not to be secure */
+ /* and the crypto stuff is enough to prove the server's identity */
+ pubkey_64=tokens[2];
+ pubkey_buffer=base64_to_bin(pubkey_64);
+ /* at this point, we may free the tokens */
+ free(tokens[0]);
+ free(tokens);
+ if(!pubkey_buffer){
+ ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : base 64 error");
+ return SSH_SERVER_ERROR;
+ }
+ if(buffer_get_len(pubkey_buffer)!=string_len(pubkey)){
+ buffer_free(pubkey_buffer);
+ return SSH_SERVER_KNOWN_CHANGED;
+ }
+ /* now test that they are identical */
+ if(memcmp(buffer_get(pubkey_buffer),pubkey->string,buffer_get_len(pubkey_buffer))!=0){
+ buffer_free(pubkey_buffer);
+ return SSH_SERVER_KNOWN_CHANGED;
+ }
+ buffer_free(pubkey_buffer);
+ return SSH_SERVER_KNOWN_OK;
+}
+
+int ssh_write_knownhost(SSH_SESSION *session){
+ char *pubkey_64;
+ STRING *pubkey=session->current_crypto->server_pubkey;
+ char buffer[4096];
+ FILE *file;
+ options_default_known_hosts_file(session->options);
+ if(!session->options->host){
+ ssh_set_error(session,SSH_FATAL,"Cannot write host in known hosts if the hostname is unknown");
+ return -1;
+ }
+ /* a = append only */
+ file=fopen(session->options->known_hosts_file,"a");
+ if(!file){
+ ssh_set_error(session,SSH_FATAL,"Opening known host file %s for appending : %s",
+ session->options->known_hosts_file,strerror(errno));
+ return -1;
+ }
+ pubkey_64=bin_to_base64(pubkey->string,string_len(pubkey));
+ snprintf(buffer,sizeof(buffer),"%s %s %s\n",session->options->host,session->current_crypto->server_pubkey_type,pubkey_64);
+ free(pubkey_64);
+ fwrite(buffer,strlen(buffer),1,file);
+ fclose(file);
+ return 0;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/keys.c b/kftpgrabber/src/misc/libs/ssh/keys.c
new file mode 100644
index 0000000..f404f4b
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/keys.c
@@ -0,0 +1,353 @@
+/* keys handle the public key related functions */
+/* decoding a public key (both rsa and dsa), decoding a signature (rsa and dsa), veryfying them */
+
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+#include <string.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include "priv.h"
+
+
+/* Public key decoding functions */
+
+char *ssh_type_to_char(int type){
+ switch(type){
+ case TYPE_DSS:
+ return "ssh-dss";
+ case TYPE_RSA:
+ case TYPE_RSA1:
+ return "ssh-rsa";
+ default:
+ return NULL;
+ }
+}
+
+PUBLIC_KEY *publickey_make_dss(BUFFER *buffer){
+ STRING *p,*q,*g,*pubkey;
+ PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY));
+ key->type=TYPE_DSS;
+ key->type_c="ssh-dss";
+ p=buffer_get_ssh_string(buffer);
+ q=buffer_get_ssh_string(buffer);
+ g=buffer_get_ssh_string(buffer);
+ pubkey=buffer_get_ssh_string(buffer);
+ buffer_free(buffer); /* we don't need it anymore */
+ if(!p || !q || !g || !pubkey){
+ ssh_set_error(NULL,SSH_FATAL,"Invalid DSA public key");
+ if(p)
+ free(p);
+ if(q)
+ free(q);
+ if(g)
+ free(g);
+ if(pubkey)
+ free(pubkey);
+ free(key);
+ return NULL;
+ }
+ key->dsa_pub=DSA_new();
+ key->dsa_pub->p=make_string_bn(p);
+ key->dsa_pub->q=make_string_bn(q);
+ key->dsa_pub->g=make_string_bn(g);
+ key->dsa_pub->pub_key=make_string_bn(pubkey);
+ free(p);
+ free(q);
+ free(g);
+ free(pubkey);
+ return key;
+}
+
+PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer){
+ STRING *e,*n;
+ PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY));
+ key->type=TYPE_RSA;
+ key->type_c="ssh-rsa";
+ e=buffer_get_ssh_string(buffer);
+ n=buffer_get_ssh_string(buffer);
+ buffer_free(buffer); /* we don't need it anymore */
+ if(!e || !n){
+ ssh_set_error(NULL,SSH_FATAL,"Invalid RSA public key");
+ if(e)
+ free(e);
+ if(n)
+ free(n);
+ free(key);
+ return NULL;
+ }
+ key->rsa_pub=RSA_new();
+ key->rsa_pub->e=make_string_bn(e);
+ key->rsa_pub->n=make_string_bn(n);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("e",key->rsa_pub->e);
+ ssh_print_bignum("n",key->rsa_pub->n);
+#endif
+ free(e);
+ free(n);
+ return key;
+}
+
+void publickey_free(PUBLIC_KEY *key){
+ if(!key)
+ return;
+ switch(key->type){
+ case TYPE_DSS:
+ DSA_free(key->dsa_pub);
+ break;
+ case TYPE_RSA:
+ case TYPE_RSA1:
+ RSA_free(key->rsa_pub);
+ break;
+ default:
+ break;
+ }
+ free(key);
+}
+
+PUBLIC_KEY *publickey_from_string(STRING *pubkey_s){
+ BUFFER *tmpbuf=buffer_new();
+ STRING *type_s;
+ char *type;
+
+ buffer_add_data(tmpbuf,pubkey_s->string,string_len(pubkey_s));
+ type_s=buffer_get_ssh_string(tmpbuf);
+ if(!type_s){
+ buffer_free(tmpbuf);
+ ssh_set_error(NULL,SSH_FATAL,"Invalid public key format");
+ return NULL;
+ }
+ type=string_to_char(type_s);
+ free(type_s);
+ if(!strcmp(type,"ssh-dss")){
+ free(type);
+ return publickey_make_dss(tmpbuf);
+ }
+ if(!strcmp(type,"ssh-rsa")){
+ free(type);
+ return publickey_make_rsa(tmpbuf);
+ }
+ ssh_set_error(NULL,SSH_FATAL,"unknown public key protocol %s",type);
+ buffer_free(tmpbuf);
+ free(type);
+ return NULL;
+}
+
+/* Signature decoding functions */
+
+STRING *signature_to_string(SIGNATURE *sign){
+ STRING *str;
+ STRING *rs,*r,*s;
+ unsigned char buffer[40];
+ BUFFER *tmpbuf=buffer_new();
+ STRING *tmp;
+ tmp=string_from_char(ssh_type_to_char(sign->type));
+ buffer_add_ssh_string(tmpbuf,tmp);
+ free(tmp);
+ switch(sign->type){
+ case TYPE_DSS:
+ r=make_bignum_string(sign->dsa_sign->r);
+ s=make_bignum_string(sign->dsa_sign->s);
+ rs=string_new(40);
+ memset(buffer,0,40);
+ memcpy(buffer,r->string+string_len(r)-20,20);
+ memcpy(buffer+ 20, s->string + string_len(s) - 20, 20);
+ string_fill(rs,buffer,40);
+ free(r);
+ free(s);
+ buffer_add_ssh_string(tmpbuf,rs);
+ free(rs);
+ break;
+ case TYPE_RSA:
+ case TYPE_RSA1:
+ buffer_add_ssh_string(tmpbuf,sign->rsa_sign);
+ break;
+ }
+ str=string_new(buffer_get_len(tmpbuf));
+ string_fill(str,buffer_get(tmpbuf),buffer_get_len(tmpbuf));
+ buffer_free(tmpbuf);
+ return str;
+}
+
+/* TODO : split this function in two so it becomes smaller */
+SIGNATURE *signature_from_string(STRING *signature,PUBLIC_KEY *pubkey,int needed_type){
+ DSA_SIG *sig;
+ SIGNATURE *sign=malloc(sizeof(SIGNATURE));
+ BUFFER *tmpbuf=buffer_new();
+ STRING *rs;
+ STRING *r,*s,*type_s,*e;
+ int len,rsalen;
+ char *type;
+ buffer_add_data(tmpbuf,signature->string,string_len(signature));
+ type_s=buffer_get_ssh_string(tmpbuf);
+ if(!type_s){
+ ssh_set_error(NULL,SSH_FATAL,"Invalid signature packet");
+ buffer_free(tmpbuf);
+ return NULL;
+ }
+ type=string_to_char(type_s);
+ free(type_s);
+ switch(needed_type){
+ case TYPE_DSS:
+ if(strcmp(type,"ssh-dss")){
+ ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
+ buffer_free(tmpbuf);
+ free(type);
+ return NULL;
+ }
+ break;
+ case TYPE_RSA:
+ if(strcmp(type,"ssh-rsa")){
+ ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
+ buffer_free(tmpbuf);
+ free(type);
+ return NULL;
+ }
+ break;
+ default:
+ ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
+ free(type);
+ buffer_free(tmpbuf);
+ return NULL;
+ }
+ free(type);
+ switch(needed_type){
+ case TYPE_DSS:
+ rs=buffer_get_ssh_string(tmpbuf);
+ buffer_free(tmpbuf);
+ if(!rs || string_len(rs)!=40){ /* 40 is the dual signature blob len. */
+ if(rs)
+ free(rs);
+ return NULL;
+ }
+ /* we make use of strings because we have all-made functions to convert them to bignums */
+ r=string_new(20);
+ s=string_new(20);
+ string_fill(r,rs->string,20);
+ string_fill(s,rs->string+20,20);
+ free(rs);
+ sig=DSA_SIG_new();
+ sig->r=make_string_bn(r); /* is that really portable ? Openssh's hack isn't better */
+ sig->s=make_string_bn(s);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("r",sig->r);
+ ssh_print_bignum("s",sig->s);
+#endif
+ free(r);
+ free(s);
+ sign->type=TYPE_DSS;
+ sign->dsa_sign=sig;
+ return sign;
+ case TYPE_RSA:
+ e=buffer_get_ssh_string(tmpbuf);
+ buffer_free(tmpbuf);
+ if(!e){
+ free(e);
+ return NULL;
+ }
+ len=string_len(e);
+ rsalen=RSA_size(pubkey->rsa_pub);
+ if(len>rsalen){
+ free(e);
+ free(sign);
+ ssh_set_error(NULL,SSH_FATAL,"signature too big ! %d instead of %d",len,rsalen);
+ return NULL;
+ }
+ if(len<rsalen)
+ ssh_say(0,"Len %d < %d\n",len,rsalen);
+ sign->type=TYPE_RSA;
+ sign->rsa_sign=e;
+#ifdef DEBUG_CRYPTO
+ ssh_say(0,"Len : %d\n",len);
+ ssh_print_hexa("rsa signature",e->string,len);
+#endif
+ return sign;
+ default:
+ return NULL;
+ }
+}
+
+void signature_free(SIGNATURE *sign){
+ if(!sign)
+ return;
+ switch(sign->type){
+ case TYPE_DSS:
+ DSA_SIG_free(sign->dsa_sign);
+ break;
+ case TYPE_RSA:
+ case TYPE_RSA1:
+ free(sign->rsa_sign);
+ break;
+ default:
+ ssh_say(1,"freeing a signature with no type !\n");
+ }
+ free(sign);
+}
+
+/* maybe the missing function from libcrypto */
+/* i think now, maybe it's a bad idea to name it has it should have be named in libcrypto */
+static STRING *RSA_do_sign(void *payload,int len,RSA *privkey){
+ STRING *sign;
+ void *buffer=malloc(RSA_size(privkey));
+ unsigned int size;
+ int err;
+ err=RSA_sign(NID_sha1,payload,len,buffer,&size,privkey);
+ if(!err){
+ free(buffer);
+ return NULL;
+ }
+ sign=string_new(size);
+ string_fill(sign,buffer,size);
+ free(buffer);
+ return sign;
+}
+
+STRING *ssh_do_sign(SSH_SESSION *session,BUFFER *sigbuf, PRIVATE_KEY *privatekey){
+ SHACTX *ctx;
+ STRING *session_str=string_new(SHA_DIGEST_LEN);
+ char hash[SHA_DIGEST_LEN];
+ SIGNATURE *sign;
+ STRING *signature;
+ string_fill(session_str,session->current_crypto->session_id,SHA_DIGEST_LENGTH);
+ ctx=sha1_init();
+ sha1_update(ctx,session_str,string_len(session_str)+4);
+ sha1_update(ctx,buffer_get(sigbuf),buffer_get_len(sigbuf));
+ sha1_final(hash,ctx);
+ free(session_str);
+ sign=malloc(sizeof(SIGNATURE));
+ switch(privatekey->type){
+ case TYPE_DSS:
+ sign->dsa_sign=DSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->dsa_priv);
+ sign->rsa_sign=NULL;
+ break;
+ case TYPE_RSA:
+ sign->rsa_sign=RSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->rsa_priv);
+ sign->dsa_sign=NULL;
+ break;
+ }
+ sign->type=privatekey->type;
+ if(!sign->dsa_sign && !sign->rsa_sign){
+ ssh_set_error(session,SSH_FATAL,"Signing : openssl error");
+ signature_free(sign);
+ return NULL;
+ }
+ signature=signature_to_string(sign);
+ signature_free(sign);
+ return signature;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/libssh.h b/kftpgrabber/src/misc/libs/ssh/libssh.h
new file mode 100644
index 0000000..7fdc939
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/libssh.h
@@ -0,0 +1,218 @@
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#ifndef _LIBSSH_H
+#define _LIBSSH_H
+#include "config.h"
+#include <unistd.h>
+#include <sys/select.h> /* for fd_set * */
+#include <sys/types.h>
+#define LIBSSH_VERSION "libssh-0.11-dev"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct string_struct STRING;
+typedef struct buffer_struct BUFFER;
+typedef struct public_key_struct PUBLIC_KEY;
+typedef struct private_key_struct PRIVATE_KEY;
+typedef struct ssh_options_struct SSH_OPTIONS;
+typedef struct channel_struct CHANNEL;
+typedef struct ssh_session SSH_SESSION;
+typedef struct ssh_kbdint SSH_KBDINT;
+
+/* integer values */
+typedef u_int32_t u32;
+typedef u_int16_t u16;
+typedef u_int64_t u64;
+typedef u_int8_t u8;
+
+/* the offsets of methods */
+#define KEX_ALGO 0
+#define KEX_HOSTKEY 1
+#define KEX_CRYPT_C_S 2
+#define KEX_CRYPT_S_C 3
+#define KEX_MAC_C_S 4
+#define KEX_MAC_S_C 5
+#define KEX_COMP_C_S 6
+#define KEX_COMP_S_C 7
+#define KEX_LANG_C_S 8
+#define KEX_LANG_S_C 9
+
+#define SSH_AUTH_SUCCESS 0
+#define SSH_AUTH_DENIED 1
+#define SSH_AUTH_PARTIAL 2
+#define SSH_AUTH_INFO 3
+#define SSH_AUTH_ERROR -1
+
+#define SSH_SERVER_ERROR -1
+#define SSH_SERVER_NOT_KNOWN 0
+#define SSH_SERVER_KNOWN_OK 1
+#define SSH_SERVER_KNOWN_CHANGED 2
+#define SSH_SERVER_FOUND_OTHER 3
+
+#ifndef MD5_DIGEST_LEN
+ #define MD5_DIGEST_LEN 16
+#endif
+/* errors */
+
+enum ssh_error {SSH_NO_ERROR, SSH_REQUEST_DENIED, SSH_INVALID_REQUEST, SSH_CONNECTION_LOST,SSH_FATAL,SSH_INVALID_DATA};
+char *ssh_get_error(SSH_SESSION *session); /* returns a static char array */
+enum ssh_error ssh_error_code(SSH_SESSION *session);
+void ssh_say(int priority,char *format,...);
+void ssh_set_verbosity(int num);
+
+ /* There is a verbosity level */
+ /* 3 : packet level */
+ /* 2 : protocol level */
+ /* 1 : functions level */
+ /* 0 : important messages only */
+ /* -1 : no messages */
+
+/* in client.c */
+
+SSH_SESSION *ssh_connect(SSH_OPTIONS *options);
+void ssh_disconnect(SSH_SESSION *session);
+int ssh_service_request(SSH_SESSION *session,char *service);
+char *ssh_get_issue_banner(SSH_SESSION *session);
+/* get copyright informations */
+const char *ssh_copyright();
+/* string.h */
+
+/* You can use these functions, they won't change */
+/* makestring returns a newly allocated string from a char * ptr */
+STRING *string_from_char(char *what);
+/* it returns the string len in host byte orders. str->size is big endian warning ! */
+int string_len(STRING *str);
+STRING *string_new(u32 size);
+/* string_fill copies the data in the string. it does NOT check for boundary so allocate enough place with new_string */
+/* right before */
+void string_fill(STRING *str,void *data,int len);
+/* returns a newly allocated char array with the str string and a final nul caracter */
+char *string_to_char(STRING *str);
+STRING *string_copy(STRING *str);
+
+/* deprecated */
+void ssh_crypto_init();
+
+/* useful for debug */
+void ssh_print_hexa(char *descr,unsigned char *what, int len);
+void ssh_get_random(void *,int);
+
+/* this one can be called by the client to see the hash of the public key before accepting it */
+int ssh_get_pubkey_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]);
+STRING *ssh_get_pubkey(SSH_SESSION *session);
+
+/* deprecated */
+int pubkey_get_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]);
+
+/* in connect.c */
+int ssh_fd_poll(SSH_SESSION *session);
+int ssh_select(CHANNEL **channels,CHANNEL **outchannels, int maxfd, fd_set *readfds, struct timeval *timeout);
+
+void publickey_free(PUBLIC_KEY *key);
+
+/* in keyfiles.c */
+
+PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase);
+void private_key_free(PRIVATE_KEY *prv);
+STRING *publickey_from_file(char *filename,int *_type);
+STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path,
+ char **privkeyfile,int *type,int *count);
+int ssh_is_server_known(SSH_SESSION *session);
+int ssh_write_knownhost(SSH_SESSION *session);
+
+/* in channels.c */
+
+/* this one is deprecated */
+CHANNEL *open_session_channel(SSH_SESSION *session,int window,int maxpacket);
+CHANNEL *channel_open_forward(SSH_SESSION *session,char *remotehost, int remoteport, char *sourcehost, int localport);
+CHANNEL *channel_open_session(SSH_SESSION *session);
+void channel_free(CHANNEL *channel);
+int channel_request_pty(CHANNEL *channel);
+int channel_request_pty_size(CHANNEL *channel, char *term,int cols, int rows);
+int channel_change_pty_size(CHANNEL *channel,int cols,int rows);
+int channel_request_shell(CHANNEL *channel);
+int channel_request_subsystem(CHANNEL *channel, char *system);
+int channel_request_env(CHANNEL *channel,char *name, char *value);
+int channel_request_exec(CHANNEL *channel, char *cmd);
+int channel_request_sftp(CHANNEL *channel);
+int channel_write(CHANNEL *channel,void *data,int len);
+int channel_set_write_handler(CHANNEL *channel,
+ void (*write_fct)(CHANNEL *channel, void *data, int len, void *userdefined),
+ void *user);
+int channel_set_stderr_write_handler(CHANNEL *channel,
+ void (*write_err_fct)(CHANNEL *channel, void *data, int len, void *userdefined),
+ void *user);
+int channel_send_eof(CHANNEL *channel);
+int channel_read(CHANNEL *channel, BUFFER *buffer,int bytes,int is_stderr);
+int channel_poll(CHANNEL *channel, int is_stderr);
+int channel_close(CHANNEL *channel);
+int channel_read_nonblocking(CHANNEL *channel, char *dest, int len, int is_stderr);
+int channel_is_open(CHANNEL *channel);
+/* in options.c */
+
+SSH_OPTIONS *options_new();
+SSH_OPTIONS *options_copy(SSH_OPTIONS *opt);
+int options_set_wanted_method(SSH_OPTIONS *opt,int method, char *list);
+void options_set_username(SSH_OPTIONS *opt,char *username);
+void options_set_port(SSH_OPTIONS *opt, unsigned int port);
+SSH_OPTIONS *ssh_getopt(int *argcptr, char **argv);
+void options_set_host(SSH_OPTIONS *opt, const char *host);
+/* don't connect to host, use fd instead */
+void options_set_fd(SSH_OPTIONS *opt, int fd);
+void options_set_bindaddr(SSH_OPTIONS *opt, char *bindaddr);
+void options_set_identity(SSH_OPTIONS *opt, char *identity);
+void options_set_status_callback(SSH_OPTIONS *opt, void (*callback)(void *arg, float status), void *arg);
+void options_set_timeout(SSH_OPTIONS *opt, long seconds, long usec);
+void options_set_ssh_dir(SSH_OPTIONS *opt, char *dir);
+void options_set_known_hosts_file(SSH_OPTIONS *opt, char *dir);
+/* buffer.c */
+
+BUFFER *buffer_new();
+void buffer_free(BUFFER *buffer);
+/* buffer_get returns a pointer to the begining of the buffer. no position is taken into account */
+void *buffer_get(BUFFER *buffer);
+/* same here */
+int buffer_get_len(BUFFER *buffer);
+
+
+/* in auth.c */
+/* these functions returns AUTH_ERROR is some serious error has happened,
+ AUTH_SUCCESS if success,
+ AUTH_PARTIAL if partial success,
+ AUTH_DENIED if refused */
+int ssh_userauth_none(SSH_SESSION *session,char *username);
+int ssh_userauth_password(SSH_SESSION *session,char *username,char *password);
+int ssh_userauth_offer_pubkey(SSH_SESSION *session, char *username,int type, STRING *publickey);
+int ssh_userauth_pubkey(SSH_SESSION *session, char *username, STRING *publickey, PRIVATE_KEY *privatekey);
+int ssh_userauth_autopubkey(SSH_SESSION *session, const char *passphrase);
+int ssh_userauth_kbdint(SSH_SESSION *session, char *user, char *submethods);
+int ssh_userauth_kbdint_getnprompts(SSH_SESSION *session);
+char *ssh_userauth_kbdint_getname(SSH_SESSION *session);
+char *ssh_userauth_kbdint_getinstruction(SSH_SESSION *session);
+char *ssh_userauth_kbdint_getprompt(SSH_SESSION *session, int i, char *echo);
+void ssh_userauth_kbdint_setanswer(SSH_SESSION *session, unsigned int i, char *answer);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LIBSSH_H */
diff --git a/kftpgrabber/src/misc/libs/ssh/misc.c b/kftpgrabber/src/misc/libs/ssh/misc.c
new file mode 100644
index 0000000..fc3cb79
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/misc.c
@@ -0,0 +1,98 @@
+/* misc.c */
+/* some misc routines than aren't really part of the ssh protocols but can be useful to the client */
+
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include "libssh.h"
+
+/* if the program was executed suid root, don't trust the user ! */
+static int is_trusted(){
+ if(geteuid()!=getuid())
+ return 0;
+ return 1;
+}
+
+static char *get_homedir_from_uid(int uid){
+ struct passwd *pwd;
+ char *home;
+ while((pwd=getpwent())){
+ if(pwd->pw_uid == uid){
+ home=strdup(pwd->pw_dir);
+ endpwent();
+ return home;
+ }
+ }
+ endpwent();
+ return NULL;
+}
+
+static char *get_homedir_from_login(char *user){
+ struct passwd *pwd;
+ char *home;
+ while((pwd=getpwent())){
+ if(!strcmp(pwd->pw_name,user)){
+ home=strdup(pwd->pw_dir);
+ endpwent();
+ return home;
+ }
+ }
+ endpwent();
+ return NULL;
+}
+
+char *ssh_get_user_home_dir(){
+ char *home;
+ char *user;
+ int trusted=is_trusted();
+ if(trusted){
+ if((home=getenv("HOME")))
+ return strdup(home);
+ if((user=getenv("USER")))
+ return get_homedir_from_login(user);
+ }
+ return get_homedir_from_uid(getuid());
+}
+
+/* we have read access on file */
+int ssh_file_readaccess_ok(char *file){
+ if(!access(file,R_OK))
+ return 1;
+ return 0;
+}
+
+
+u64 ntohll(u64 a){
+#ifdef WORDS_BIGENDIAN
+ return a;
+#else
+ u32 low=a & 0xffffffff;
+ u32 high = a >> 32 ;
+ low=ntohl(low);
+ high=ntohl(high);
+ return (( ((u64)low) << 32) | ( high));
+#endif
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/options.c b/kftpgrabber/src/misc/libs/ssh/options.c
new file mode 100644
index 0000000..74ab189
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/options.c
@@ -0,0 +1,341 @@
+/* options.c */
+/* handle pre-connection options */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include "priv.h"
+
+SSH_OPTIONS *options_new(){
+ SSH_OPTIONS *option=malloc(sizeof(SSH_OPTIONS));
+ memset(option,0,sizeof(SSH_OPTIONS));
+ option->port=22; /* set the default port */
+ option->fd=-1;
+ return option;
+}
+
+void options_set_port(SSH_OPTIONS *opt, unsigned int port){
+ opt->port=port&0xffff;
+}
+SSH_OPTIONS *options_copy(SSH_OPTIONS *opt){
+ SSH_OPTIONS *ret=options_new();
+ int i;
+ ret->fd=opt->fd;
+ ret->port=opt->port;
+ if(opt->username)
+ ret->username=strdup(opt->username);
+ if(opt->host)
+ ret->host=strdup(opt->host);
+ if(opt->bindaddr)
+ ret->host=strdup(opt->bindaddr);
+ if(opt->identity)
+ ret->identity=strdup(opt->identity);
+ if(opt->ssh_dir)
+ ret->ssh_dir=strdup(opt->ssh_dir);
+ if(opt->known_hosts_file)
+ ret->known_hosts_file=strdup(opt->known_hosts_file);
+ for(i=0;i<10;++i)
+ if(opt->wanted_methods[i])
+ ret->wanted_methods[i]=strdup(opt->wanted_methods[i]);
+ ret->passphrase_function=opt->passphrase_function;
+ ret->connect_status_function=opt->connect_status_function;
+ ret->connect_status_arg=opt->connect_status_arg;
+ ret->timeout=opt->timeout;
+ ret->timeout_usec=opt->timeout_usec;
+ return ret;
+}
+
+void options_free(SSH_OPTIONS *opt){
+ int i;
+ if(opt->username)
+ free(opt->username);
+ if(opt->identity)
+ free(opt->identity);
+ /* we don't touch the banner. if the implementation did use it, they have to free it */
+ if(opt->host)
+ free(opt->host);
+ if(opt->bindaddr)
+ free(opt->bindaddr);
+ if(opt->ssh_dir)
+ free(opt->ssh_dir);
+ for(i=0;i<10;i++)
+ if(opt->wanted_methods[i])
+ free(opt->wanted_methods[i]);
+ memset(opt,0,sizeof(SSH_OPTIONS));
+ free(opt);
+}
+
+
+void options_set_host(SSH_OPTIONS *opt, const char *hostname){
+ char *ptr=strdup(hostname);
+ char *ptr2=strchr(ptr,'@');
+ if(opt->host) /* don't leak memory */
+ free(opt->host);
+ if(ptr2){
+ *ptr2=0;
+ opt->host=strdup(ptr2+1);
+ if(opt->username)
+ free(opt->username);
+ opt->username=strdup(ptr);
+ free(ptr);
+ } else
+ opt->host=ptr;
+}
+
+void options_set_fd(SSH_OPTIONS *opt, int fd){
+ opt->fd=fd;
+}
+
+void options_set_bindaddr(SSH_OPTIONS *opt, char *bindaddr){
+ opt->bindaddr=strdup(bindaddr);
+}
+
+void options_set_username(SSH_OPTIONS *opt,char *username){
+ opt->username=strdup(username);
+}
+
+void options_set_ssh_dir(SSH_OPTIONS *opt, char *dir){
+ char buffer[1024];
+ snprintf(buffer,1024,dir,ssh_get_user_home_dir());
+ opt->ssh_dir=strdup(buffer);
+}
+void options_set_known_hosts_file(SSH_OPTIONS *opt, char *dir){
+ char buffer[1024];
+ snprintf(buffer,1024,dir,ssh_get_user_home_dir());
+ opt->known_hosts_file=strdup(buffer);
+}
+
+void options_set_identity(SSH_OPTIONS *opt, char *identity){
+ char buffer[1024];
+ snprintf(buffer,1024,identity,ssh_get_user_home_dir());
+ opt->identity=strdup(buffer);
+}
+
+/* what's the deal here ? some options MUST be set before authentication or key exchange,
+ * otherwise default values are going to be used. what must be configurable :
+ * Public key certification method *
+ * key exchange method (dh-sha1 for instance)*
+ * c->s, s->c ciphers *
+ * c->s s->c macs *
+ * c->s s->c compression */
+
+/* they all return 0 if all went well, 1 or !=0 if not. the most common error is unmatched algo (unimplemented) */
+/* don't forget other errors can happen if no matching algo is found in sshd answer */
+
+int options_set_wanted_method(SSH_OPTIONS *opt,int method, char *list){
+ if(method > 9 || method < 0){
+ ssh_set_error(NULL,SSH_FATAL,"method %d out of range",method);
+ return -1;
+ }
+ if( (!opt->use_nonexisting_algo) && !verify_existing_algo(method,list)){
+ ssh_set_error(NULL,SSH_FATAL,"Setting method : no algorithm for method \"%s\" (%s)\n",ssh_kex_nums[method],list);
+ return -1;
+ }
+ if(opt->wanted_methods[method])
+ free(opt->wanted_methods[method]);
+ opt->wanted_methods[method]=strdup(list);
+ return 0;
+}
+
+static char *get_username_from_uid(int uid){
+ struct passwd *pwd;
+ char *user;
+ while((pwd=getpwent())){
+ if(pwd->pw_uid == uid){
+ user=strdup(pwd->pw_name);
+ endpwent();
+ return user;
+ }
+ }
+ endpwent();
+ ssh_set_error(NULL,SSH_FATAL,"uid %d doesn't exist !",uid);
+ return NULL;
+}
+
+/* this function must be called when no specific username has been asked. it has to guess it */
+int options_default_username(SSH_OPTIONS *opt){
+ char *user;
+ if(opt->username)
+ return 0;
+ user=getenv("USER");
+ if(user){
+ opt->username=strdup(user);
+ return 0;
+ }
+ user=get_username_from_uid(getuid());
+ if(user){
+ opt->username=user;
+ return 0;
+ }
+ return -1;
+}
+
+int options_default_ssh_dir(SSH_OPTIONS *opt){
+ char buffer[256];
+ if(opt->ssh_dir)
+ return 0;
+ snprintf(buffer,256,"%s/.ssh/",ssh_get_user_home_dir());
+ opt->ssh_dir=strdup(buffer);
+ return 0;
+}
+
+int options_default_known_hosts_file(SSH_OPTIONS *opt){
+ char buffer[1024];
+ if(opt->known_hosts_file)
+ return 0;
+ options_default_ssh_dir(opt);
+ snprintf(buffer,1024,"%s/known_hosts",opt->ssh_dir);
+ opt->known_hosts_file=strdup(buffer);
+ return 0;
+}
+
+void options_set_status_callback(SSH_OPTIONS *opt, void (*callback)(void *arg, float status), void *arg ){
+ opt->connect_status_function=callback;
+ opt->connect_status_arg=arg;
+}
+
+void options_set_timeout(SSH_OPTIONS *opt, long seconds,long usec){
+ opt->timeout=seconds;
+ opt->timeout_usec=usec;
+}
+
+SSH_OPTIONS *ssh_getopt(int *argcptr, char **argv){
+ int i;
+ int argc=*argcptr;
+ char *user=NULL;
+ int port=22;
+ int debuglevel=0;
+ int usersa=0;
+ int usedss=0;
+ int compress=0;
+ int cont=1;
+ char *cipher=NULL;
+ char *localaddr=NULL;
+ char *identity=NULL;
+ char **save=malloc(argc * sizeof(char *));
+ int current=0;
+
+ int saveoptind=optind; /* need to save 'em */
+ int saveopterr=opterr;
+ SSH_OPTIONS *options;
+ opterr=0; /* shut up getopt */
+ while(cont && ((i=getopt(argc,argv,"c:i:Cl:p:vb:rd12"))!=-1)){
+
+ switch(i){
+ case 'l':
+ user=optarg;
+ break;
+ case 'p':
+ port=atoi(optarg)&0xffff;
+ break;
+ case 'v':
+ debuglevel++;
+ break;
+ case 'r':
+ usersa++;
+ break;
+ case 'd':
+ usedss++;
+ break;
+ case 'c':
+ cipher=optarg;
+ break;
+ case 'i':
+ identity=optarg;
+ break;
+ case 'b':
+ localaddr=optarg;
+ break;
+ case 'C':
+ compress++;
+ break;
+ case '2':
+ break; /* only ssh2 support till now */
+ case '1':
+ ssh_set_error(NULL,SSH_FATAL,"libssh does not support SSH1 protocol");
+ cont =0;
+ break;
+ default:
+ {
+ char opt[3]="- ";
+ opt[1]=optopt;
+ save[current++]=strdup(opt);
+ if(optarg)
+ save[current++]=argv[optind+1];
+ }
+ }
+ }
+ opterr=saveopterr;
+ while(optind < argc)
+ save[current++]=argv[optind++];
+
+ if(usersa && usedss){
+ ssh_set_error(NULL,SSH_FATAL,"either RSA or DSS must be chosen");
+ cont=0;
+ }
+ ssh_set_verbosity(debuglevel);
+ optind=saveoptind;
+ if(!cont){
+ free(save);
+ return NULL;
+ }
+ /* first recopy the save vector into original's */
+ for(i=0;i<current;i++)
+ argv[i+1]=save[i]; /* don't erase argv[0] */
+ argv[current+1]=NULL;
+ *argcptr=current+1;
+ free(save);
+ /* set a new option struct */
+ options=options_new();
+ if(compress){
+ if(options_set_wanted_method(options,KEX_COMP_C_S,"zlib"))
+ cont=0;
+ if(options_set_wanted_method(options,KEX_COMP_S_C,"zlib"))
+ cont=0;
+ }
+ if(cont &&cipher){
+ if(options_set_wanted_method(options,KEX_CRYPT_C_S,cipher))
+ cont=0;
+ if(cont && options_set_wanted_method(options,KEX_CRYPT_S_C,cipher))
+ cont=0;
+ }
+ if(cont && usersa)
+ if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-rsa"))
+ cont=0;
+ if(cont && usedss)
+ if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-dss"))
+ cont=0;
+ if(cont && user)
+ options_set_username(options,user);
+ if(cont && identity)
+ options_set_identity(options,identity);
+ if(cont && localaddr)
+ options_set_bindaddr(options,localaddr);
+ options_set_port(options,port);
+ if(!cont){
+ options_free(options);
+ return NULL;
+ } else
+ return options;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/packet.c b/kftpgrabber/src/misc/libs/ssh/packet.c
new file mode 100644
index 0000000..c41e3a5
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/packet.c
@@ -0,0 +1,303 @@
+/* packet.c */
+/* packet building functions */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "priv.h"
+#include "ssh2.h"
+#include <netdb.h>
+#include <errno.h>
+#include "crypto.h"
+
+/* XXX include selected mac size */
+static int macsize=SHA_DIGEST_LENGTH;
+
+/* completeread will read blocking until len bytes have been read */
+static int completeread(int fd, void *buffer, int len){
+ int r;
+ int total=0;
+ int toread=len;
+ while((r=read(fd,buffer+total,toread))){
+ if(r==-1)
+ return -1;
+ total += r;
+ toread-=r;
+ if(total==len)
+ return len;
+ if(r==0)
+ return 0;
+ }
+ return total ; /* connection closed */
+}
+
+int packet_read(SSH_SESSION *session){
+ u32 len;
+ void *packet=NULL;
+ char mac[30];
+ char buffer[16];
+ int be_read,i;
+ int to_be_read;
+ u8 padding;
+ unsigned int blocksize=(session->current_crypto?session->current_crypto->in_cipher->blocksize:8);
+ session->datatoread=0; /* clear the dataavailable flag */
+ memset(&session->in_packet,0,sizeof(PACKET));
+ if(session->in_buffer)
+ buffer_free(session->in_buffer);
+ session->in_buffer=buffer_new();
+
+ be_read=completeread(session->fd,buffer,blocksize);
+ if(be_read!=blocksize){
+ if(be_read<=0){
+ session->alive=0;
+ close(session->fd);
+ session->fd=-1;
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,
+ (be_read==0)?"Connection closed by remote host" : "Error reading socket");
+ return -1;
+ }
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): asked %d bytes, received %d",blocksize,be_read);
+ return -1;
+ }
+ len=packet_decrypt_len(session,buffer);
+ buffer_add_data(session->in_buffer,buffer,blocksize);
+
+ if(len> MAX_PACKET_LEN){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): Packet len too high(%uld %.8lx)",len,len);
+ return -1;
+ }
+ to_be_read=len-be_read+sizeof(u32);
+ if(to_be_read<0){
+ /* remote sshd is trying to get me ?*/
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"given numbers of bytes left to be read <0 (%d)!",to_be_read);
+ return -1;
+ }
+ /* handle the case in which the whole packet size = blocksize */
+ if(to_be_read !=0){
+ packet=malloc(to_be_read);
+ i=completeread(session->fd,packet,to_be_read);
+ if(i<=0){
+ session->alive=0;
+ close(session->fd);
+ session->fd=-1;
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Server closed connection");
+ return -1;
+ }
+ if(i!=to_be_read){
+ free(packet);
+ packet=NULL;
+ ssh_say(3,"Read only %d, wanted %d\n",i,to_be_read);
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): read only %d, wanted %d",i,to_be_read);
+ return -1;
+ }
+ ssh_say(3,"Read a %d bytes packet\n",len);
+ buffer_add_data(session->in_buffer,packet,to_be_read);
+ free(packet);
+ }
+ if(session->current_crypto){
+ packet_decrypt(session,buffer_get(session->in_buffer)+blocksize,buffer_get_len(session->in_buffer)-blocksize);
+ if((i=completeread(session->fd,mac,macsize))!=macsize){
+ if(i<=0){
+ session->alive=0;
+ close(session->fd);
+ session->fd=-1;
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Server closed connection");
+ return -1;
+ }
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): wanted %d, had %d",i,macsize);
+ return -1;
+ }
+ if(packet_hmac_verify(session,session->in_buffer,mac)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"HMAC error");
+ return -1;
+ }
+ }
+ buffer_pass_bytes(session->in_buffer,sizeof(u32)); /*pass the size which has been processed before*/
+ if(!buffer_get_u8(session->in_buffer,&padding)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Packet too short to read padding");
+ return -1;
+ }
+ ssh_say(3,"%hhd bytes padding\n",padding);
+ if(padding > buffer_get_rest_len(session->in_buffer)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"invalid padding: %d (%d resting)",padding,buffer_get_rest_len(session->in_buffer));
+ ssh_print_hexa("incrimined packet",buffer_get(session->in_buffer),buffer_get_len(session->in_buffer));
+ return -1;
+ }
+ buffer_pass_bytes_end(session->in_buffer,padding);
+#ifdef HAVE_LIBZ
+ if(session->current_crypto && session->current_crypto->do_compress_in){
+ decompress_buffer(session,session->in_buffer);
+ }
+#endif
+ session->recv_seq++;
+ return 0;
+}
+
+int packet_translate(SSH_SESSION *session){
+ memset(&session->in_packet,0,sizeof(PACKET));
+ if(!session->in_buffer)
+ return -1;
+ ssh_say(3,"Final size %d\n",buffer_get_rest_len(session->in_buffer));
+ if(!buffer_get_u8(session->in_buffer,&session->in_packet.type)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Packet too short to read type");
+ return -1;
+ }
+ ssh_say(3,"type %hhd\n",session->in_packet.type);
+ session->in_packet.valid=1;
+ return 0;
+}
+
+static int atomic_write(int fd, void *buffer, int len){
+ int written;
+ int total=0;
+ do {
+ written=write(fd,buffer,len);
+ if(written==0)
+ return 0;
+ if(written==-1)
+ return total;
+ total+=written;
+ len-=written;
+ buffer+=written;
+ } while (len > 0);
+ return total;
+}
+
+int packet_send(SSH_SESSION *session){
+ char padstring[32];
+ u32 finallen;
+ u8 padding;
+ u32 currentlen=buffer_get_len(session->out_buffer);
+ char *hmac;
+ int ret=0;
+ unsigned int blocksize=(session->current_crypto?session->current_crypto->out_cipher->blocksize:8);
+ ssh_say(3,"Writing on the wire a packet having %ld bytes before",currentlen);
+#ifdef HAVE_LIBZ
+ if(session->current_crypto && session->current_crypto->do_compress_out){
+ compress_buffer(session,session->out_buffer);
+ currentlen=buffer_get_len(session->out_buffer);
+ }
+#endif
+ padding=(blocksize- ((currentlen+5) % blocksize));
+ if(padding<4)
+ padding+=blocksize;
+ if(session->current_crypto)
+ ssh_get_random(padstring,padding);
+ else
+ memset(padstring,0,padding);
+ finallen=htonl(currentlen+padding+1);
+ ssh_say(3,",%d bytes after comp + %d padding bytes = %d bytes packet\n",currentlen,padding,(ntohl(finallen)));
+ buffer_add_data_begin(session->out_buffer,&padding,sizeof(u8));
+ buffer_add_data_begin(session->out_buffer,&finallen,sizeof(u32));
+ buffer_add_data(session->out_buffer,padstring,padding);
+ hmac=packet_encrypt(session,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer));
+ if(hmac)
+ buffer_add_data(session->out_buffer,hmac,20);
+ if(atomic_write(session->fd,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer))!=buffer_get_len(session->out_buffer)){
+ session->alive=0;
+ close(session->fd);
+ session->fd=-1;
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Writing packet : error on socket (or connection closed): %s",
+ strerror(errno));
+ ret=-1;
+ }
+ session->send_seq++;
+ buffer_reinit(session->out_buffer);
+ return ret;
+}
+
+void packet_parse(SSH_SESSION *session){
+ int type=session->in_packet.type;
+ u32 foo;
+ STRING *error_s;
+ char *error=NULL;
+
+ switch(type){
+ case SSH2_MSG_DISCONNECT:
+ buffer_get_u32(session->in_buffer,&foo);
+ error_s=buffer_get_ssh_string(session->in_buffer);
+ if(error_s)
+ error=string_to_char(error_s);
+ ssh_say(2,"Received SSH_MSG_DISCONNECT\n");
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Received SSH_MSG_DISCONNECT : %s",error);
+ if(error_s){
+ free(error_s);
+ free(error);
+ }
+ close(session->fd);
+ session->fd=-1;
+ session->alive=0;
+ return;
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ case SSH2_MSG_CHANNEL_DATA:
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ case SSH2_MSG_CHANNEL_REQUEST:
+ case SSH2_MSG_CHANNEL_EOF:
+ case SSH2_MSG_CHANNEL_CLOSE:
+
+ channel_handle(session,type);
+ case SSH2_MSG_IGNORE:
+ return;
+ default:
+ ssh_say(0,"Received unhandled msg %d\n",type);
+ }
+}
+int packet_wait(SSH_SESSION *session,int type,int blocking){
+ while(1){
+ if(packet_read(session))
+ return -1;
+ if(packet_translate(session))
+ return -1;
+ switch(session->in_packet.type){
+ case SSH2_MSG_DISCONNECT:
+ packet_parse(session);
+ return -1;
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ case SSH2_MSG_CHANNEL_DATA:
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ case SSH2_MSG_CHANNEL_REQUEST:
+ case SSH2_MSG_CHANNEL_EOF:
+ case SSH2_MSG_CHANNEL_CLOSE:
+ packet_parse(session);
+ break;;
+ case SSH2_MSG_IGNORE:
+ break;
+ default:
+ if(type && (type != session->in_packet.type)){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"waitpacket(): Received a %d type packet, was waiting for a %d\n",session->in_packet.type,type);
+ return -1;
+ }
+ return 0;
+ }
+ if(blocking==0)
+ return 0;
+ }
+ return 0;
+}
+
+void packet_clear_out(SSH_SESSION *session){
+ if(session->out_buffer)
+ buffer_reinit(session->out_buffer);
+ else
+ session->out_buffer=buffer_new();
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/priv.h b/kftpgrabber/src/misc/libs/ssh/priv.h
new file mode 100644
index 0000000..2c93081
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/priv.h
@@ -0,0 +1,384 @@
+/*
+Copyright 2003,04 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+/* priv.h file */
+/* This include file contains everything you shouldn't deal with in user programs. */
+/* Consider that anything in this file might change without notice; libssh.h file will keep */
+/* backward compatibility on binary & source */
+
+#ifndef _LIBSSH_PRIV_H
+#define _LIBSSH_PRIV_H
+#include "libssh.h"
+
+/* Debugging constants */
+
+/* Define this if you want to debug crypto systems */
+/* it's usefull when you are debugging the lib */
+/*#define DEBUG_CRYPTO */
+
+/* some constants */
+#define MAX_PACKET_LEN 262144
+#define ERROR_BUFFERLEN 1024
+#define CLIENTBANNER "SSH-2.0-" LIBSSH_VERSION
+#define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */
+/* some types for public keys */
+#define TYPE_DSS 1
+#define TYPE_RSA 2
+#define TYPE_RSA1 3
+
+/* profiling constants. Don't touch them unless you know what you do */
+#define OPENSSL_CRYPTO
+#define OPENSSL_BIGNUMS
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* wrapper things */
+
+#ifdef OPENSSL_CRYPTO
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+#include <openssl/hmac.h>
+typedef SHA_CTX SHACTX;
+typedef MD5_CTX MD5CTX;
+typedef HMAC_CTX HMACCTX;
+#ifdef MD5_DIGEST_LEN
+ #undef MD5_DIGEST_LEN
+#endif
+#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
+#define MD5_DIGEST_LEN MD5_DIGEST_LENGTH
+
+#endif /* OPENSSL_CRYPTO */
+#ifdef OPENSSL_BIGNUMS
+#include <openssl/bn.h>
+typedef BIGNUM* bignum;
+typedef BN_CTX* bignum_CTX;
+
+#define bignum_new() BN_new()
+#define bignum_free(num) BN_clear_free(num)
+#define bignum_set_word(bn,n) BN_set_word(bn,n)
+#define bignum_bin2bn(bn,datalen,data) BN_bin2bn(bn,datalen,data)
+#define bignum_bn2hex(num) BN_bn2hex(num)
+#define bignum_rand(rnd, bits, top, bottom) BN_rand(rnd,bits,top,bottom)
+#define bignum_ctx_new() BN_CTX_new()
+#define bignum_ctx_free(num) BN_CTX_free(num)
+#define bignum_mod_exp(dest,generator,exp,modulo,ctx) BN_mod_exp(dest,generator,exp,modulo,ctx)
+#define bignum_num_bytes(num) BN_num_bytes(num)
+#define bignum_num_bits(num) BN_num_bits(num)
+#define bignum_is_bit_set(num,bit) BN_is_bit_set(num,bit)
+#define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr)
+
+#endif /* OPENSSL_BIGNUMS */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/* wrapper.c */
+MD5CTX *md5_init(void);
+void md5_update(MD5CTX *c, const void *data, unsigned long len);
+void md5_final(unsigned char *md,MD5CTX *c);
+SHACTX *sha1_init(void);
+void sha1_update(SHACTX *c, const void *data, unsigned long len);
+void sha1_final(unsigned char *md,SHACTX *c);
+void sha1(unsigned char *digest,int len,unsigned char *hash);
+#define HMAC_SHA1 1
+#define HMAC_MD5 2
+HMACCTX *hmac_init(const void *key,int len,int type);
+void hmac_update(HMACCTX *c, const void *data, unsigned long len);
+void hmac_final(HMACCTX *ctx,unsigned char *hashmacbuf,int *len);
+
+/* strings and buffers */
+/* must be 32 bits number + immediatly our data */
+struct string_struct {
+ u32 size;
+ char string[MAX_PACKET_LEN];
+} __attribute__ ((packed));
+
+
+struct buffer_struct {
+ char *data;
+ int used;
+ int allocated;
+ int pos;
+};
+
+/* i should remove it one day */
+typedef struct packet_struct {
+ int valid;
+ u32 len;
+ u8 type;
+} PACKET;
+
+typedef struct kex_struct {
+ char cookie[16];
+ char **methods;
+} KEX;
+
+struct public_key_struct {
+ int type;
+ char *type_c; /* Don't free it ! it is static */
+ DSA *dsa_pub;
+ RSA *rsa_pub;
+};
+
+struct private_key_struct {
+ int type;
+ DSA *dsa_priv;
+ RSA *rsa_priv;
+};
+
+typedef struct signature_struct {
+ int type;
+ DSA_SIG *dsa_sign;
+ STRING *rsa_sign;
+} SIGNATURE;
+
+struct ssh_options_struct {
+ char *clientbanner; /* explicit banner to send */
+ char *username;
+ char *host;
+ char *bindaddr;
+ char *identity;
+ char *ssh_dir;
+ char *known_hosts_file;
+ int fd; /* specificaly wanted file descriptor, don't connect host */
+ int port;
+ int dont_verify_hostkey; /* Don't spare time, don't check host key ! unneeded to say it's dangerous and not safe */
+ int use_nonexisting_algo; /* if user sets a not supported algorithm for kex, don't complain */
+ char *wanted_methods[10]; /* the kex methods can be choosed. better use the kex fonctions to do that */
+ void *wanted_cookie; /* wants a specific cookie to be sent ? if null, generate a new one */
+ void *passphrase_function; /* this functions will be called if a keyphrase is needed. look keyfiles.c for more info */
+ void (*connect_status_function)(void *arg, float status); /* status callback function */
+ void *connect_status_arg; /* arbitrary argument */
+ long timeout; /* seconds */
+ long timeout_usec;
+ };
+
+typedef struct ssh_crypto_struct {
+ bignum e,f,x,k;
+ char session_id[SHA_DIGEST_LEN];
+
+ char encryptIV[SHA_DIGEST_LEN];
+ char decryptIV[SHA_DIGEST_LEN];
+
+ char decryptkey[SHA_DIGEST_LEN*2];
+ char encryptkey[SHA_DIGEST_LEN*2];
+
+ char encryptMAC[SHA_DIGEST_LEN];
+ char decryptMAC[SHA_DIGEST_LEN];
+ char hmacbuf[EVP_MAX_MD_SIZE];
+ struct crypto_struct *in_cipher, *out_cipher; /* the cipher structures/objects */
+ STRING *server_pubkey;
+ char *server_pubkey_type;
+ int do_compress_out; /* idem */
+ int do_compress_in; /* don't set them, set the option instead */
+ void *compress_out_ctx; /* don't touch it */
+ void *compress_in_ctx; /* really, don't */
+} CRYPTO;
+
+struct channel_struct {
+ struct channel_struct *prev;
+ struct channel_struct *next;
+ SSH_SESSION *session; /* SSH_SESSION pointer */
+ u32 local_channel;
+ u32 local_window;
+ int local_eof;
+ u32 local_maxpacket;
+ u32 remote_channel;
+ u32 remote_window;
+ int remote_eof; /* end of file received */
+ u32 remote_maxpacket;
+ int open; /* shows if the channel is still opened */
+ void (*write_fct)(struct channel_struct *channel, void *data, int len, void *userarg);
+ /* this write function is a callback on some userdefined function which is used for writing datas *coming from remote ssh* */
+ /* use channel_write() to write into a ssh pipe */
+ void (*write_err_fct)(struct channel_struct *channel, void *data, int len, void *userarg);
+ /* same as write_fct for stderr */
+ BUFFER *stdout_buffer;
+ BUFFER *stderr_buffer;
+ void *userarg;
+};
+
+struct ssh_session {
+ int fd;
+ SSH_OPTIONS *options;
+ char *serverbanner;
+ char *clientbanner;
+ int protoversion;
+ u32 send_seq;
+ u32 recv_seq;
+ int connected; /* !=0 when the user got a session handle */
+ int alive;
+ int auth_service_asked;
+ int datatoread; /* reading now on socket will not block */
+ STRING *banner; /* that's the issue banner from the server */
+ BUFFER *in_buffer;
+ PACKET in_packet;
+ BUFFER *out_buffer;
+ KEX server_kex;
+ KEX client_kex;
+ BUFFER *in_hashbuf;
+ BUFFER *out_hashbuf;
+ CRYPTO *current_crypto;
+ CRYPTO *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
+ CHANNEL *channels; /* linked list of channels */
+ int maxchannel;
+ int error_code;
+ char error_buffer[ERROR_BUFFERLEN];
+ struct ssh_kbdint *kbdint;
+};
+
+struct ssh_kbdint {
+ u32 nprompts;
+ char *name;
+ char *instruction;
+ char **prompts;
+ char *echo; /* bool array */
+ char **answers;
+};
+
+/* errors.c */
+void ssh_set_error(SSH_SESSION *session,enum ssh_error code,char *descr,...);
+
+/* in dh.c */
+/* DH key generation */
+void dh_generate_e(SSH_SESSION *session);
+void dh_generate_x(SSH_SESSION *session);
+STRING *dh_get_e(SSH_SESSION *session);
+void dh_import_f(SSH_SESSION *session,STRING *f_string);
+void dh_import_pubkey(SSH_SESSION *session,STRING *pubkey_string);
+void dh_build_k(SSH_SESSION *session);
+void make_sessionid(SSH_SESSION *session);
+/* add data for the final cookie */
+void hashbufin_add_cookie(SSH_SESSION *session,unsigned char *cookie);
+void hashbufout_add_cookie(SSH_SESSION *session);
+void generate_session_keys(SSH_SESSION *session);
+/* returns 1 if server signature ok, 0 otherwise. The NEXT crypto is checked, not the current one */
+int signature_verify(SSH_SESSION *session,STRING *signature);
+bignum make_string_bn(STRING *string);
+STRING *make_bignum_string(bignum num);
+
+/* in crypt.c */
+u32 packet_decrypt_len(SSH_SESSION *session,char *crypted);
+int packet_decrypt(SSH_SESSION *session, void *packet,unsigned int len);
+char *packet_encrypt(SSH_SESSION *session,void *packet,unsigned int len);
+ /* it returns the hmac buffer if exists*/
+int packet_hmac_verify(SSH_SESSION *session,BUFFER *buffer,char *mac);
+
+/* in packet.c */
+void packet_clear_out(SSH_SESSION *session);
+void packet_parse(SSH_SESSION *session);
+int packet_send(SSH_SESSION *session);
+int packet_read(SSH_SESSION *session);
+int packet_translate(SSH_SESSION *session);
+int packet_wait(SSH_SESSION *session,int type,int blocking);
+
+/* connect.c */
+SSH_SESSION *ssh_session_new();
+int ssh_connect_host(const char *host,const char *bind_addr, int port, long timeout, long usec);
+
+/* in kex.c */
+extern char *ssh_kex_nums[];
+void send_kex(SSH_SESSION *session,int server_kex);
+void list_kex(KEX *kex);
+int set_kex(SSH_SESSION *session);
+int ssh_get_kex(SSH_SESSION *session, int server_kex);
+int verify_existing_algo(int algo,char *name);
+char **space_tokenize(char *chain);
+
+/* in keys.c */
+char *ssh_type_to_char(int type);
+PUBLIC_KEY *publickey_make_dss(BUFFER *buffer);
+PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer);
+PUBLIC_KEY *publickey_from_string(STRING *pubkey_s);
+SIGNATURE *signature_from_string(STRING *signature,PUBLIC_KEY *pubkey,int needed_type);
+void signature_free(SIGNATURE *sign);
+STRING *ssh_do_sign(SSH_SESSION *session,BUFFER *sigbuf, PRIVATE_KEY *privatekey);
+
+/* channel.c */
+void channel_handle(SSH_SESSION *session, int type);
+
+/* options.c */
+void options_free(SSH_OPTIONS *opt);
+/* this function must be called when no specific username has been asked. it has to guess it */
+int options_default_username(SSH_OPTIONS *opt);
+int options_default_ssh_dir(SSH_OPTIONS *opt);
+int options_default_known_hosts_file(SSH_OPTIONS *opt);
+
+/* buffer.c */
+void buffer_add_ssh_string(BUFFER *buffer,STRING *string);
+void buffer_add_u8(BUFFER *buffer, u8 data);
+void buffer_add_u32(BUFFER *buffer, u32 data);
+void buffer_add_u64(BUFFER *buffer,u64 data);
+void buffer_add_data(BUFFER *buffer, void *data, int len);
+void buffer_add_data_begin(BUFFER *buffer,void *data,int len);
+void buffer_add_buffer(BUFFER *buffer, BUFFER *source);
+void buffer_reinit(BUFFER *buffer);
+
+/* buffer_get_rest returns a pointer to the current position into the buffer */
+void *buffer_get_rest(BUFFER *buffer);
+/* buffer_get_rest_len returns the number of bytes which can be read */
+int buffer_get_rest_len(BUFFER *buffer);
+
+/* buffer_read_*() returns the number of bytes read, except for ssh strings */
+int buffer_get_u8(BUFFER *buffer,u8 *data);
+int buffer_get_u32(BUFFER *buffer,u32 *data);
+int buffer_get_u64(BUFFER *buffer, u64 *data);
+
+int buffer_get_data(BUFFER *buffer,void *data,int requestedlen);
+/* buffer_get_ssh_string() is an exception. if the String read is too large or invalid, it will answer NULL. */
+STRING *buffer_get_ssh_string(BUFFER *buffer);
+/* buffer_pass_bytes acts as if len bytes have been read (used for padding) */
+int buffer_pass_bytes_end(BUFFER *buffer,int len);
+int buffer_pass_bytes(BUFFER *buffer, int len);
+
+/* in base64.c */
+BUFFER *base64_to_bin(char *source);
+char *bin_to_base64(unsigned char *source, int len);
+
+/* gzip.c */
+int compress_buffer(SSH_SESSION *session,BUFFER *buf);
+int decompress_buffer(SSH_SESSION *session,BUFFER *buf);
+
+/* wrapper.c */
+int crypt_set_algorithms(SSH_SESSION *);
+CRYPTO *crypto_new();
+void crypto_free(CRYPTO *crypto);
+bignum bignum_new();
+
+/* in misc.c */
+/* gets the user home dir. */
+char *ssh_get_user_home_dir();
+int ssh_file_readaccess_ok(char *file);
+
+/* macro for byte ordering */
+u64 ntohll(u64);
+#define htonll(x) ntohll(x)
+
+
+#ifdef __cplusplus
+} ;
+#endif
+
+#endif /* _LIBSSH_PRIV_H */
diff --git a/kftpgrabber/src/misc/libs/ssh/sftp.c b/kftpgrabber/src/misc/libs/ssh/sftp.c
new file mode 100644
index 0000000..9895456
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/sftp.c
@@ -0,0 +1,1289 @@
+/* scp.c contains the needed function to work with file transfer protocol over ssh*/
+/* don't look further if you believe this is just FTP over some tunnel. It IS different */
+/* This file contains code written by Nick Zitzmann */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include "priv.h"
+#include "ssh2.h"
+#include "sftp.h"
+#ifndef NO_SFTP
+/* here how it works : sftp commands are channeled by the ssh sftp subsystem. */
+/* every packet are sent/read using a SFTP_PACKET type structure. */
+/* into these packets, most of the server answers are messages having an ID and */
+/* having a message specific part. it is described by SFTP_MESSAGE */
+/* when reading a message, the sftp system puts it into the queue, so the process having asked for it */
+/* can fetch it, while continuing to read for other messages (it is inspecified in which order messages may */
+/* be sent back to the client */
+
+
+/* functions */
+static void sftp_packet_free(SFTP_PACKET *packet);
+void sftp_enqueue(SFTP_SESSION *session, SFTP_MESSAGE *msg);
+static void sftp_message_free(SFTP_MESSAGE *msg);
+
+SFTP_SESSION *sftp_new(SSH_SESSION *session){
+ SFTP_SESSION *sftp=malloc(sizeof(SFTP_SESSION));
+ memset(sftp,0,sizeof(SFTP_SESSION));
+ sftp->session=session;
+ sftp->channel=open_session_channel(session,131000,32000);
+ if(!sftp->channel){
+ free(sftp);
+ return NULL;
+ }
+ if(channel_request_sftp(sftp->channel)){
+ sftp_free(sftp);
+ return NULL;
+ }
+ return sftp;
+}
+
+void sftp_free(SFTP_SESSION *sftp){
+ struct request_queue *ptr;
+ channel_send_eof(sftp->channel);
+ /* let libssh handle the channel closing from the server reply */
+ ptr=sftp->queue;
+ while(ptr){
+ struct request_queue *old;
+ sftp_message_free(ptr->message);
+ old=ptr->next;
+ free(ptr);
+ ptr=old;
+ }
+ memset(sftp,0,sizeof(*sftp));
+ free(sftp);
+}
+
+int sftp_packet_write(SFTP_SESSION *sftp,u8 type, BUFFER *payload){
+ u32 size;
+ buffer_add_data_begin(payload,&type,sizeof(u8));
+ size=htonl(buffer_get_len(payload));
+ buffer_add_data_begin(payload,&size,sizeof(u32));
+ size=channel_write(sftp->channel,buffer_get(payload),buffer_get_len(payload));
+ if(size != buffer_get_len(payload)){
+ ssh_say(1,"had to write %d bytes, wrote only %d\n",buffer_get_len(payload),size);
+ }
+ return size;
+}
+
+SFTP_PACKET *sftp_packet_read(SFTP_SESSION *sftp){
+ SFTP_PACKET *packet=malloc(sizeof(SFTP_PACKET));
+ u32 size;
+ packet->sftp=sftp;
+ packet->payload=buffer_new();
+ if(channel_read(sftp->channel,packet->payload,4,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ buffer_get_u32(packet->payload,&size);
+ size=ntohl(size);
+ if(channel_read(sftp->channel,packet->payload,1,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ buffer_get_u8(packet->payload,&packet->type);
+ if(size>1)
+ if(channel_read(sftp->channel,packet->payload,size-1,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ return packet;
+}
+
+static SFTP_MESSAGE *sftp_message_new(){
+ SFTP_MESSAGE *msg=malloc(sizeof(SFTP_MESSAGE));
+ memset(msg,0,sizeof(*msg));
+ msg->payload=buffer_new();
+ return msg;
+}
+
+static void sftp_message_free(SFTP_MESSAGE *msg){
+ if(msg->payload)
+ buffer_free(msg->payload);
+ free(msg);
+}
+
+SFTP_MESSAGE *sftp_get_message(SFTP_PACKET *packet){
+ SFTP_MESSAGE *msg=sftp_message_new();
+ msg->sftp=packet->sftp;
+ msg->packet_type=packet->type;
+ if((packet->type!=SSH_FXP_STATUS)&&(packet->type!=SSH_FXP_HANDLE) &&
+ (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS)
+ && (packet->type != SSH_FXP_NAME)){
+ ssh_set_error(packet->sftp->session,SSH_INVALID_DATA,"get_message : unknown packet type %d\n",packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ if(buffer_get_u32(packet->payload,&msg->id)!=sizeof(u32)){
+ ssh_set_error(packet->sftp->session,SSH_INVALID_DATA,"invalid packet %d : no ID",packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ ssh_say(2,"packet with id %d type %d\n",msg->id,msg->packet_type);
+ buffer_add_data(msg->payload,buffer_get_rest(packet->payload),buffer_get_rest_len(packet->payload));
+ return msg;
+}
+
+int sftp_read_and_dispatch(SFTP_SESSION *session){
+ SFTP_PACKET *packet;
+ SFTP_MESSAGE *message=NULL;
+ packet=sftp_packet_read(session);
+ if(!packet)
+ return -1; /* something nasty happened reading the packet */
+ message=sftp_get_message(packet);
+ sftp_packet_free(packet);
+ if(!message)
+ return -1;
+ sftp_enqueue(session,message);
+ return 0;
+}
+
+static void sftp_packet_free(SFTP_PACKET *packet){
+ if(packet->payload)
+ buffer_free(packet->payload);
+ free(packet);
+}
+
+int sftp_init(SFTP_SESSION *sftp){
+ SFTP_PACKET *packet;
+ BUFFER *buffer=buffer_new();
+ STRING *ext_name_s=NULL, *ext_data_s=NULL;
+ char *ext_name,*ext_data;
+ u32 version=htonl(LIBSFTP_VERSION);
+ buffer_add_u32(buffer,version);
+ sftp_packet_write(sftp,SSH_FXP_INIT,buffer);
+ buffer_free(buffer);
+ packet=sftp_packet_read(sftp);
+ if(!packet)
+ return -1;
+ if(packet->type != SSH_FXP_VERSION){
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received a %d messages instead of SSH_FXP_VERSION",packet->type);
+ sftp_packet_free(packet);
+ return -1;
+ }
+ buffer_get_u32(packet->payload,&version);
+ version=ntohl(version);
+ if(!(ext_name_s=buffer_get_ssh_string(packet->payload))||!(ext_data_s=buffer_get_ssh_string(packet->payload)))
+ ssh_say(2,"sftp server version %d\n",version);
+ else{
+ ext_name=string_to_char(ext_name_s);
+ ext_data=string_to_char(ext_data_s);
+ ssh_say(2,"sftp server version %d (%s,%s)\n",version,ext_name,ext_data);
+ free(ext_name);
+ free(ext_data);
+ }
+ if(ext_name_s)
+ free(ext_name_s);
+ if(ext_data_s)
+ free(ext_data_s);
+ sftp_packet_free(packet);
+ sftp->server_version=version;
+ return 0;
+}
+
+REQUEST_QUEUE *request_queue_new(SFTP_MESSAGE *msg){
+ REQUEST_QUEUE *queue=malloc(sizeof(REQUEST_QUEUE));
+ memset(queue,0,sizeof(REQUEST_QUEUE));
+ queue->message=msg;
+ return queue;
+}
+
+void request_queue_free(REQUEST_QUEUE *queue){
+ memset(queue,0,sizeof(*queue));
+ free(queue);
+}
+
+void sftp_enqueue(SFTP_SESSION *session, SFTP_MESSAGE *msg){
+ REQUEST_QUEUE *queue=request_queue_new(msg);
+ REQUEST_QUEUE *ptr;
+ ssh_say(2,"queued msg type %d id %d\n",msg->id,msg->packet_type);
+ if(!session->queue)
+ session->queue=queue;
+ else {
+ ptr=session->queue;
+ while(ptr->next){
+ ptr=ptr->next; /* find end of linked list */
+ }
+ ptr->next=queue; /* add it on bottom */
+ }
+}
+
+/* pulls of a message from the queue based on the ID. returns null if no message has been found */
+SFTP_MESSAGE *sftp_dequeue(SFTP_SESSION *session, u32 id){
+ REQUEST_QUEUE *queue,*prev=NULL;
+ SFTP_MESSAGE *msg;
+ if(session->queue==NULL){
+ return NULL;
+ }
+ queue=session->queue;
+ while(queue){
+ if(queue->message->id==id){
+ /* remove from queue */
+ if(prev==NULL){
+ session->queue=queue->next;
+ } else {
+ prev->next=queue->next;
+ }
+ msg=queue->message;
+ request_queue_free(queue);
+ ssh_say(2,"dequeued msg id %d type %d\n",msg->id,msg->packet_type);
+ return msg;
+ }
+ prev=queue;
+ queue=queue->next;
+ }
+ return NULL;
+}
+
+/* assigns a new sftp ID for new requests and assures there is no collision between them. */
+u32 sftp_get_new_id(SFTP_SESSION *session){
+ return ++session->id_counter;
+}
+
+STATUS_MESSAGE *parse_status_msg(SFTP_MESSAGE *msg){
+ STATUS_MESSAGE *status;
+ if(msg->packet_type != SSH_FXP_STATUS){
+ ssh_set_error(msg->sftp->session, SSH_INVALID_DATA,"Not a ssh_fxp_status message passed in !");
+ return NULL;
+ }
+ status=malloc(sizeof(STATUS_MESSAGE));
+ memset(status,0,sizeof(*status));
+ status->id=msg->id;
+ if( (buffer_get_u32(msg->payload,&status->status)!= 4)
+ || !(status->error=buffer_get_ssh_string(msg->payload)) ||
+ !(status->lang=buffer_get_ssh_string(msg->payload))){
+ if(status->error)
+ free(status->error);
+ /* status->lang never get allocated if something failed */
+ free(status);
+ ssh_set_error(msg->sftp->session,SSH_INVALID_DATA,"invalid SSH_FXP_STATUS message");
+ return NULL;
+ }
+ status->status=ntohl(status->status);
+ status->errormsg=string_to_char(status->error);
+ status->langmsg=string_to_char(status->lang);
+ return status;
+}
+
+void status_msg_free(STATUS_MESSAGE *status){
+ if(status->errormsg)
+ free(status->errormsg);
+ if(status->error)
+ free(status->error);
+ if(status->langmsg)
+ free(status->langmsg);
+ if(status->lang)
+ free(status->lang);
+ free(status);
+}
+
+SFTP_FILE *parse_handle_msg(SFTP_MESSAGE *msg){
+ SFTP_FILE *file;
+ if(msg->packet_type != SSH_FXP_HANDLE){
+ ssh_set_error(msg->sftp->session,SSH_INVALID_DATA,"Not a ssh_fxp_handle message passed in !");
+ return NULL;
+ }
+ file=malloc(sizeof(SFTP_FILE));
+ memset(file,0,sizeof(*file));
+ file->sftp=msg->sftp;
+ file->handle=buffer_get_ssh_string(msg->payload);
+ file->offset=0;
+ file->eof=0;
+ if(!file->handle){
+ ssh_set_error(msg->sftp->session,SSH_INVALID_DATA,"Invalid SSH_FXP_HANDLE message");
+ free(file);
+ return NULL;
+ }
+ return file;
+}
+
+SFTP_DIR *sftp_opendir(SFTP_SESSION *sftp, char *path){
+ SFTP_DIR *dir=NULL;
+ SFTP_FILE *file;
+ STATUS_MESSAGE *status;
+ SFTP_MESSAGE *msg=NULL;
+ STRING *path_s;
+ BUFFER *payload=buffer_new();
+ u32 id=sftp_get_new_id(sftp);
+ buffer_add_u32(payload,id);
+ path_s=string_from_char(path);
+ buffer_add_ssh_string(payload,path_s);
+ free(path_s);
+ sftp_packet_write(sftp,SSH_FXP_OPENDIR,payload);
+ buffer_free(payload);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ file=parse_handle_msg(msg);
+ sftp_message_free(msg);
+ if(file){
+ dir=malloc(sizeof(SFTP_DIR));
+ memset(dir,0,sizeof(*dir));
+ dir->sftp=sftp;
+ dir->name=strdup(path);
+ dir->handle=file->handle;
+ free(file);
+ }
+ return dir;
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received message %d during opendir!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+/* parse the attributes from a payload from some messages */
+/* i coded it on baselines from the protocol version 4. */
+/* please excuse me for the inaccuracy of the code. it isn't my fault, it's sftp draft's one */
+/* this code is dead anyway ... */
+/* version 4 specific code */
+SFTP_ATTRIBUTES *sftp_parse_attr_4(SFTP_SESSION *sftp,BUFFER *buf,int expectnames){
+ u32 flags=0;
+ SFTP_ATTRIBUTES *attr=malloc(sizeof(SFTP_ATTRIBUTES));
+ STRING *owner=NULL;
+ STRING *group=NULL;
+ int ok=0;
+ memset(attr,0,sizeof(*attr));
+ /* it isn't really a loop, but i use it because it's like a try..catch.. construction in C */
+ do {
+ if(buffer_get_u32(buf,&flags)!=4)
+ break;
+ flags=ntohl(flags);
+ attr->flags=flags;
+ if(flags & SSH_FILEXFER_ATTR_SIZE){
+ if(buffer_get_u64(buf,&attr->size)!=8)
+ break;
+ attr->size=ntohll(attr->size);
+ }
+ if(flags & SSH_FILEXFER_ATTR_OWNERGROUP){
+ if(!(owner=buffer_get_ssh_string(buf)))
+ break;
+ if(!(group=buffer_get_ssh_string(buf)))
+ break;
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ if(buffer_get_u32(buf,&attr->permissions)!=4)
+ break;
+ attr->permissions=ntohl(attr->permissions);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACCESSTIME){
+ if(buffer_get_u64(buf,&attr->atime64)!=8)
+ break;
+ attr->atime64=ntohll(attr->atime64);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->atime_nseconds)!=4)
+ break;
+ attr->atime_nseconds=ntohl(attr->atime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_CREATETIME){
+ if(buffer_get_u64(buf,&attr->createtime)!=8)
+ break;
+ attr->createtime=ntohll(attr->createtime);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->createtime_nseconds)!=4)
+ break;
+ attr->createtime_nseconds=ntohl(attr->createtime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_MODIFYTIME){
+ if(buffer_get_u64(buf,&attr->mtime64)!=8)
+ break;
+ attr->mtime64=ntohll(attr->mtime64);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->mtime_nseconds)!=4)
+ break;
+ attr->mtime_nseconds=ntohl(attr->mtime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACL){
+ if(!(attr->acl=buffer_get_ssh_string(buf)))
+ break;
+ }
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED){
+ if(buffer_get_u32(buf,&attr->extended_count)!=4)
+ break;
+ attr->extended_count=ntohl(attr->extended_count);
+ while(attr->extended_count && (attr->extended_type=buffer_get_ssh_string(buf))
+ && (attr->extended_data=buffer_get_ssh_string(buf))){
+ attr->extended_count--;
+ }
+ if(attr->extended_count)
+ break;
+ }
+ ok=1;
+ } while (0);
+ if(!ok){
+ /* break issued somewhere */
+ if(owner)
+ free(owner);
+ if(group)
+ free(group);
+ if(attr->acl)
+ free(attr->acl);
+ if(attr->extended_type)
+ free(attr->extended_type);
+ if(attr->extended_data)
+ free(attr->extended_data);
+ free(attr);
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Invalid ATTR structure");
+ return NULL;
+ }
+ /* everything went smoothly */
+ if(owner){
+ attr->owner=string_to_char(owner);
+ free(owner);
+ }
+ if(group){
+ attr->group=string_to_char(group);
+ free(group);
+ }
+ return attr;
+}
+
+/* Version 3 code. it is the only one really supported (the draft for the 4 misses clarifications) */
+/* maybe a paste of the draft is better than the code */
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count */
+SFTP_ATTRIBUTES *sftp_parse_attr_3(SFTP_SESSION *sftp,BUFFER *buf,int expectname){
+ u32 flags=0;
+ STRING *name;
+ STRING *longname;
+ SFTP_ATTRIBUTES *attr=malloc(sizeof(SFTP_ATTRIBUTES));
+ int ok=0;
+ memset(attr,0,sizeof(*attr));
+ /* it isn't really a loop, but i use it because it's like a try..catch.. construction in C */
+ do {
+ if(expectname){
+ if(!(name=buffer_get_ssh_string(buf)))
+ break;
+ attr->name=string_to_char(name);
+ free(name);
+ ssh_say(2,"name : %s\n",attr->name);
+ if(!(longname=buffer_get_ssh_string(buf)))
+ break;
+ attr->longname=string_to_char(longname);
+ free(longname);
+ }
+ if(buffer_get_u32(buf,&flags)!=sizeof(u32))
+ break;
+ flags=ntohl(flags);
+ attr->flags=flags;
+ ssh_say(2,"flags : %.8lx\n",flags);
+ if(flags & SSH_FILEXFER_ATTR_SIZE){
+ if(buffer_get_u64(buf,&attr->size)!=sizeof(u64))
+ break;
+ attr->size=ntohll(attr->size);
+ ssh_say(2,"size : %lld\n",attr->size);
+ }
+ if(flags & SSH_FILEXFER_ATTR_UIDGID){
+ if(buffer_get_u32(buf,&attr->uid)!=sizeof(u32))
+ break;
+ if(buffer_get_u32(buf,&attr->gid)!=sizeof(u32))
+ break;
+ attr->uid=ntohl(attr->uid);
+ attr->gid=ntohl(attr->gid);
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ if(buffer_get_u32(buf,&attr->permissions)!=sizeof(u32))
+ break;
+ attr->permissions=ntohl(attr->permissions);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACMODTIME){
+ if(buffer_get_u32(buf,&attr->atime)!=sizeof(u32))
+ break;
+ attr->atime=ntohl(attr->atime);
+ if(buffer_get_u32(buf,&attr->mtime)!=sizeof(u32))
+ break;
+ attr->mtime=ntohl(attr->mtime);
+ }
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED){
+ if(buffer_get_u32(buf,&attr->extended_count)!=sizeof(u32))
+ break;
+ attr->extended_count=ntohl(attr->extended_count);
+ while(attr->extended_count && (attr->extended_type=buffer_get_ssh_string(buf))
+ && (attr->extended_data=buffer_get_ssh_string(buf))){
+ attr->extended_count--;
+ }
+ if(attr->extended_count)
+ break;
+ }
+ ok=1;
+ } while (0);
+ if(!ok){
+ /* break issued somewhere */
+ if(attr->name)
+ free(attr->name);
+ if(attr->extended_type)
+ free(attr->extended_type);
+ if(attr->extended_data)
+ free(attr->extended_data);
+ free(attr);
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Invalid ATTR structure");
+ return NULL;
+ }
+ /* everything went smoothly */
+ return attr;
+}
+
+void buffer_add_attributes(BUFFER *buffer, SFTP_ATTRIBUTES *attr){
+ u32 flags=(attr?attr->flags:0);
+ flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+ buffer_add_u32(buffer,htonl(flags));
+ if(attr){
+ if (flags & SSH_FILEXFER_ATTR_SIZE)
+ {
+ buffer_add_u64(buffer, htonll(attr->size));
+ }
+ if(flags & SSH_FILEXFER_ATTR_UIDGID){
+ buffer_add_u32(buffer,htonl(attr->uid));
+ buffer_add_u32(buffer,htonl(attr->gid));
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ buffer_add_u32(buffer,htonl(attr->permissions));
+ }
+ if (flags & SSH_FILEXFER_ATTR_ACMODTIME)
+ {
+ buffer_add_u32(buffer, htonl(attr->atime));
+ buffer_add_u32(buffer, htonl(attr->mtime));
+ }
+ }
+}
+
+
+SFTP_ATTRIBUTES *sftp_parse_attr(SFTP_SESSION *session, BUFFER *buf,int expectname){
+ switch(session->server_version){
+ case 4:
+ return sftp_parse_attr_4(session,buf,expectname);
+ case 3:
+ return sftp_parse_attr_3(session,buf,expectname);
+ default:
+ ssh_set_error(session->session,SSH_INVALID_DATA,"Version %d unsupported by client",session->server_version);
+ return NULL;
+ }
+ return NULL;
+}
+
+int sftp_server_version(SFTP_SESSION *sftp){
+ return sftp->server_version;
+}
+
+SFTP_ATTRIBUTES *sftp_readdir(SFTP_SESSION *sftp, SFTP_DIR *dir){
+ BUFFER *payload;
+ u32 id;
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ SFTP_ATTRIBUTES *attr;
+ if(!dir->buffer){
+ payload=buffer_new();
+ id=sftp_get_new_id(sftp);
+ buffer_add_u32(payload,id);
+ buffer_add_ssh_string(payload,dir->handle);
+ sftp_packet_write(sftp,SSH_FXP_READDIR,payload);
+ buffer_free(payload);
+ ssh_say(2,"sent a ssh_fxp_readdir with id %d\n",id);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ if(status->status==SSH_FX_EOF){
+ dir->eof=1;
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Unknown error status : %d",status->status);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_NAME:
+ buffer_get_u32(msg->payload,&dir->count);
+ dir->count=ntohl(dir->count);
+ dir->buffer=msg->payload;
+ msg->payload=NULL;
+ sftp_message_free(msg);
+ break;
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"unsupported message back %d",msg->packet_type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ }
+ /* now dir->buffer contains a buffer and dir->count != 0 */
+ if(dir->count==0){
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Count of files sent by the server is zero, which is invalid, or libsftp bug");
+ return NULL;
+ }
+ ssh_say(2,"Count is %d\n",dir->count);
+ attr=sftp_parse_attr(sftp,dir->buffer,1);
+ dir->count--;
+ if(dir->count==0){
+ buffer_free(dir->buffer);
+ dir->buffer=NULL;
+ }
+ return attr;
+}
+
+int sftp_dir_eof(SFTP_DIR *dir){
+ return (dir->eof);
+}
+
+void sftp_attributes_free(SFTP_ATTRIBUTES *file){
+ if(file->name)
+ free(file->name);
+ if(file->longname)
+ free(file->longname);
+ if(file->acl)
+ free(file->acl);
+ if(file->extended_data)
+ free(file->extended_data);
+ if(file->extended_type)
+ free(file->extended_type);
+ if(file->group)
+ free(file->group);
+ if(file->owner)
+ free(file->owner);
+ free(file);
+}
+
+static int sftp_handle_close(SFTP_SESSION *sftp, STRING *handle){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ int id=sftp_get_new_id(sftp);
+ int err=0;
+ BUFFER *buffer=buffer_new();
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,handle);
+ sftp_packet_write(sftp,SSH_FXP_CLOSE,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_OK){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ status_msg_free(status);
+ return err;
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received message %d during sftp_handle_close!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+int sftp_file_close(SFTP_FILE *file){
+ int err=0;
+ if(file->name)
+ free(file->name);
+ if(file->handle){
+ err=sftp_handle_close(file->sftp,file->handle);
+ free(file->handle);
+ }
+ free(file);
+ return err;
+}
+
+int sftp_dir_close(SFTP_DIR *dir){
+ int err=0;
+ if(dir->name)
+ free(dir->name);
+ if(dir->handle){
+ err=sftp_handle_close(dir->sftp,dir->handle);
+ free(dir->handle);
+ }
+ if(dir->buffer)
+ buffer_free(dir->buffer);
+ free(dir);
+ return err;
+}
+
+SFTP_FILE *sftp_open(SFTP_SESSION *sftp, char *file, int access, SFTP_ATTRIBUTES *attr){
+ SFTP_FILE *handle;
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ u32 flags=0;
+ u32 id=sftp_get_new_id(sftp);
+ BUFFER *buffer=buffer_new();
+ STRING *filename;
+ if(access & O_RDONLY)
+ flags|=SSH_FXF_READ;
+ if(access & O_WRONLY)
+ flags |= SSH_FXF_WRITE;
+ if(access & O_RDWR)
+ flags|=(SSH_FXF_WRITE | SSH_FXF_READ);
+ if(access & O_CREAT)
+ flags |=SSH_FXF_CREAT;
+ if(access & O_TRUNC)
+ flags |=SSH_FXF_TRUNC;
+ if(access & O_EXCL)
+ flags |= SSH_FXF_EXCL;
+ buffer_add_u32(buffer,id);
+ filename=string_from_char(file);
+ buffer_add_ssh_string(buffer,filename);
+ free(filename);
+ buffer_add_u32(buffer,htonl(flags));
+ buffer_add_attributes(buffer,attr);
+ sftp_packet_write(sftp,SSH_FXP_OPEN,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ handle=parse_handle_msg(msg);
+ sftp_message_free(msg);
+ return handle;
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received message %d during open!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+void sftp_file_set_nonblocking(SFTP_FILE *handle){
+ handle->nonblocking=1;
+}
+void sftp_file_set_blocking(SFTP_FILE *handle){
+ handle->nonblocking=0;
+}
+
+int sftp_read(SFTP_FILE *handle, void *data, int len){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ SFTP_SESSION *sftp=handle->sftp;
+ STRING *datastring;
+ int id;
+ int err=0;
+ BUFFER *buffer;
+ if(handle->eof)
+ return 0;
+ buffer=buffer_new();
+ id=sftp_get_new_id(handle->sftp);
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,handle->handle);
+ buffer_add_u64(buffer,htonll(handle->offset));
+ buffer_add_u32(buffer,htonl(len));
+ sftp_packet_write(handle->sftp,SSH_FXP_READ,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if (handle->nonblocking){
+ if(channel_poll(handle->sftp->channel,0)==0){
+ /* we cannot block */
+ return 0;
+ }
+ }
+ if(sftp_read_and_dispatch(handle->sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(handle->sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_EOF){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ else
+ handle->eof=1;
+ status_msg_free(status);
+ return err?err:0;
+ case SSH_FXP_DATA:
+ datastring=buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if(!datastring){
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received invalid DATA packet from sftp server");
+ return -1;
+ }
+ if(string_len(datastring)>len){
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received a too big DATA packet from sftp server : %d and asked for %d",
+ string_len(datastring),len);
+ free(datastring);
+ return -1;
+ }
+ len=string_len(datastring);
+ handle->offset+=len;
+ memcpy(data,datastring->string,len);
+ free(datastring);
+ return len;
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received message %d during read!",msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+ return -1; /* not reached */
+}
+
+int sftp_write(SFTP_FILE *file, void *data, int len){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ STRING *datastring;
+ SFTP_SESSION *sftp=file->sftp;
+ int id;
+ int err=0;
+ BUFFER *buffer;
+ buffer=buffer_new();
+ id=sftp_get_new_id(file->sftp);
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,file->handle);
+ buffer_add_u64(buffer,htonll(file->offset));
+ datastring=string_new(len);
+ string_fill(datastring,data,len);
+ buffer_add_ssh_string(buffer,datastring);
+ free(datastring);
+ if(sftp_packet_write(file->sftp,SSH_FXP_WRITE,buffer) != buffer_get_len(buffer)){
+ ssh_say(1,"sftp_packet_write did not write as much data as expected\n");
+ }
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(file->sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(file->sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_OK){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ file->offset+=len;
+ status_msg_free(status);
+ return (err?err:len);
+ default:
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received message %d during write!",msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+ return -1; /* not reached */
+}
+
+void sftp_seek(SFTP_FILE *file, int new_offset){
+ file->offset=new_offset;
+}
+
+unsigned long sftp_tell(SFTP_FILE *file){
+ return file->offset;
+}
+
+void sftp_rewind(SFTP_FILE *file){
+ file->offset=0;
+}
+
+/* code written by Nick */
+int sftp_rm(SFTP_SESSION *sftp, char *file) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *filename = string_from_char(file);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, filename);
+ free(filename);
+ sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp)) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to remove file", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* code written by Nick */
+int sftp_rmdir(SFTP_SESSION *sftp, char *directory) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *filename = string_from_char(directory);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, filename);
+ free(filename);
+ sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ {
+ return -1;
+ }
+ else if (status->status != SSH_FX_OK) /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ {
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ }
+ else
+ {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to remove directory", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* Code written by Nick */
+int sftp_mkdir(SFTP_SESSION *sftp, char *directory, SFTP_ATTRIBUTES *attr) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *path = string_from_char(directory);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, path);
+ free(path);
+ buffer_add_attributes(buffer, attr);
+ sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else
+ if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to make directory", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* code written by nick */
+int sftp_rename(SFTP_SESSION *sftp, char *original, char *newname) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *oldpath = string_from_char(original);
+ STRING *newpath = string_from_char(newname);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, oldpath);
+ free(oldpath);
+ buffer_add_ssh_string(buffer, newpath);
+ free(newpath);
+ sftp_packet_write(sftp, SSH_FXP_RENAME, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to rename", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* Code written by Nick */
+int sftp_setstat(SFTP_SESSION *sftp, char *file, SFTP_ATTRIBUTES *attr) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *path = string_from_char(file);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, path);
+ free(path);
+ buffer_add_attributes(buffer, attr);
+ sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* another code written by Nick */
+char *sftp_canonicalize_path(SFTP_SESSION *sftp, char *path)
+{
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *pathstr = string_from_char(path);
+ STRING *name = NULL;
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+ char *cname;
+ u32 ignored;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, pathstr);
+ free(pathstr);
+ sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer);
+ buffer_free(buffer);
+ while (!msg)
+ {
+ if (sftp_read_and_dispatch(sftp))
+ return NULL;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_NAME) /* good response */
+ {
+ buffer_get_u32(msg->payload, &ignored); /* we don't care about "count" */
+ name = buffer_get_ssh_string(msg->payload); /* we only care about the file name string */
+ cname = string_to_char(name);
+ free(name);
+ return cname;
+ }
+ else if (msg->packet_type == SSH_FXP_STATUS) /* bad response (error) */
+ {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ }
+ else /* this shouldn't happen */
+ {
+ ssh_set_error(sftp->session,SSH_INVALID_DATA, "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+SFTP_ATTRIBUTES *sftp_xstat(SFTP_SESSION *sftp, char *path,int param){
+ u32 id=sftp_get_new_id(sftp);
+ BUFFER *buffer=buffer_new();
+ STRING *pathstr= string_from_char(path);
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status=NULL;
+ SFTP_ATTRIBUTES *pattr=NULL;
+
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,pathstr);
+ free(pathstr);
+ sftp_packet_write(sftp,param,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ if(msg->packet_type==SSH_FXP_ATTRS){
+ pattr=sftp_parse_attr(sftp,msg->payload,0);
+ return pattr;
+ }
+ if(msg->packet_type== SSH_FXP_STATUS){
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server: %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(sftp->session,SSH_INVALID_DATA,"Received mesg %d during stat(),mesg->packet_type");
+ sftp_message_free(msg);
+ return NULL;
+}
+
+SFTP_ATTRIBUTES *sftp_stat(SFTP_SESSION *session, char *path){
+ return sftp_xstat(session,path,SSH_FXP_STAT);
+}
+SFTP_ATTRIBUTES *sftp_lstat(SFTP_SESSION *session, char *path){
+ return sftp_xstat(session,path,SSH_FXP_LSTAT);
+}
+
+SFTP_ATTRIBUTES *sftp_fstat(SFTP_FILE *file) {
+ u32 id=sftp_get_new_id(file->sftp);
+ BUFFER *buffer=buffer_new();
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status=NULL;
+ SFTP_ATTRIBUTES *pattr=NULL;
+
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,file->handle);
+ sftp_packet_write(file->sftp,SSH_FXP_FSTAT,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(file->sftp))
+ return NULL;
+ msg=sftp_dequeue(file->sftp,id);
+ }
+ if(msg->packet_type==SSH_FXP_ATTRS){
+ pattr=sftp_parse_attr(file->sftp,msg->payload,0);
+ return pattr;
+ }
+ if(msg->packet_type== SSH_FXP_STATUS){
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(file->sftp->session,SSH_REQUEST_DENIED,"sftp server: %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(file->sftp->session,SSH_INVALID_DATA,"Received mesg %d during fstat(),mesg->packet_type");
+ sftp_message_free(msg);
+ return NULL;
+}
+
+
+#endif /* NO_SFTP */
diff --git a/kftpgrabber/src/misc/libs/ssh/sftp.h b/kftpgrabber/src/misc/libs/ssh/sftp.h
new file mode 100644
index 0000000..10334ab
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/sftp.h
@@ -0,0 +1,225 @@
+/* sftp headers */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#ifndef SFTP_H
+#define SFTP_H
+#include "libssh.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sftp_session_struct {
+ SSH_SESSION *session;
+ CHANNEL *channel;
+ int server_version;
+ struct request_queue *queue;
+ u32 id_counter;
+} SFTP_SESSION ;
+
+typedef struct {
+ SFTP_SESSION *sftp;
+ u8 type;
+ BUFFER *payload;
+} SFTP_PACKET;
+
+/* file handler */
+typedef struct sftp_file{
+ SFTP_SESSION *sftp;
+ char *name;
+ u64 offset;
+ STRING *handle;
+ int eof;
+ int nonblocking;
+} SFTP_FILE ;
+
+typedef struct sftp_dir {
+ SFTP_SESSION *sftp;
+ char *name;
+ STRING *handle; /* handle to directory */
+ BUFFER *buffer; /* contains raw attributes from server which haven't been parsed */
+ u32 count; /* counts the number of following attributes structures into buffer */
+ int eof; /* end of directory listing */
+} SFTP_DIR;
+
+typedef struct {
+ SFTP_SESSION *sftp;
+ u8 packet_type;
+ BUFFER *payload;
+ u32 id;
+} SFTP_MESSAGE;
+
+typedef struct request_queue{
+ struct request_queue *next;
+ SFTP_MESSAGE *message;
+} REQUEST_QUEUE;
+
+/* SSH_FXP_MESSAGE described into .7 page 26 */
+typedef struct {
+ u32 id;
+ u32 status;
+ STRING *error;
+ STRING *lang;
+ char *errormsg;
+ char *langmsg;
+} STATUS_MESSAGE;
+
+/* don't worry much of these aren't really used */
+typedef struct {
+ char *name;
+ char *longname; /* some weird stuff */
+ u32 flags;
+ u8 type;
+ u64 size;
+ u32 uid;
+ u32 gid;
+ char *owner;
+ char *group;
+ u32 permissions;
+ u64 atime64;
+ u32 atime;
+ u32 atime_nseconds;
+ u64 createtime;
+ u32 createtime_nseconds;
+ u64 mtime64;
+ u32 mtime;
+ u32 mtime_nseconds;
+ STRING *acl;
+ u32 extended_count;
+ STRING *extended_type;
+ STRING *extended_data;
+} SFTP_ATTRIBUTES;
+
+#define LIBSFTP_VERSION 3
+
+SFTP_SESSION *sftp_new(SSH_SESSION *session);
+void sftp_free(SFTP_SESSION *sftp);
+int sftp_init(SFTP_SESSION *sftp);
+SFTP_DIR *sftp_opendir(SFTP_SESSION *session, char *path);
+/* reads one file and attribute from opened directory. fails at end */
+SFTP_ATTRIBUTES *sftp_readdir(SFTP_SESSION *session, SFTP_DIR *dir);
+/* returns 1 if the directory was EOF */
+int sftp_dir_eof(SFTP_DIR *dir);
+SFTP_ATTRIBUTES *sftp_stat(SFTP_SESSION *session, char *path);
+SFTP_ATTRIBUTES *sftp_lstat(SFTP_SESSION *session, char *path);
+/* sftp_lstat stats a file but doesn't follow symlinks */
+SFTP_ATTRIBUTES *sftp_fstat(SFTP_FILE *file);
+void sftp_attributes_free(SFTP_ATTRIBUTES *file);
+int sftp_dir_close(SFTP_DIR *dir);
+int sftp_file_close(SFTP_FILE *file);
+/* access are the sames than the ones from ansi fopen() */
+SFTP_FILE *sftp_open(SFTP_SESSION *session, char *file, int access, SFTP_ATTRIBUTES *attr);
+int sftp_read(SFTP_FILE *file, void *dest, int len);
+int sftp_write(SFTP_FILE *file, void *source, int len);
+void sftp_seek(SFTP_FILE *file, int new_offset);
+unsigned long sftp_tell(SFTP_FILE *file);
+void sftp_rewind(SFTP_FILE *file);
+int sftp_rm(SFTP_SESSION *sftp, char *file);
+int sftp_rmdir(SFTP_SESSION *sftp, char *directory);
+int sftp_mkdir(SFTP_SESSION *sftp, char *directory, SFTP_ATTRIBUTES *attr);
+int sftp_rename(SFTP_SESSION *sftp, char *original, char *newname);
+int sftp_setstat(SFTP_SESSION *sftp, char *file, SFTP_ATTRIBUTES *attr);
+char *sftp_canonicalize_path(SFTP_SESSION *sftp, char *path);
+
+/* SFTP commands and constants */
+#define SSH_FXP_INIT 1
+#define SSH_FXP_VERSION 2
+#define SSH_FXP_OPEN 3
+#define SSH_FXP_CLOSE 4
+#define SSH_FXP_READ 5
+#define SSH_FXP_WRITE 6
+#define SSH_FXP_LSTAT 7
+#define SSH_FXP_FSTAT 8
+#define SSH_FXP_SETSTAT 9
+#define SSH_FXP_FSETSTAT 10
+#define SSH_FXP_OPENDIR 11
+#define SSH_FXP_READDIR 12
+#define SSH_FXP_REMOVE 13
+#define SSH_FXP_MKDIR 14
+#define SSH_FXP_RMDIR 15
+#define SSH_FXP_REALPATH 16
+#define SSH_FXP_STAT 17
+#define SSH_FXP_RENAME 18
+#define SSH_FXP_READLINK 19
+#define SSH_FXP_SYMLINK 20
+
+#define SSH_FXP_STATUS 101
+#define SSH_FXP_HANDLE 102
+#define SSH_FXP_DATA 103
+#define SSH_FXP_NAME 104
+#define SSH_FXP_ATTRS 105
+
+#define SSH_FXP_EXTENDED 200
+#define SSH_FXP_EXTENDED_REPLY 201
+
+/* attributes */
+/* sftp draft is completely braindead : version 3 and 4 have different flags for same constants */
+/* and even worst, version 4 has same flag for 2 different constants */
+/* follow up : i won't develop any sftp4 compliant library before having a clarification */
+
+#define SSH_FILEXFER_ATTR_SIZE 0x00000001
+#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004
+#define SSH_FILEXFER_ATTR_ACCESSTIME 0x00000008
+#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008
+#define SSH_FILEXFER_ATTR_CREATETIME 0x00000010
+#define SSH_FILEXFER_ATTR_MODIFYTIME 0x00000020
+#define SSH_FILEXFER_ATTR_ACL 0x00000040
+#define SSH_FILEXFER_ATTR_OWNERGROUP 0x00000080
+#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100
+#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000
+#define SSH_FILEXFER_ATTR_UIDGID 0x00000002
+
+/* types */
+#define SSH_FILEXFER_TYPE_REGULAR 1
+#define SSH_FILEXFER_TYPE_DIRECTORY 2
+#define SSH_FILEXFER_TYPE_SYMLINK 3
+#define SSH_FILEXFER_TYPE_SPECIAL 4
+#define SSH_FILEXFER_TYPE_UNKNOWN 5
+
+/* server responses */
+#define SSH_FX_OK 0
+#define SSH_FX_EOF 1
+#define SSH_FX_NO_SUCH_FILE 2
+#define SSH_FX_PERMISSION_DENIED 3
+#define SSH_FX_FAILURE 4
+#define SSH_FX_BAD_MESSAGE 5
+#define SSH_FX_NO_CONNECTION 6
+#define SSH_FX_CONNECTION_LOST 7
+#define SSH_FX_OP_UNSUPPORTED 8
+#define SSH_FX_INVALID_HANDLE 9
+#define SSH_FX_NO_SUCH_PATH 10
+#define SSH_FX_FILE_ALREADY_EXISTS 11
+#define SSH_FX_WRITE_PROTECT 12
+#define SSH_FX_NO_MEDIA 13
+
+/* file flags */
+#define SSH_FXF_READ 0x01
+#define SSH_FXF_WRITE 0x02
+#define SSH_FXF_APPEND 0x04
+#define SSH_FXF_CREAT 0x08
+#define SSH_FXF_TRUNC 0x10
+#define SSH_FXF_EXCL 0x20
+#define SSH_FXF_TEXT 0x40
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SFTP_H */
diff --git a/kftpgrabber/src/misc/libs/ssh/ssh2.h b/kftpgrabber/src/misc/libs/ssh/ssh2.h
new file mode 100644
index 0000000..e6dc04f
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/ssh2.h
@@ -0,0 +1,69 @@
+#ifndef __SSH2_H
+#define __SSH2_H
+
+#define SSH2_MSG_DISCONNECT 1
+#define SSH2_MSG_IGNORE 2
+#define SSH2_MSG_UNIMPLEMENTED 3
+#define SSH2_MSG_DEBUG 4
+#define SSH2_MSG_SERVICE_REQUEST 5
+#define SSH2_MSG_SERVICE_ACCEPT 6
+
+#define SSH2_MSG_KEXINIT 20
+#define SSH2_MSG_NEWKEYS 21
+
+#define SSH2_MSG_KEXDH_INIT 30
+#define SSH2_MSG_KEXDH_REPLY 31
+
+#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
+#define SSH2_MSG_KEX_DH_GEX_GROUP 31
+#define SSH2_MSG_KEX_DH_GEX_INIT 32
+#define SSH2_MSG_KEX_DH_GEX_REPLY 33
+#define SSH2_MSG_KEX_DH_GEX_REQUEST 34
+#define SSH2_MSG_USERAUTH_REQUEST 50
+#define SSH2_MSG_USERAUTH_FAILURE 51
+#define SSH2_MSG_USERAUTH_SUCCESS 52
+#define SSH2_MSG_USERAUTH_BANNER 53
+#define SSH2_MSG_USERAUTH_PK_OK 60
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+#define SSH2_MSG_USERAUTH_INFO_REQUEST 60
+#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61
+#define SSH2_MSG_GLOBAL_REQUEST 80
+#define SSH2_MSG_REQUEST_SUCCESS 81
+#define SSH2_MSG_REQUEST_FAILURE 82
+#define SSH2_MSG_CHANNEL_OPEN 90
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
+#define SSH2_MSG_CHANNEL_DATA 94
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95
+#define SSH2_MSG_CHANNEL_EOF 96
+#define SSH2_MSG_CHANNEL_CLOSE 97
+#define SSH2_MSG_CHANNEL_REQUEST 98
+#define SSH2_MSG_CHANNEL_SUCCESS 99
+#define SSH2_MSG_CHANNEL_FAILURE 100
+
+#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
+#define SSH2_DISCONNECT_PROTOCOL_ERROR 2
+#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3
+#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4
+#define SSH2_DISCONNECT_RESERVED 4
+#define SSH2_DISCONNECT_MAC_ERROR 5
+#define SSH2_DISCONNECT_COMPRESSION_ERROR 6
+#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7
+#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
+#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
+#define SSH2_DISCONNECT_CONNECTION_LOST 10
+#define SSH2_DISCONNECT_BY_APPLICATION 11
+#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12
+#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13
+#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
+#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15
+
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1
+#define SSH2_OPEN_CONNECT_FAILED 2
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3
+#define SSH2_OPEN_RESOURCE_SHORTAGE 4
+
+#define SSH2_EXTENDED_DATA_STDERR 1
+
+#endif
diff --git a/kftpgrabber/src/misc/libs/ssh/string.c b/kftpgrabber/src/misc/libs/ssh/string.c
new file mode 100644
index 0000000..1126e7a
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/string.c
@@ -0,0 +1,65 @@
+/*string.c */
+/* string manipulations... */
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include <stdlib.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include "priv.h"
+
+STRING *string_new(u32 size){
+ STRING *str=malloc(size + 4);
+ str->size=htonl(size);
+ return str;
+}
+
+void string_fill(STRING *str,void *data,int len){
+ memcpy(str->string,data,len);
+}
+
+STRING *string_from_char(char *what){
+ STRING *ptr;
+ int len=strlen(what);
+ ptr=malloc(4 + len);
+ ptr->size=htonl(len);
+ memcpy(ptr->string,what,len);
+ return ptr;
+}
+
+int string_len(STRING *str){
+ return ntohl(str->size);
+}
+
+char *string_to_char(STRING *str){
+ int len=ntohl(str->size)+1;
+ char *string=malloc(len);
+ memcpy(string,str->string,len-1);
+ string[len-1]=0;
+ return string;
+}
+
+STRING *string_copy(STRING *str){
+ STRING *ret=malloc(ntohl(str->size)+4);
+ ret->size=str->size;
+ memcpy(ret->string,str->string,ntohl(str->size));
+ return ret;
+}
diff --git a/kftpgrabber/src/misc/libs/ssh/wrapper.c b/kftpgrabber/src/misc/libs/ssh/wrapper.c
new file mode 100644
index 0000000..b99beeb
--- /dev/null
+++ b/kftpgrabber/src/misc/libs/ssh/wrapper.c
@@ -0,0 +1,241 @@
+/* wrapper.c */
+/* wrapping functions for crypto functions. */
+/* why a wrapper ? let's say you want to port libssh from libcrypto of openssl to libfoo */
+/* you are going to spend hours to remove every references to SHA1_Update() to libfoo_sha1_update */
+/* after the work is finished, you're going to have only this file to modify */
+/* it's not needed to say that your modifications are welcome */
+
+/*
+Copyright 2003 Aris Adamantiadis
+
+This file is part of the SSH Library
+
+The SSH Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The SSH Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the SSH Library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include "priv.h"
+#include "crypto.h"
+#include <string.h>
+#ifdef OPENSSL_CRYPTO
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+#ifdef HAVE_OPENSSL_AES_H
+#define HAS_AES
+#include <openssl/aes.h>
+#endif
+#ifdef HAVE_OPENSSL_BLOWFISH_H
+#define HAS_BLOWFISH
+#include <openssl/blowfish.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER<0x009070000)
+#define OLD_CRYPTO
+#endif
+
+SHACTX *sha1_init(){
+ SHACTX *c=malloc(sizeof(SHACTX));
+ SHA1_Init(c);
+ return c;
+}
+void sha1_update(SHACTX *c, const void *data, unsigned long len){
+ SHA1_Update(c,data,len);
+}
+void sha1_final(unsigned char *md,SHACTX *c){
+ SHA1_Final(md,c);
+ free(c);
+}
+void sha1(unsigned char *digest,int len,unsigned char *hash){
+ SHA1(digest,len,hash);
+}
+
+MD5CTX *md5_init(){
+ MD5CTX *c=malloc(sizeof(MD5CTX));
+ MD5_Init(c);
+ return c;
+}
+void md5_update(MD5CTX *c, const void *data, unsigned long len){
+ MD5_Update(c,data,len);
+}
+void md5_final(unsigned char *md,MD5CTX *c){
+ MD5_Final(md,c);
+ free(c);
+}
+
+HMACCTX *hmac_init(const void *key, int len,int type){
+ HMAC_CTX *ctx;
+ ctx=malloc(sizeof(HMAC_CTX));
+#ifndef OLD_CRYPTO
+ HMAC_CTX_init(ctx); // openssl 0.9.7 requires it.
+#endif
+ switch(type){
+ case HMAC_SHA1:
+ HMAC_Init(ctx,key,len,EVP_sha1());
+ break;
+ case HMAC_MD5:
+ HMAC_Init(ctx,key,len,EVP_md5());
+ break;
+ default:
+ free(ctx);
+ ctx=NULL;
+ }
+ return ctx;
+}
+void hmac_update(HMACCTX *ctx,const void *data, unsigned long len){
+ HMAC_Update(ctx,data,len);
+}
+void hmac_final(HMACCTX *ctx,unsigned char *hashmacbuf,int *len){
+ HMAC_Final(ctx,hashmacbuf,len);
+#ifndef OLD_CRYPTO
+ HMAC_CTX_cleanup(ctx);
+#else
+ HMAC_cleanup(ctx);
+#endif
+ free(ctx);
+}
+
+static void alloc_key(struct crypto_struct *cipher){
+ cipher->key=malloc(cipher->keylen);
+}
+
+#ifdef HAS_BLOWFISH
+/* the wrapper functions for blowfish */
+static void blowfish_set_key(struct crypto_struct *cipher, void *key){
+ if(!cipher->key){
+ alloc_key(cipher);
+ BF_set_key(cipher->key,16,key);
+ }
+}
+
+static void blowfish_encrypt(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV){
+ BF_cbc_encrypt(in,out,len,cipher->key,IV,BF_ENCRYPT);
+}
+
+static void blowfish_decrypt(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV){
+ BF_cbc_encrypt(in,out,len,cipher->key,IV,BF_DECRYPT);
+}
+#endif
+#ifdef HAS_AES
+static void aes_set_encrypt_key(struct crypto_struct *cipher, void *key){
+ if(!cipher->key){
+ alloc_key(cipher);
+ AES_set_encrypt_key(key,cipher->keysize,cipher->key);
+ }
+}
+static void aes_set_decrypt_key(struct crypto_struct *cipher, void *key){
+ if(!cipher->key){
+ alloc_key(cipher);
+ AES_set_decrypt_key(key,cipher->keysize,cipher->key);
+ }
+}
+static void aes_encrypt(struct crypto_struct *cipher, void *in, void *out, unsigned long len, void *IV){
+ AES_cbc_encrypt(in,out,len,cipher->key,IV,AES_ENCRYPT);
+}
+static void aes_decrypt(struct crypto_struct *cipher, void *in, void *out, unsigned long len, void *IV){
+ AES_cbc_encrypt(in,out,len,cipher->key,IV,AES_DECRYPT);
+}
+#endif
+/* the table of supported ciphers */
+static struct crypto_struct ssh_ciphertab[]={
+#ifdef HAS_BLOWFISH
+ { "blowfish-cbc", 8 ,sizeof (BF_KEY),NULL,128,blowfish_set_key,blowfish_set_key,blowfish_encrypt, blowfish_decrypt},
+#endif
+#ifdef HAS_AES
+ { "aes128-cbc",16,sizeof(AES_KEY),NULL,128,aes_set_encrypt_key,aes_set_decrypt_key,aes_encrypt,aes_decrypt},
+ { "aes192-cbc",16,sizeof(AES_KEY),NULL,192,aes_set_encrypt_key,aes_set_decrypt_key,aes_encrypt,aes_decrypt},
+ { "aes256-cbc",16,sizeof(AES_KEY),NULL,256,aes_set_encrypt_key,aes_set_decrypt_key,aes_encrypt,aes_decrypt},
+#endif
+ { NULL,0,0,NULL,0,NULL,NULL,NULL}
+};
+#endif /* OPENSSL_CRYPTO */
+
+/* it allocates a new cipher structure based on its offset into the global table */
+struct crypto_struct *cipher_new(int offset){
+ struct crypto_struct *cipher=malloc(sizeof(struct crypto_struct));
+ /* note the memcpy will copy the pointers : so, you shouldn't free them */
+ memcpy(cipher,&ssh_ciphertab[offset],sizeof(*cipher));
+ return cipher;
+}
+
+void cipher_free(struct crypto_struct *cipher){
+ if(cipher->key){
+ /* destroy the key */
+ memset(cipher->key,0,cipher->keylen);
+ free(cipher->key);
+ }
+ free(cipher);
+}
+
+CRYPTO *crypto_new(){
+ CRYPTO *crypto=malloc(sizeof (CRYPTO));
+ memset(crypto,0,sizeof(*crypto));
+ return crypto;
+}
+
+void crypto_free(CRYPTO *crypto){
+ if(crypto->server_pubkey)
+ free(crypto->server_pubkey);
+ if(crypto->in_cipher)
+ cipher_free(crypto->in_cipher);
+ if(crypto->out_cipher)
+ cipher_free(crypto->out_cipher);
+ if(crypto->e)
+ bignum_free(crypto->e);
+ if(crypto->f)
+ bignum_free(crypto->f);
+ if(crypto->x)
+ bignum_free(crypto->x);
+ if(crypto->k)
+ bignum_free(crypto->k);
+ /* lot of other things */
+ /* i'm lost in my own code. good work */
+ memset(crypto,0,sizeof(*crypto));
+ free(crypto);
+}
+
+int crypt_set_algorithms(SSH_SESSION *session){
+ /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */
+ int i=0;
+ /* out */
+ char *wanted=session->client_kex.methods[KEX_CRYPT_C_S];
+ while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name))
+ i++;
+ if(!ssh_ciphertab[i].name){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Crypt_set_algorithms : no crypto algorithm function found for %s",wanted);
+ return -1;
+ }
+ ssh_say(2,"Set output algorithm %s\n",wanted);
+ session->next_crypto->out_cipher=cipher_new(i);
+ i=0;
+ /* in */
+ wanted=session->client_kex.methods[KEX_CRYPT_S_C];
+ while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name))
+ i++;
+ if(!ssh_ciphertab[i].name){
+ ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Crypt_set_algorithms : no crypto algorithm function found for %s",wanted);
+ return -1;
+ }
+ ssh_say(2,"Set input algorithm %s\n",wanted);
+ session->next_crypto->in_cipher=cipher_new(i);
+
+ /* compression */
+ if(strstr(session->client_kex.methods[KEX_COMP_C_S],"zlib"))
+ session->next_crypto->do_compress_out=1;
+ if(strstr(session->client_kex.methods[KEX_COMP_S_C],"zlib"))
+ session->next_crypto->do_compress_in=1;
+ return 0;
+}
diff --git a/kftpgrabber/src/misc/misc.cpp b/kftpgrabber/src/misc/misc.cpp
new file mode 100644
index 0000000..c4e3bfa
--- /dev/null
+++ b/kftpgrabber/src/misc/misc.cpp
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "misc.h"
+
+#include <kurl.h>
+#include <kmdcodec.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+
+#include <qcolor.h>
+#include <qpixmapcache.h>
+
+#include <X11/Xlib.h>
+
+namespace KFTPGrabberBase {
+
+QPixmap loadPanelPixmap(const QString &name)
+{
+ return KGlobal::iconLoader()->loadIcon(name, KIcon::Panel, 0, true);
+}
+
+QIconSet loadToolbarIcon(const QString &name, int state)
+{
+ return KGlobal::iconLoader()->loadIconSet(name, KIcon::Toolbar,0,state);
+}
+
+QIconSet loadSmallIcon(const QString &name, int state)
+{
+ return KGlobal::iconLoader()->loadIconSet(name, KIcon::Small,0,state);
+}
+
+QPixmap loadToolbarPixmap(const QString &name)
+{
+ return KGlobal::iconLoader()->loadIcon(name, KIcon::Toolbar,0, true);
+}
+
+QPixmap loadSmallPixmap(const QString &name)
+{
+ return KGlobal::iconLoader()->loadIcon(name, KIcon::Small,0, true);
+}
+
+QPixmap createColorPixmap(QString color)
+{
+ QPixmap tmp(28, 12);
+ tmp.fill(QColor(color));
+
+ QPixmap pixmap(32, 16);
+ pixmap.fill(QColor(0, 0, 0));
+
+ copyBlt(&pixmap, 2, 2, &tmp, 0, 0, 28, 12);
+
+ return pixmap;
+}
+
+QString getStoreDir(const QString &filename)
+{
+ return locateLocal("appdata", filename);
+}
+
+QPixmap createProgressPixmap(int progress, int current)
+{
+ if (progress > 100)
+ progress = 100;
+
+ if (current > 100)
+ current = 100;
+
+ QPixmap pixmap;
+ QString key = QString("%1:%2").arg(progress).arg(current);
+
+ if(!QPixmapCache::find(key, pixmap)) {
+ QPixmap tmp(100, 16);
+ tmp.fill(QColor(237, 237, 237));
+
+ if (progress > 0) {
+ QPixmap p_pix(progress, 16);
+ p_pix.fill(QColor(0, 115, 255));
+
+ QPixmap c_pix(current, 16);
+ c_pix.fill(QColor(0, 88, 192));
+
+ copyBlt(&tmp, 0, 0, &p_pix, 0, 0, progress, 16);
+ copyBlt(&tmp, 0, 0, &c_pix, 0, 0, current, 16);
+ }
+
+ QPixmapCache::insert(key, tmp);
+ return tmp;
+ }
+
+ return pixmap;
+}
+
+bool isModifierKeysPressed(unsigned int mask)
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int keybstate;
+ XQueryPointer(qt_xdisplay(), qt_xrootwin(), &root, &child, &root_x, &root_y, &win_x, &win_y, &keybstate);
+
+ return keybstate & mask;
+}
+
+QString appendPath(const QString &path, const QString &what)
+{
+ if (path.right(1) == "/")
+ return path + what;
+ else
+ return path + "/" + what;
+}
+
+QString path2Name(const QString &path)
+{
+ // Convert full path to filename
+ return (path == "/") ? QString("/") : path.mid(path.findRev('/')+1);
+}
+
+QString path2Dir(const QString &path)
+{
+ // Convert full path to path
+ return path.mid(0, path.findRev('/'));
+}
+
+QString genID()
+{
+ return kapp->randomString(5);
+}
+
+QString encodePassword(const QString& password)
+{
+ return KCodecs::base64Encode(password.ascii(), true).data();
+}
+
+QString decodePassword(const QString& password)
+{
+ return KCodecs::base64Decode(password.ascii()).data();
+}
+
+KConfig *config(const QString &section)
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup(section);
+
+ return conf;
+}
+
+KURL remoteUrl(const QString &path, KURL url)
+{
+ if (path.isEmpty())
+ return KURL();
+
+ KURL tmp = url;
+ tmp.setPath(path);
+
+ return tmp;
+}
+
+} // end namespace
+
diff --git a/kftpgrabber/src/misc/misc.h b/kftpgrabber/src/misc/misc.h
new file mode 100644
index 0000000..654804c
--- /dev/null
+++ b/kftpgrabber/src/misc/misc.h
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef MISC_H
+#define MISC_H
+
+#include <qiconset.h>
+#include <qptrlist.h>
+
+#include <kiconloader.h>
+
+class KURL;
+class KConfig;
+
+namespace KFTPGrabberBase {
+ // Icon loading stuff
+ QPixmap loadPanelPixmap(const QString &name);
+ QIconSet loadToolbarIcon(const QString &name, int state = 0);
+ QIconSet loadSmallIcon(const QString &name, int state = 0);
+ QPixmap loadToolbarPixmap(const QString &name);
+ QPixmap loadSmallPixmap(const QString &name);
+
+ // Other stuff
+ QPixmap createColorPixmap(QString color);
+ QPixmap createProgressPixmap(int progress, int current = 0);
+ QString appendPath(const QString &path, const QString &what);
+ QString path2Name(const QString &path);
+ QString path2Dir(const QString &path);
+ bool isModifierKeysPressed(unsigned int mask);
+ QString genID();
+ QString getStoreDir(const QString &filename = 0);
+
+ KConfig *config(const QString &section);
+ KURL remoteUrl(const QString &path, KURL url);
+
+ // For "safer" password storage
+ QString encodePassword(const QString& password);
+ QString decodePassword(const QString& password);
+}
+
+#endif
+
diff --git a/kftpgrabber/src/misc/plugins/Makefile.am b/kftpgrabber/src/misc/plugins/Makefile.am
new file mode 100644
index 0000000..0a48ec2
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/Makefile.am
@@ -0,0 +1,3 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+SUBDIRS = bookmarkimport
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/Makefile.am b/kftpgrabber/src/misc/plugins/bookmarkimport/Makefile.am
new file mode 100644
index 0000000..54d0366
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/Makefile.am
@@ -0,0 +1,3 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+SUBDIRS = gftp ncftp kftp filezilla3
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/Makefile.am b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/Makefile.am
new file mode 100644
index 0000000..737df95
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(srcdir)/../../../interfaces \
+ $(all_includes)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kftpimportplugin_filezilla3.la
+kftpimportplugin_filezilla3_la_SOURCES = kftpimportfz3plugin.cpp
+kftpimportplugin_filezilla3_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS) ../../../interfaces/libkftpinterfaces.la
+kftpimportplugin_filezilla3_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+kde_services_DATA = kftpimportplugin_filezilla3.desktop
+noinst_HEADERS = kftpimportfz3plugin.h
+
+pluginsdir = $(kde_datadir)/kftpimportplugin_filezilla3
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.cpp b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.cpp
new file mode 100644
index 0000000..0928c79
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.cpp
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpimportfz3plugin.h"
+
+#include <qdir.h>
+#include <qfile.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmdcodec.h>
+
+K_EXPORT_COMPONENT_FACTORY(kftpimportplugin_filezilla3,
+ KGenericFactory<KFTPImportFz3Plugin>("kftpimportplugin_filezilla3"))
+
+KFTPImportFz3Plugin::KFTPImportFz3Plugin(QObject *parent, const char *name, const QStringList&)
+ : KFTPBookmarkImportPlugin(parent, name)
+{
+ KGlobal::locale()->insertCatalogue("kftpgrabber");
+}
+
+QDomDocument KFTPImportFz3Plugin::getImportedXml()
+{
+ return m_domDocument;
+}
+
+void KFTPImportFz3Plugin::import(const QString &fileName)
+{
+ m_domDocument.setContent(QString("<category name=\"%1\"/>").arg(i18n("FileZilla 3 import")));
+
+ QFile file(fileName);
+ if (!file.open(IO_ReadOnly)) {
+ emit progress(100);
+ return;
+ }
+
+ m_workDocument.setContent(&file);
+ file.close();
+
+ // Import categories recursively
+ importCategory(m_domDocument.documentElement(), m_workDocument.documentElement().firstChild());
+
+ emit progress(100);
+}
+
+void KFTPImportFz3Plugin::importCategory(QDomNode parent, const QDomNode &node)
+{
+ QDomNode n = node.firstChild();
+
+ while (!n.isNull()) {
+ if (!n.isElement()) {
+ n = n.nextSibling();
+ continue;
+ }
+
+ QDomElement e = n.toElement();
+
+ if (e.tagName() == "Folder") {
+ QDomElement categoryElement = m_domDocument.createElement("category");
+ categoryElement.setAttribute("name", e.firstChild().nodeValue().stripWhiteSpace());
+ parent.appendChild(categoryElement);
+
+ importCategory(categoryElement, n);
+ } else if (e.tagName() == "Server") {
+ QString name = e.lastChild().nodeValue().stripWhiteSpace();
+ QString host = e.namedItem("Host").toElement().text();
+ QString port = e.namedItem("Port").toElement().text();
+ QString localDir = e.namedItem("LocalDir").toElement().text();
+ QString remoteDir = e.namedItem("RemoteDir").toElement().text();
+ QString username = e.namedItem("User").toElement().text();
+ QString password = e.namedItem("Pass").toElement().text();
+
+ // Set name
+ QDomElement siteElement = m_domDocument.createElement("server");
+ siteElement.setAttribute("name", name);
+ parent.appendChild(siteElement);
+
+ // Set host
+ QDomElement tmpElement = m_domDocument.createElement("host");
+ QDomText txtNode = m_domDocument.createTextNode(host);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set port
+ tmpElement = m_domDocument.createElement("port");
+ txtNode = m_domDocument.createTextNode(port);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set remote directory
+ tmpElement = m_domDocument.createElement("defremotepath");
+ txtNode = m_domDocument.createTextNode(remoteDir);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set local directory
+ tmpElement = m_domDocument.createElement("deflocalpath");
+ txtNode = m_domDocument.createTextNode(localDir);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set username
+ if (username.isNull()) {
+ username = "anonymous";
+
+ tmpElement = m_domDocument.createElement("anonlogin");
+ txtNode = m_domDocument.createTextNode("1");
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+
+ tmpElement = m_domDocument.createElement("username");
+ txtNode = m_domDocument.createTextNode(username);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set password
+ tmpElement = m_domDocument.createElement("password");
+ txtNode = m_domDocument.createTextNode(KCodecs::base64Encode(password.ascii(), true).data());
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+QString KFTPImportFz3Plugin::getDefaultPath()
+{
+ return QString(".filezilla/sitemanager.xml");
+}
+
+#include "kftpimportfz3plugin.moc"
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.h b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.h
new file mode 100644
index 0000000..c340ef5
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportfz3plugin.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPIMPORTFZ3PLUGIN_H
+#define KFTPIMPORTFZ3PLUGIN_H
+
+#include <qdom.h>
+#include <kftpbookmarkimportplugin.h>
+
+/**
+ * This plugin enables importing of FileZilla 3 bookmark files into KFTPGrabber.
+ *
+ * @author Jernej Kos
+ */
+class KFTPImportFz3Plugin : public KFTPBookmarkImportPlugin
+{
+Q_OBJECT
+public:
+ KFTPImportFz3Plugin(QObject *parent, const char *name, const QStringList&);
+
+ /**
+ * This method should return the properly formated XML for KFTPGrabber
+ * bookmarks that is generated from the import.
+ *
+ * @return The @ref QDomDocument representation of XML
+ */
+ QDomDocument getImportedXml();
+
+ /**
+ * This method should start the import procedure.
+ *
+ * @param fileName is the path to the file that will be imported
+ */
+ void import(const QString &fileName);
+
+ /**
+ * This method should return the default path where the bookmarks could
+ * be located. The path must be relative to the user's home directory.
+ *
+ * @return The default path where bookmarks are located
+ */
+ QString getDefaultPath();
+private:
+ QDomDocument m_domDocument;
+ QDomDocument m_workDocument;
+
+ void importCategory(QDomNode parent, const QDomNode &node);
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportplugin_filezilla3.desktop b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportplugin_filezilla3.desktop
new file mode 100644
index 0000000..f3b91ae
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/filezilla3/kftpimportplugin_filezilla3.desktop
@@ -0,0 +1,38 @@
+[Desktop Entry]
+Name=FileZilla 3 Import plugin
+Name[ar]=قابس الإستيراد FileZilla 3
+Name[bg]=Приставка за импортиране на FileZilla 3
+Name[da]=Import-plugin
+Name[de]=FileZilla3-Importmodul
+Name[el]=Πρόσθετο εισαγωγής FileZilla 3
+Name[es]=Complemento de importación de FileZilla 3
+Name[et]=FileZilla 3 impordiplugin
+Name[ga]=Breiseán Iompórtála FileZilla 3
+Name[it]=Plugin di importazione FileZilla 3
+Name[ja]=FileZilla 3 インポートプラグイン
+Name[nl]=FileZilla 3-importplugin
+Name[pt]='Plugin' de Importação do FileZilla 3
+Name[pt_BR]=Plug-in de importação do FileZilla 3
+Name[sv]=FileZilla 3-importinsticksprogram
+Name[uk]=Втулок імпорту FileZilla 3
+Name[zh_CN]=FileZilla 3 导入插件
+Comment=FileZilla 3 bookmarks import plugin
+Comment[ar]=قابس FileZilla 3 لإستيراد علامات المواقع
+Comment[bg]=Приставка за импортиране на FileZilla 3 отметки в KFTPGrabber
+Comment[da]=Filezilla 3 bogmærkeimport-plugin
+Comment[de]=FileZilla3-Lesezeichenimportmodul
+Comment[el]=Πρόσθετο εισαγωγής σελιδοδεικτών FileZilla 3
+Comment[es]=Complemento de importación de marcadores de FileZilla 3
+Comment[et]=FileZilla 3 järjehoidjate impordiplugin
+Comment[ga]=Breiseán iompórtála leabharmharcanna FileZilla 3
+Comment[it]=Plugin di importazione dei segnalibri di FileZilla 3
+Comment[ja]=FileZilla 3 ブックマークをインポートするプラグイン
+Comment[nl]=FileZilla 3-bladwijzers importplugin
+Comment[pt]='Plugin' de importação de favoritos do FileZilla 3
+Comment[pt_BR]=Plug-in de importação de favoritos do FileZilla 3
+Comment[sv]=Insticksprogram för FileZilla 3-bokmärkesimport
+Comment[uk]=Втулок імпортування закладок FileZilla 3
+ServiceTypes=KFTPGrabber/BookmarkImportPlugin
+Type=Service
+X-KDE-Library=kftpimportplugin_filezilla3
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/Makefile.am b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/Makefile.am
new file mode 100644
index 0000000..15a7928
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(srcdir)/../../../interfaces \
+ $(all_includes)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kftpimportplugin_gftp.la
+kftpimportplugin_gftp_la_SOURCES = kftpimportgftpplugin.cpp
+kftpimportplugin_gftp_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS) ../../../interfaces/libkftpinterfaces.la
+kftpimportplugin_gftp_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+kde_services_DATA = kftpimportplugin_gftp.desktop
+noinst_HEADERS = kftpimportgftpplugin.h
+
+pluginsdir = $(kde_datadir)/kftpimportplugin_gftp
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.cpp b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.cpp
new file mode 100644
index 0000000..9bd5e56
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.cpp
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpimportgftpplugin.h"
+
+#include <qdir.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmdcodec.h>
+
+K_EXPORT_COMPONENT_FACTORY(kftpimportplugin_gftp,
+ KGenericFactory<KFTPImportGftpPlugin>("kftpimportplugin_gftp"))
+
+KFTPImportGftpPlugin::KFTPImportGftpPlugin(QObject *parent, const char *name, const QStringList&)
+ : KFTPBookmarkImportPlugin(parent, name)
+{
+ KGlobal::locale()->insertCatalogue("kftpgrabber");
+ m_domDocument.setContent(QString("<category name=\"%1\"/>").arg(i18n("gFTP import")));
+}
+
+QDomDocument KFTPImportGftpPlugin::getImportedXml()
+{
+ return m_domDocument;
+}
+
+void KFTPImportGftpPlugin::import(const QString &fileName)
+{
+ // First we fetch some global settings
+ KConfig tmpConfig(userPath(".gftp/gftprc"), true, false, "HOME");
+ QString email = tmpConfig.readEntry("email", "anonymous@");
+ int numRetries = tmpConfig.readNumEntry("retries", -1);
+ int sleepTime = tmpConfig.readNumEntry("sleep_time", -1);
+
+ // Open the bookmarks file (it has INI-like file format, so we can use the KConfig
+ // class to do the parsing and converting)
+ KConfig config(fileName, true, false, "HOME");
+ QStringList groupList = config.groupList();
+
+ float size = (float) groupList.count();
+ if (size == 0) {
+ // There are no bookmarks (or incorrect file), we are done
+
+ emit progress(100);
+ return;
+ }
+
+ int counter = 0;
+ QStringList::Iterator end( groupList.end() );
+ for( QStringList::Iterator it( groupList.begin() ); it != end; ++it ) {
+ // gFTP bookmarks can have subgroups
+ QString groupName = *it;
+ QStringList groupNames = QStringList::split("/", groupName);
+
+ QDomNode groupNode;
+ QDomElement parentElement = m_domDocument.documentElement();
+ config.setGroup(groupName);
+ QString tmp = config.readEntry("hostname");
+
+ for (unsigned int i = 0; ! tmp.isNull() && i < groupNames.count() - 1; ++i ) {
+ // First see if parenElement has any sub group
+ groupNode = findSubGroup(parentElement, groupNames[i]);
+
+ if( groupNode.isNull() ) {
+ // No, it has no subgroup, let's create one
+ while (i < groupNames.count() -1) {
+ QDomElement tmpElement = m_domDocument.createElement("category");
+ tmpElement.setAttribute("name", groupNames[i]);
+ parentElement.appendChild(tmpElement);
+ parentElement = tmpElement;
+
+ ++i;
+ }
+ } else {
+ // Sub group found, lets check next level
+ parentElement = groupNode.toElement();
+ }
+ }
+
+ // Now group tree is updated so lets create the site (if it has hostname)
+ if (!tmp.isNull()) {
+ // Set name
+ QDomElement siteElement = m_domDocument.createElement("server");
+ siteElement.setAttribute("name", groupNames.last());
+ parentElement.appendChild(siteElement);
+
+ // Set host
+ tmp = config.readEntry("hostname");
+ QDomElement tmpElement = m_domDocument.createElement("host");
+ QDomText txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set port
+ int p = config.readNumEntry("port", 21);
+ tmpElement = m_domDocument.createElement("port");
+ txtNode = m_domDocument.createTextNode(QString::number(p));
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set remote directory
+ tmp = config.readEntry("remote directory", "/");
+ tmpElement = m_domDocument.createElement("defremotepath");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set local directory
+ tmp = config.readEntry("local directory", QDir::homeDirPath());
+ tmpElement = m_domDocument.createElement("deflocalpath");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set username
+ tmp = config.readEntry("username", "anonymous");
+ tmpElement = m_domDocument.createElement("username");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ if (tmp == "anonymous") {
+ tmpElement = m_domDocument.createElement("anonlogin");
+ txtNode = m_domDocument.createTextNode("1");
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+
+ // Set password
+ tmp = config.readEntry("password");
+ tmpElement = m_domDocument.createElement("password");
+
+ if (tmp == "@EMAIL@" || tmp.isNull() || tmp.isEmpty())
+ tmp = email;
+ else
+ tmp = decodePassword(tmp);
+
+ // We have to encode the password
+ tmp = KCodecs::base64Encode(tmp.ascii(), true).data();
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set retries
+ if (numRetries >= 0) {
+ tmpElement = m_domDocument.createElement("retrytime");
+ txtNode = m_domDocument.createTextNode(QString::number(sleepTime));
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ tmpElement = m_domDocument.createElement("retrycount");
+ txtNode = m_domDocument.createTextNode(QString::number(numRetries));
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+ }
+
+ emit progress(int(float(counter) / size * 100));
+ ++counter;
+ }
+
+ emit progress(100);
+}
+
+QString KFTPImportGftpPlugin::decodePassword(const QString &password)
+{
+ // Leave unencoded passwords as they are
+ if (password[0] != '$')
+ return password;
+
+ QString work = password;
+ work.remove(0, 1);
+
+ QString result;
+
+ for (uint i = 0; i < work.length() - 1; i += 2) {
+ char c = work.at(i).latin1();
+ char n = work.at(i+1).latin1();
+
+ result.append( ((c & 0x3c) << 2) | ((n & 0x3c) >> 2) );
+ }
+
+ return result;
+}
+
+QDomNode KFTPImportGftpPlugin::findSubGroup(QDomElement parent, const QString& name)
+{
+ QDomNodeList nodeList = parent.childNodes();
+
+ for(unsigned int i = 0; i < nodeList.count(); ++i) {
+ if(nodeList.item(i).toElement().attribute("name") == name)
+ return nodeList.item(i);
+ }
+
+ return QDomNode();
+}
+
+QString KFTPImportGftpPlugin::getDefaultPath()
+{
+ return QString(".gftp/bookmarks");
+}
+
+#include "kftpimportgftpplugin.moc"
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.h b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.h
new file mode 100644
index 0000000..af2bceb
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportgftpplugin.h
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPIMPORTGFTPPLUGIN_H
+#define KFTPIMPORTGFTPPLUGIN_H
+
+#include <qdom.h>
+#include <kftpbookmarkimportplugin.h>
+
+/**
+This plugin can import GFTP bookmarks into KFTPGrabber. This plugin has been ported
+from "KBear by Bj�n Sahlstr� <kbjorn@users.sourceforge.net>".
+
+@author Jernej Kos
+*/
+class KFTPImportGftpPlugin : public KFTPBookmarkImportPlugin
+{
+Q_OBJECT
+public:
+ KFTPImportGftpPlugin(QObject *parent, const char *name, const QStringList&);
+
+ /**
+ * This method should return the properly formated XML for KFTPGrabber
+ * bookmarks that is generated from the import.
+ *
+ * @return The @ref QDomDocument representation of XML
+ */
+ QDomDocument getImportedXml();
+
+ /**
+ * This method should start the import procedure.
+ *
+ * @param fileName is the path to the file that will be imported
+ */
+ void import(const QString &fileName);
+
+ /**
+ * This method should return the default path where the bookmarks could
+ * be located. The path must be relative to the user's home directory.
+ *
+ * @return The default path where bookmarks are located
+ */
+ QString getDefaultPath();
+private:
+ QDomDocument m_domDocument;
+
+ QDomNode findSubGroup(QDomElement parent, const QString& name);
+ QString decodePassword(const QString &password);
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportplugin_gftp.desktop b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportplugin_gftp.desktop
new file mode 100644
index 0000000..815f1d4
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/gftp/kftpimportplugin_gftp.desktop
@@ -0,0 +1,61 @@
+[Desktop Entry]
+Name=gFTP Import plugin
+Name[ar]=قابس أستيراد gFTP
+Name[bg]=Приставка за импортиране на gFTP
+Name[br]=Lugent enporzh gFTP
+Name[cs]=gFTP importní modul
+Name[da]=gFTP import-plugin
+Name[de]=gFTP-Importmodul
+Name[el]=Πρόσθετο εισαγωγής gFTP
+Name[es]=Complemento de importación de gFTP
+Name[et]=gFTP impordiplugin
+Name[fr]=Module d'importation de gFTP
+Name[ga]=Breiseán Iompórtála gFTP
+Name[gl]=Plugin de importazón de gFTP
+Name[it]=Plugin di importazione gFTP
+Name[ja]=gFTP インポートプラグイン
+Name[ka]=gFTP-ის იმპორტის მოდული
+Name[lt]=gFTP importavimo įskiepis
+Name[nl]=gFTP-importplugin
+Name[pa]=gFTP ਆਯਾਤ ਪਲੱਗਿੰਨ
+Name[pt]='Plugin' de importação do gFTP
+Name[pt_BR]=Plug-in de importação do gFTP
+Name[ru]=Модуль импорта из gFTP
+Name[sr]=Прикључак gFTP увоза
+Name[sr@Latn]=Priključak gFTP uvoza
+Name[sv]=gFTP-importinsticksprogram
+Name[tr]=gFTP'den alma Eklentisi
+Name[uk]=Втулок імпорту gFTP
+Name[xx]=xxgFTP Import pluginxx
+Name[zh_CN]=gFTP 导入插件
+Comment=gFTP bookmarks import plugin for KFTPGrabber
+Comment[ar]=قابس gFTP لإستيراد علامات المواقع لِــ KFTPGrabber
+Comment[bg]=Приставка за импортиране на gFTP отметки в KFTPGrabber
+Comment[br]=Lugent enporzh sinedoù gFTP evit KFTPGrabber
+Comment[cs]=gFTP modul importu záložek pro KFTPGrabber
+Comment[da]=gFTP bogmærker import-plugin for KFTPGrabber
+Comment[de]=gFTP-Lesezeichenimportmodul für KFTPGrabber
+Comment[el]=Πρόσθετο εισαγωγής σελιδοδεικτών του gFTP για το KFTPGrabber
+Comment[es]=Complemento de importación de marcadores de gFTP para KFTPGrabber
+Comment[et]=KFTPGrabberi gFTP järjehoidjate impordiplugin
+Comment[fr]=Module d'importation des signets gFTP pour KFTPGrabber
+Comment[ga]=Breiseán iompórtála leabharmharcanna gFTP le haghaidh KFTPGrabber
+Comment[gl]=Plugin de importazón de marcadores de gFTP para KFTPGrabber
+Comment[it]=Plugin di importazione dei segnalibri di gFTP per KFTPGrabber
+Comment[ja]=KFTPGrabber gFTP のブックマークをインポートするプラグイン
+Comment[ka]=gFTP-ის სანიშნეების იმპორტის მოდული KFTPGrabber-თვის
+Comment[lt]=gFTP žymelių importavimo į KFTPGrabber įskiepis
+Comment[nl]=KFTPGrabber-plugin voor het importeren van gFTP-bladwijzers
+Comment[pt]='Plugin' de importação de favoritos do gFTP para o KFTPGrabber
+Comment[pt_BR]=Plug-in de importação de favoritos do gFTP para o KFTPGrabber
+Comment[ru]=Импорт закладок gFTP в KFTPGrabber
+Comment[sr]=KFTPGrabber-ов прикључак за увоз gFTP маркера
+Comment[sr@Latn]=KFTPGrabber-ov priključak za uvoz gFTP markera
+Comment[sv]=Insticksprogram för gFTP-bokmärkesimport till KFTPgrabber
+Comment[tr]=KFTPGrabber için gFTP yer imlerini alma eklentisi
+Comment[uk]=Втулок імпортування закладок gFTP для KFTPGrabber
+Comment[xx]=xxgFTP bookmarks import plugin for KFTPGrabberxx
+ServiceTypes=KFTPGrabber/BookmarkImportPlugin
+Type=Service
+X-KDE-Library=kftpimportplugin_gftp
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/Makefile.am b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/Makefile.am
new file mode 100644
index 0000000..572413c
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(srcdir)/../../../interfaces \
+ $(all_includes)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kftpimportplugin_kftp.la
+kftpimportplugin_kftp_la_SOURCES = kftpimportkftpplugin.cpp
+kftpimportplugin_kftp_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS) ../../../interfaces/libkftpinterfaces.la
+kftpimportplugin_kftp_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+kde_services_DATA = kftpimportplugin_kftp.desktop
+noinst_HEADERS = kftpimportkftpplugin.h
+
+pluginsdir = $(kde_datadir)/kftpimportplugin_kftp
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.cpp b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.cpp
new file mode 100644
index 0000000..3ea06d8
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.cpp
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpimportkftpplugin.h"
+
+#include <qdir.h>
+#include <qfile.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmdcodec.h>
+
+K_EXPORT_COMPONENT_FACTORY(kftpimportplugin_kftp,
+ KGenericFactory<KFTPImportKftpPlugin>("kftpimportplugin_kftp"))
+
+KFTPImportKftpPlugin::KFTPImportKftpPlugin(QObject *parent, const char *name, const QStringList&)
+ : KFTPBookmarkImportPlugin(parent, name)
+{
+ KGlobal::locale()->insertCatalogue("kftpgrabber");
+}
+
+QDomDocument KFTPImportKftpPlugin::getImportedXml()
+{
+ return m_domDocument;
+}
+
+void KFTPImportKftpPlugin::import(const QString &fileName)
+{
+ m_domDocument.setContent(QString("<category name=\"%1\"/>").arg(i18n("KFTPGrabber import")));
+
+ // There is actually nothing to import, we just have to read the existing XML and
+ // remove site ids.
+ QFile file(fileName);
+ if (!file.open(IO_ReadOnly)) {
+ emit progress(100);
+ return;
+ }
+
+ m_workDocument.setContent(&file);
+ file.close();
+
+ // Strip all ids
+ stripIds();
+
+ // Now append the bookmarks
+ QDomNode n = m_workDocument.documentElement().firstChild();
+
+ while (!n.isNull()) {
+ QDomNode import = m_domDocument.importNode(n, true);
+ m_domDocument.documentElement().appendChild(import);
+
+ n = n.nextSibling();
+ }
+
+ emit progress(100);
+}
+
+void KFTPImportKftpPlugin::stripIds(QDomNode node)
+{
+ if (node.isNull())
+ node = m_workDocument.documentElement();
+
+ QDomNode n = node.firstChild();
+
+ while (!n.isNull()) {
+ if (n.toElement().tagName() == "category") {
+ if (!n.toElement().hasAttribute("id"))
+ n.toElement().removeAttribute("id");
+
+ stripIds(n);
+ } else if (n.toElement().tagName() == "server") {
+ if (n.toElement().hasAttribute("id"))
+ n.toElement().removeAttribute("id");
+ }
+
+ n = n.nextSibling();
+ }
+}
+
+QString KFTPImportKftpPlugin::getDefaultPath()
+{
+ return QString("");
+}
+
+#include "kftpimportkftpplugin.moc"
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.h b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.h
new file mode 100644
index 0000000..9f2e593
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportkftpplugin.h
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPIMPORTKFTPPLUGIN_H
+#define KFTPIMPORTKFTPPLUGIN_H
+
+#include <qdom.h>
+#include <kftpbookmarkimportplugin.h>
+
+/**
+ * This plugin enables importing of KFTPGrabber XML bookmark files into KFTPGrabber,
+ * so the users can import bookmarks that were previously exported.
+ *
+ * @author Jernej Kos
+ */
+class KFTPImportKftpPlugin : public KFTPBookmarkImportPlugin
+{
+Q_OBJECT
+public:
+ KFTPImportKftpPlugin(QObject *parent, const char *name, const QStringList&);
+
+ /**
+ * This method should return the properly formated XML for KFTPGrabber
+ * bookmarks that is generated from the import.
+ *
+ * @return The @ref QDomDocument representation of XML
+ */
+ QDomDocument getImportedXml();
+
+ /**
+ * This method should start the import procedure.
+ *
+ * @param fileName is the path to the file that will be imported
+ */
+ void import(const QString &fileName);
+
+ /**
+ * This method should return the default path where the bookmarks could
+ * be located. The path must be relative to the user's home directory.
+ *
+ * @return The default path where bookmarks are located
+ */
+ QString getDefaultPath();
+private:
+ QDomDocument m_domDocument;
+ QDomDocument m_workDocument;
+
+ void stripIds(QDomNode node = QDomNode());
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportplugin_kftp.desktop b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportplugin_kftp.desktop
new file mode 100644
index 0000000..0cb42ba
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/kftp/kftpimportplugin_kftp.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Name=KFTPGrabber XML Import plugin
+Name[ar]=قابس الإستيراد لِــ KFTPGrabber XML
+Name[bg]=Приставка за импортиране на XML в KFTPGrabber
+Name[br]=Lugent enporzh XML evit KFTPGrabber
+Name[cs]=KFTPGrabber XML importní modul
+Name[da]=KFTPGrabber XML Import-plugin
+Name[de]=KFTPGrabber XML-Importmodul
+Name[el]=Πρόσθετο εισαγωγής XML του KFTPGrabber
+Name[es]=Complemento de importación de XML de KFTPGrabber
+Name[et]=KFTPGrabberi XML-i impordiplugin
+Name[ga]=Breiseán Iompórtála XML KFTPGrabber
+Name[gl]=Plugin de importazón de XML para KFTPGrabber
+Name[it]=KFTPGrabber Plugin di importazione XML
+Name[ja]=KFTPGrabber XML インポートプラグイン
+Name[ka]=KFTPGrabber-ის XML-ის იმპორტის მოდული
+Name[lt]=KFTPGrabber XML importavimo įskiepis
+Name[nl]=KFTPGrabber XML Importplugin
+Name[pt]='Plugin' de importação XML do KFTPGrabber
+Name[pt_BR]=Plug-in de importação XML do KFTPGrabber
+Name[sr]=Прикључак KFTPGrabber-а за XML увоз
+Name[sr@Latn]=Priključak KFTPGrabber-a za XML uvoz
+Name[sv]=Insticksprogram för XML-import till KFTPgrabber
+Name[tr]=KFTPGrabber XML alma eklentisi
+Name[uk]=Втулок імпортування XML для KFTPGrabber
+Name[xx]=xxKFTPGrabber XML Import pluginxx
+Name[zh_CN]=KFTPGrabber XML 导入插件
+Comment=KFTPGrabber XML bookmarks import plugin
+Comment[ar]=قابس الإستيراد KFTPGrabber XML لعلامات المواقع
+Comment[bg]=Приставка за импортиране на XML отметки в KFTPGrabber
+Comment[br]=Lugent enporzh sinedoù XML KFTPGrabber evit KFTPGrabber
+Comment[cs]=XML modul importu záložek pro KFTPGrabber
+Comment[da]=KFTPGrabber XML bogmærkeimport-plugin
+Comment[de]=KFTPGrabber XML-Lesezeichenimportmodul
+Comment[el]=Πρόσθετο εισαγωγής σελιδοδεικτών XML του KFTPGrabber
+Comment[es]=Complemento de importación de marcadores en XML de KFTPGrabber
+Comment[et]=KFTPGrabberi XML-järjehoidjate impordiplugin
+Comment[ga]=Breiseán iompórtála leabharmharcanna XML le haghaidh KFTPGrabber
+Comment[gl]=Plugin de importazón de marcadores XML para KFTPGrabber
+Comment[it]=Plugin di importazione dei segnalibri XML per KFTPGrabber
+Comment[ja]=KFTPGrabber XML ブックマークをインポートするプラグイン
+Comment[ka]=KFTPGrabber-ის XML-ის სანიშნეების იმპორტის მოდული
+Comment[lt]=KFTPGrabber XML importavimo įskiepis
+Comment[nl]=KFTPGrabber-plugin voor het importeren van XML-bladwijzers
+Comment[pt]='Plugin' de importação de favoritos em XML para o KFTPGrabber
+Comment[pt_BR]=Plug-in de importação de favoritos em XML para o KFTPGrabber
+Comment[sr]=KFTPGrabber-ов прикључак за увоз XML маркера
+Comment[sr@Latn]=KFTPGrabber-ov priključak za uvoz XML markera
+Comment[sv]=Insticksprogram för XML-bokmärkesimport till KFTPgrabber
+Comment[tr]=KFTPGrabber için XML yer imlerini alma eklentisi
+Comment[uk]=Втулок імпортування XML-закладок для KFTPGrabber
+Comment[xx]=xxKFTPGrabber XML bookmarks import pluginxx
+Comment[zh_CN]=KFTPGrabber XML 书签导入插件
+ServiceTypes=KFTPGrabber/BookmarkImportPlugin
+Type=Service
+X-KDE-Library=kftpimportplugin_kftp
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/Makefile.am b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/Makefile.am
new file mode 100644
index 0000000..feb557e
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(srcdir)/../../../interfaces \
+ $(all_includes)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kftpimportplugin_ncftp.la
+kftpimportplugin_ncftp_la_SOURCES = kftpimportncftpplugin.cpp
+kftpimportplugin_ncftp_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS) ../../../interfaces/libkftpinterfaces.la
+kftpimportplugin_ncftp_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+kde_services_DATA = kftpimportplugin_ncftp.desktop
+noinst_HEADERS = kftpimportncftpplugin.h
+
+pluginsdir = $(kde_datadir)/kftpimportplugin_ncftp
+
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.cpp b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.cpp
new file mode 100644
index 0000000..e146f88
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.cpp
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "kftpimportncftpplugin.h"
+
+#include <qdir.h>
+#include <qfile.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmdcodec.h>
+
+K_EXPORT_COMPONENT_FACTORY(kftpimportplugin_ncftp,
+ KGenericFactory<KFTPImportNcftpPlugin>("kftpimportplugin_ncftp"))
+
+KFTPImportNcftpPlugin::KFTPImportNcftpPlugin(QObject *parent, const char *name, const QStringList&)
+ : KFTPBookmarkImportPlugin(parent, name)
+{
+ KGlobal::locale()->insertCatalogue("kftpgrabber");
+ m_domDocument.setContent(QString("<category name=\"%1\"/>").arg(i18n("NcFtp import")));
+}
+
+QDomDocument KFTPImportNcftpPlugin::getImportedXml()
+{
+ return m_domDocument;
+}
+
+void KFTPImportNcftpPlugin::import(const QString &fileName)
+{
+ /*
+ ARNES FTP serve,ftp.arnes.si,username,*encoded*cGFzc3dvcmQA,,/remote,I,21,4294967295,1,1,-1,1,193.2.1.79,Komentar,,,,,S,-1,/local
+ Redhat,ftp.redhat.com,,,,,I,21,1102099812,-1,-1,-1,1,66.187.224.30,,,,,,S,-1,
+ */
+
+ QFile f(fileName);
+ if (!f.open(IO_ReadOnly)) {
+ emit progress(100);
+ return;
+ }
+
+ QTextStream stream(&f);
+ QString line;
+ int lineNum = 0;
+
+ while (!stream.atEnd()) {
+ line = stream.readLine();
+ if (++lineNum <= 2) continue;
+
+ // Add the imported bookmark
+ QDomElement parentElement = m_domDocument.documentElement();
+
+ // Set name
+ QDomElement siteElement = m_domDocument.createElement("server");
+ siteElement.setAttribute("name", subSection(line, 0));
+ parentElement.appendChild(siteElement);
+
+ // Set host
+ QString tmp = subSection(line, 1);
+ QDomElement tmpElement = m_domDocument.createElement("host");
+ QDomText txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set port
+ tmp = subSection(line, 7, "21");
+ tmpElement = m_domDocument.createElement("port");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set remote directory
+ tmp = subSection(line, 5, "/");
+ tmpElement = m_domDocument.createElement("defremotepath");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set local directory
+ tmp = subSection(line, 21, QDir::homeDirPath());
+ tmpElement = m_domDocument.createElement("deflocalpath");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set username
+ tmp = subSection(line, 2, "anonymous");
+ tmpElement = m_domDocument.createElement("username");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ if (tmp == "anonymous") {
+ tmpElement = m_domDocument.createElement("anonlogin");
+ txtNode = m_domDocument.createTextNode("1");
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+
+ // Set password
+ tmp = subSection(line, 3, "");
+ tmp.replace("*encoded*", "");
+
+ tmpElement = m_domDocument.createElement("password");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+
+ // Set description
+ tmp = subSection(line, 14, "");
+ if (!tmp.isEmpty()) {
+ tmpElement = m_domDocument.createElement("description");
+ txtNode = m_domDocument.createTextNode(tmp);
+ tmpElement.appendChild(txtNode);
+ siteElement.appendChild(tmpElement);
+ }
+ }
+
+ emit progress(100);
+}
+
+QString KFTPImportNcftpPlugin::subSection(const QString &text, int section, const QString &def)
+{
+ QString tmp = text.section(',', section, section);
+
+ return tmp.isEmpty() ? def : tmp;
+}
+
+QString KFTPImportNcftpPlugin::getDefaultPath()
+{
+ return QString(".ncftp/bookmarks");
+}
+
+#include "kftpimportncftpplugin.moc"
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.h b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.h
new file mode 100644
index 0000000..7a58d0e
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportncftpplugin.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPIMPORTNCFTPPLUGIN_H
+#define KFTPIMPORTNCFTPPLUGIN_H
+
+#include <qdom.h>
+#include <kftpbookmarkimportplugin.h>
+
+/**
+This plugin can import NcFTP bookmarks into KFTPGrabber.
+
+@author Jernej Kos
+*/
+class KFTPImportNcftpPlugin : public KFTPBookmarkImportPlugin
+{
+Q_OBJECT
+public:
+ KFTPImportNcftpPlugin(QObject *parent, const char *name, const QStringList&);
+
+ /**
+ * This method should return the properly formated XML for KFTPGrabber
+ * bookmarks that is generated from the import.
+ *
+ * @return The @ref QDomDocument representation of XML
+ */
+ QDomDocument getImportedXml();
+
+ /**
+ * This method should start the import procedure.
+ *
+ * @param fileName is the path to the file that will be imported
+ */
+ void import(const QString &fileName);
+
+ /**
+ * This method should return the default path where the bookmarks could
+ * be located. The path must be relative to the user's home directory.
+ *
+ * @return The default path where bookmarks are located
+ */
+ QString getDefaultPath();
+private:
+ QDomDocument m_domDocument;
+
+ QString subSection(const QString &text, int section, const QString &def = QString::null);
+};
+
+#endif
diff --git a/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportplugin_ncftp.desktop b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportplugin_ncftp.desktop
new file mode 100644
index 0000000..226f2cc
--- /dev/null
+++ b/kftpgrabber/src/misc/plugins/bookmarkimport/ncftp/kftpimportplugin_ncftp.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Name=NcFTP Import plugin
+Name[ar]=قابس أستيراد NcFTP
+Name[bg]=Приставка за импортиране на NcFTP
+Name[br]=Lugent enporzh NcFTP
+Name[cs]=NcFTP importní modul
+Name[da]=NcFTP import-plugin
+Name[de]=NcFTP Importmodul
+Name[el]=Πρόσθετο εισαγωγής NcFTP
+Name[es]=Complemento de importación de NcFTP
+Name[et]=NcFTP impordiplugin
+Name[fr]=Module d'importation de NcFTP
+Name[ga]=Breiseán Iompórtála NcFTP
+Name[gl]=Plugin de Importazón de NcFTP
+Name[it]=Plugin di importazione NcFTP
+Name[ja]=NcFTP インポートプラグイン
+Name[ka]=NcFTP-ის იმპორტის მოდული
+Name[lt]=NcFTP importavimo įskiepis
+Name[nl]=NcFTP-importplugin
+Name[pa]=NcFTP ਆਯਾਤ ਪਲੱਗਿੰਨ
+Name[pt]='Plugin' de importação do NcFTP
+Name[pt_BR]=Plug-in de importação NcFTP
+Name[ru]=Модуль импорта из NcFTP
+Name[sr]=Прикључак NcFTP увоза
+Name[sr@Latn]=Priključak NcFTP uvoza
+Name[sv]=NcFTP-importinsticksprogram
+Name[tr]=NcFTP'den alma eklentisi
+Name[uk]=Втулок імпорту NcFTP
+Name[xx]=xxNcFTP Import pluginxx
+Name[zh_CN]=NcFTP 导入插件
+Comment=NcFTP bookmarks import plugin for KFTPGrabber
+Comment[ar]=قابس NcFTP لإستيراد علامات المواقع لِــ KFTPGrabber
+Comment[bg]=Приставка за импортиране на NcFTP отметки в KFTPGrabber
+Comment[br]=Lugent enporzh sinedoù NcFTP evit KFTPGrabber
+Comment[cs]=NcFTP modul importu záložek pro KFTPGrabber
+Comment[da]=NcFTP bogmærker import-plugin for KFTPGrabber
+Comment[de]=NcFTP Lesezeichenimportmodul für KFTPGrabber
+Comment[el]=Πρόσθετο εισαγωγής σελιδοδεικτών NcFTP για το KFTPGrabber
+Comment[es]=Complemento de importación de marcadores de NcFTP de KFTPGrabber
+Comment[et]=KFTPGrabberi NcFTP järjehoidjate impordiplugin
+Comment[fr]=Module d'importation des signets NcFTP pour KFTPGrabber
+Comment[ga]=Breiseán iompórtála leabharmharcanna NcFTP le haghaidh KFTPGrabber
+Comment[gl]=Plugin de importazón de marcadores NcFTP para KFTPGrabber
+Comment[it]=Plugin di importazione dei segnalibri di NcFTP per KFTPGrabber
+Comment[ja]=KFTPGrabber NcFTP ブックマークをインポートするプラグイン
+Comment[ka]=NcFTP-ის სანიშნეების იმპორტის მოდული KFTPGrabber-თვის
+Comment[lt]=NcFTP žymelių importavimo į KFTPGrabber įskiepis
+Comment[nl]=Een KFTPGrabber-plugin voor het importeren van NcFTP-bladwijzers
+Comment[pt]='Plugin' de importação de favoritos do NcFTP para o KFTPGrabber
+Comment[pt_BR]=Plug-in de importação de favoritos do NcFTP para o KFTPGrabber
+Comment[ru]=Импорт закладок NcFTP в KFTPGrabber
+Comment[sr]=KFTPGrabber-ов прикључак за увоз NcFTP маркера
+Comment[sr@Latn]=KFTPGrabber-ov priključak za uvoz NcFTP markera
+Comment[sv]=Insticksprogram för NcFTP-bokmärkesimport till KFTPgrabber
+Comment[tr]=KFTPGrabber için NcFTP'den yer imleri alma eklentisi
+Comment[uk]=Втулок імпортування закладок NcFTP для KFTPGrabber
+Comment[xx]=xxNcFTP bookmarks import plugin for KFTPGrabberxx
+Comment[zh_CN]=KFTPGrabber 的 NcFTP 书签导入插件
+ServiceTypes=KFTPGrabber/BookmarkImportPlugin
+Type=Service
+X-KDE-Library=kftpimportplugin_ncftp
+
diff --git a/kftpgrabber/src/queuegroup.cpp b/kftpgrabber/src/queuegroup.cpp
new file mode 100644
index 0000000..f9e49e4
--- /dev/null
+++ b/kftpgrabber/src/queuegroup.cpp
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "queuegroup.h"
+#include "queueobject.h"
+#include "kftptransfer.h"
+#include "kftpsession.h"
+
+using namespace KFTPSession;
+
+namespace KFTPQueue {
+
+QueueGroup::QueueGroup(QueueObject *object)
+ : QObject(object),
+ m_object(object),
+ m_childIterator(QPtrListIterator<QueueObject>(m_object->m_children)),
+ m_directories(true)
+{
+}
+
+void QueueGroup::reset()
+{
+ m_childIterator.toFirst();
+}
+
+int QueueGroup::executeNextTransfer()
+{
+ // Check if there is actually something to execute
+ if (!m_childIterator.current()) {
+ if (m_lastTransfer && m_lastTransfer->isRunning())
+ return -1;
+
+ if (m_childIterator.count() <= 1)
+ emit done();
+
+ return -1;
+ } else if (!m_directories && m_lastTransfer && m_lastTransfer->isDir()) {
+ return 0;
+ }
+
+ Transfer *transfer = static_cast<Transfer*>(m_childIterator.current());
+
+ // Check if we have enough connections available
+ if (m_lastTransfer) {
+ Session *sourceSession = m_lastTransfer->getSourceSession();
+ Session *destinationSession = m_lastTransfer->getDestinationSession();
+
+ if ((sourceSession && !sourceSession->isFreeConnection()) || (destinationSession && !destinationSession->isFreeConnection()))
+ return 0;
+
+ // Reserve the connections immediately
+ transfer->assignSessions(sourceSession, destinationSession);
+ }
+
+ // Get the transfer instance and schedule it's execution
+ transfer->QObject::disconnect(this);
+
+ connect(transfer, SIGNAL(transferComplete(long)), this, SLOT(incrementAndExecute()));
+ connect(transfer, SIGNAL(transferAbort(long)), this, SIGNAL(interrupted()));
+
+ transfer->delayedExecute();
+
+ // Prepare for the next transfer
+ m_lastTransfer = transfer;
+
+ return 1;
+}
+
+void QueueGroup::incrementAndExecute()
+{
+ if (QObject::sender()) {
+ const Transfer *transfer = static_cast<const Transfer*>(QObject::sender());
+
+ if (!transfer->isDir())
+ m_directories = false;
+ }
+
+ ++m_childIterator;
+
+ int result = executeNextTransfer();
+
+ switch (result) {
+ case 0: --m_childIterator; break;
+ case 1: {
+ m_directories = false;
+ incrementAndExecute();
+ break;
+ }
+ default: break;
+ }
+
+ if (result != -1 && !m_directories)
+ m_directories = true;
+}
+
+}
diff --git a/kftpgrabber/src/queuegroup.h b/kftpgrabber/src/queuegroup.h
new file mode 100644
index 0000000..a8c3562
--- /dev/null
+++ b/kftpgrabber/src/queuegroup.h
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPQUEUEQUEUEGROUP_H
+#define KFTPQUEUEQUEUEGROUP_H
+
+#include <qguardedptr.h>
+#include <qptrlist.h>
+
+namespace KFTPQueue {
+
+class QueueObject;
+class Transfer;
+
+/**
+ * This class manages a group of child queue objects so they get
+ * executed in the proper order.
+ *
+ * Note that all child transfers that are grouped together must be
+ * part of the same session, otherwise unexpected behavior may
+ * ocurr.
+ *
+ * @author Jernej Kos
+ */
+class QueueGroup : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param object The queue object to manage
+ */
+ QueueGroup(QueueObject *object);
+
+ /**
+ * Reset the group.
+ */
+ void reset();
+
+ /**
+ * Execute the next transfer in list.
+ *
+ * @return 1 if the transfer has been executed, 0 or -1 otherwise
+ */
+ int executeNextTransfer();
+public slots:
+ /**
+ * Increment the current iterator and call executeNextTransfer method.
+ */
+ void incrementAndExecute();
+private:
+ QueueObject *m_object;
+ QPtrListIterator<QueueObject> m_childIterator;
+ QGuardedPtr<Transfer> m_lastTransfer;
+ bool m_directories;
+signals:
+ /**
+ * This signal gets emitted when there is nothing more to do in the
+ * queue.
+ */
+ void done();
+
+ /**
+ * This signal gets emitted when the group processing is interrupted
+ * due to abort.
+ */
+ void interrupted();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/queueobject.cpp b/kftpgrabber/src/queueobject.cpp
new file mode 100644
index 0000000..454da84
--- /dev/null
+++ b/kftpgrabber/src/queueobject.cpp
@@ -0,0 +1,275 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "queueobject.h"
+#include "kftpqueue.h"
+
+#include <qtimer.h>
+
+namespace KFTPQueue {
+
+QueueObject::QueueObject(QObject *parent, Type type)
+ : QObject(parent),
+ m_aborting(false),
+ m_status(Stopped),
+ m_type(type),
+ m_size(0),
+ m_actualSize(0),
+ m_completed(0),
+ m_resumed(0),
+ m_speed(0)
+{
+ // Add the transfer
+ if (hasParentObject())
+ static_cast<QueueObject*>(parent)->addChildObject(this);
+
+ // Connect the delayed execution timer
+ connect(&m_delayedExecuteTimer, SIGNAL(timeout()), this, SLOT(execute()));
+}
+
+
+QueueObject::~QueueObject()
+{
+}
+
+void QueueObject::delayedExecute(int msec)
+{
+ /* Execute the transfer with delay - using a QTimer */
+ if (msec > 1000)
+ msec = 1000;
+
+ if (!m_delayedExecuteTimer.isActive())
+ m_delayedExecuteTimer.start(msec, true);
+}
+
+void QueueObject::execute()
+{
+}
+
+
+void QueueObject::addActualSize(filesize_t size)
+{
+ if (size == 0)
+ return;
+
+ m_actualSize += size;
+
+ if (hasParentObject())
+ parentObject()->addActualSize(size);
+
+ statisticsUpdated();
+}
+
+void QueueObject::addSize(filesize_t size)
+{
+ if (size == 0)
+ return;
+
+ m_size += size;
+ m_actualSize += size;
+
+ if (hasParentObject())
+ parentObject()->addSize(size);
+
+ statisticsUpdated();
+}
+
+void QueueObject::addCompleted(filesize_t completed)
+{
+ if (completed == 0)
+ return;
+
+ m_completed += completed;
+
+ if (hasParentObject())
+ parentObject()->addCompleted(completed);
+
+ statisticsUpdated();
+}
+
+void QueueObject::setSpeed(filesize_t speed)
+{
+ if (speed != 0 && m_speed == speed)
+ return;
+
+ m_speed = speed;
+
+ QPtrListIterator<QueueObject> it(m_children);
+ QueueObject *i;
+
+ while ((i = it.current()) != 0) {
+ ++it;
+
+ m_speed += i->getSpeed();
+ }
+
+ if (hasParentObject())
+ parentObject()->setSpeed();
+
+ statisticsUpdated();
+}
+
+void QueueObject::statisticsUpdated()
+{
+ emit objectUpdated();
+}
+
+void QueueObject::abort()
+{
+}
+
+void QueueObject::addChildObject(QueueObject *object)
+{
+ m_children.append(object);
+
+ connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(slotChildDestroyed(QObject*)));
+}
+
+void QueueObject::delChildObject(QueueObject *object)
+{
+ m_children.removeRef(object);
+}
+
+void QueueObject::slotChildDestroyed(QObject *child)
+{
+ // Remove the transfer
+ delChildObject(static_cast<QueueObject*>(child));
+}
+
+QueueObject *QueueObject::findChildObject(long id)
+{
+ QPtrListIterator<QueueObject> it(m_children);
+ QueueObject *i;
+
+ while ((i = it.current()) != 0) {
+ ++it;
+
+ if (i->getId() == id)
+ return i;
+
+ if (i->hasChildren()) {
+ QueueObject *tmp = i->findChildObject(id);
+
+ if (tmp)
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+void QueueObject::removeMarkedTransfers()
+{
+ QPtrListIterator<QueueObject> it(m_children);
+ QueueObject *i;
+
+ while ((i = it.current()) != 0) {
+ ++it;
+
+ if (i->hasChildren())
+ i->removeMarkedTransfers();
+
+ if (i->isTransfer() && static_cast<Transfer*>(i)->isDeleteMarked())
+ Manager::self()->removeTransfer(static_cast<Transfer*>(i));
+ }
+}
+
+bool QueueObject::canMove()
+{
+ return true;
+}
+
+void QueueObject::moveChildUp(QueueObject *child)
+{
+ if (m_children.findRef(child) != -1) {
+ if (m_children.prev()) {
+ int prevPos = m_children.at();
+ m_children.removeRef(child);
+ m_children.insert(prevPos, child);
+ }
+ }
+}
+
+void QueueObject::moveChildDown(QueueObject *child)
+{
+ if (m_children.findRef(child) != -1) {
+ if (m_children.next()) {
+ int nextPos = m_children.at();
+ m_children.removeRef(child);
+ m_children.insert(nextPos, child);
+ }
+ }
+}
+
+void QueueObject::moveChildTop(QueueObject *child)
+{
+ m_children.removeRef(child);
+ m_children.prepend(child);
+}
+
+void QueueObject::moveChildBottom(QueueObject *child)
+{
+ m_children.removeRef(child);
+ m_children.append(child);
+}
+
+bool QueueObject::canMoveChildUp(QueueObject *child)
+{
+ if (!child->canMove()) return false;
+ if (m_children.getFirst() == child) return false;
+
+ if (m_children.findRef(child) != -1) {
+ if (m_children.prev() && !m_children.current()->canMove())
+ return false;
+ }
+
+ return true;
+}
+
+bool QueueObject::canMoveChildDown(QueueObject *child)
+{
+ if (!child->canMove()) return false;
+ if (m_children.getLast() == child) return false;
+
+ if (m_children.findRef(child) != -1) {
+ if (m_children.next() && !m_children.current()->canMove())
+ return false;
+ }
+
+ return true;
+}
+
+}
+#include "queueobject.moc"
diff --git a/kftpgrabber/src/queueobject.h b/kftpgrabber/src/queueobject.h
new file mode 100644
index 0000000..a8e7088
--- /dev/null
+++ b/kftpgrabber/src/queueobject.h
@@ -0,0 +1,372 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPQUEUEQUEUEOBJECT_H
+#define KFTPQUEUEQUEUEOBJECT_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qtimer.h>
+
+#include "engine/directorylisting.h"
+
+namespace KFTPQueue {
+
+class QueueGroup;
+
+/**
+ * This class represents a basic object that can be queued.
+ *
+ * @author Jernej Kos
+ */
+class QueueObject : public QObject
+{
+friend class QueueGroup;
+Q_OBJECT
+public:
+ enum Type {
+ File,
+ Directory,
+ Site,
+ Toplevel
+ };
+
+ enum Status {
+ Running,
+ Stopped,
+ Connecting,
+ Locked,
+ Failed,
+ Waiting
+ };
+
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent object
+ * @param type Object type
+ */
+ QueueObject(QObject *parent, Type type);
+
+ /**
+ * Class destructor.
+ */
+ ~QueueObject();
+
+ /**
+ * Returns true if this object has a parent object.
+ *
+ * @return True if this object has a parent object
+ */
+ bool hasParentObject() const { return parent() ? parent()->inherits("KFTPQueue::QueueObject") : false; }
+
+ /**
+ * Returns the parent QueueObject.
+ *
+ * @return The parent QueueObject
+ */
+ QueueObject *parentObject() const { return static_cast<QueueObject*>(parent()); }
+
+ /**
+ * Do we have any children ?
+ *
+ * @return True if we have some kids
+ */
+ bool hasChildren() const { return m_children.count() > 0; }
+
+ /**
+ * Get object status.
+ *
+ * @return Status of this object
+ */
+ Status getStatus() const { return m_status; }
+
+ /**
+ * Is this object currently running ?
+ *
+ * @return true if this object's status is set to Running or Connecting
+ */
+ bool isRunning() const { return m_status == Running || m_status == Connecting || m_status == Waiting; }
+
+ /**
+ * Is the object currently locked ?
+ *
+ * @return true if this object's status is set to Locked
+ */
+ bool isLocked() const { return m_status == Locked; }
+
+ /**
+ * Is this object currently aborting ?
+ *
+ * @return true if this object is currently aborting
+ */
+ bool isAborting() const { return m_aborting; }
+
+ /**
+ * Returns the size of this queue object.
+ *
+ * @return Size
+ */
+ filesize_t getSize() const { return m_size; }
+
+ /**
+ * Returns the actual size - that is usefull only if this is a directory since
+ * it returns the current size of all its items (if some items were removed
+ * from its first scan getSize() will return the initial size and getActualSize()
+ * will return current size of all items). If this is not a directory, this
+ * will return the same value as getSize();
+ *
+ * @return Actual directory size
+ */
+ filesize_t getActualSize() const { return m_actualSize; }
+
+ /**
+ * Returns the already transfered file/dir size.
+ *
+ * @return Transfered file/dir size
+ */
+ filesize_t getCompleted() const { return m_completed; }
+
+ /**
+ * Returns the number of bytes that have been resumed (using REST).
+ *
+ * @return Resume offset
+ */
+ filesize_t getResumed() const { return m_resumed; }
+
+ /**
+ * Get current transfer speed or 0 if the transfer is stalled.
+ *
+ * @return Transfer speed
+ */
+ filesize_t getSpeed() const { return m_speed; }
+
+ /**
+ * Adds size bytes to the current transfer size. This will also update all
+ * parent transfers (if any).
+ *
+ * @param size Size to add
+ */
+ void addSize(filesize_t size);
+
+ /**
+ * Adds completed bytes to the current completed size. This will also update all
+ * parent transfers (if any).
+ *
+ * @param completed Size to add
+ */
+ void addCompleted(filesize_t completed);
+
+ /**
+ * Set the current transfer speed. This will also update all parent transfers.
+ *
+ * @param speed Speed to set
+ */
+ void setSpeed(filesize_t speed = 0);
+
+ /**
+ * Returns the KFTPQueue::Transfer::Type of this transfer. This can either be
+ * File or Directory.
+ *
+ * @return Transfer type
+ */
+ Type getType() const { return m_type; }
+
+ /**
+ * Is this object a directory ?
+ *
+ * @return true if this object's type is set to Directory
+ */
+ bool isDir() const { return m_type == Directory; }
+
+ /**
+ * Is this object a transfer ?
+ *
+ * @return true if this object's type is File or Directory
+ */
+ bool isTransfer() const { return m_type == File || m_type == Directory; }
+
+ /**
+ * Delays transfer execution for msec miliseconds. If this number is greater than
+ * 1000, it will be set to 1000.
+ *
+ * @param msec Number of miliseconds to delay execution
+ */
+ void delayedExecute(int msec = 100);
+
+ /**
+ * Set transfer's ID.
+ *
+ * @param id Transfer identifier (must be unique)
+ */
+ void setId(long id) { m_id = id; }
+
+ /**
+ * Get transfer's ID.
+ *
+ * @return Transfer's unique ID number
+ */
+ long getId() const { return m_id; }
+
+ /**
+ * Abort current transfer.
+ */
+ virtual void abort();
+
+ /**
+ * Add a child queue object to this object. The object is NOT reparented!
+ *
+ * @param object The child queue object
+ */
+ void addChildObject(QueueObject *object);
+
+ /**
+ * Delete a child queue object from this object. The object is NOT reparented!
+ *
+ * @param object The child queue object
+ */
+ void delChildObject(QueueObject *object);
+
+ /**
+ * Find a QueueObject that is child of the current object by its id. This
+ * method goes trough all the objects under this one.
+ *
+ * @param id Object's id
+ * @return A valid QueueObject or NULL if no such object can be found
+ */
+ QueueObject *findChildObject(long id);
+
+ /**
+ * Removes all transfers that have been marked for deletion.
+ */
+ void removeMarkedTransfers();
+
+ /**
+ * Move a child object up in the queue.
+ *
+ * @param child The object to move
+ */
+ void moveChildUp(QueueObject *child);
+
+ /**
+ * Move a child object down in the queue.
+ *
+ * @param child The object to move
+ */
+ void moveChildDown(QueueObject *child);
+
+ /**
+ * Move a child object to the top.
+ *
+ * @param child The object to move
+ */
+ void moveChildTop(QueueObject *child);
+
+ /**
+ * Move a child object to the bottom.
+ *
+ * @param child The object to move
+ */
+ void moveChildBottom(QueueObject *child);
+
+ /**
+ * Can a child be moved up ?
+ *
+ * @param child The child to be moved
+ * @return True if the child can be moved up
+ */
+ bool canMoveChildUp(QueueObject *child);
+
+ /**
+ * Can a child be moved down ?
+ *
+ * @param child The child to be moved
+ * @return True if the child can be moved down
+ */
+ bool canMoveChildDown(QueueObject *child);
+
+ /**
+ * Returns the list of this object's child QueueObjects.
+ *
+ * @return A QueueObject list
+ */
+ QPtrList<QueueObject> getChildrenList() const { return m_children; }
+public slots:
+ /**
+ * Execute this queue object.
+ */
+ virtual void execute();
+protected:
+ bool m_aborting;
+ Status m_status;
+
+ long m_id;
+ Type m_type;
+ QPtrList<QueueObject> m_children;
+
+ QTimer m_delayedExecuteTimer;
+
+ /* Statistical information */
+ filesize_t m_size;
+ filesize_t m_actualSize;
+ filesize_t m_completed;
+ filesize_t m_resumed;
+ filesize_t m_speed;
+
+ void addActualSize(filesize_t size);
+
+ /**
+ * This method is called every time the object's statistics must be
+ * updated.
+ */
+ virtual void statisticsUpdated();
+
+ /**
+ * This method should return true if the object can be moved.
+ */
+ virtual bool canMove();
+private slots:
+ void slotChildDestroyed(QObject *child);
+signals:
+ /**
+ * This signal gets emitted when the object's state has changed in
+ * some way.
+ */
+ void objectUpdated();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/site.cpp b/kftpgrabber/src/site.cpp
new file mode 100644
index 0000000..6f6668f
--- /dev/null
+++ b/kftpgrabber/src/site.cpp
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "site.h"
+#include "queuegroup.h"
+#include "kftpqueue.h"
+
+namespace KFTPQueue {
+
+Site::Site(QueueObject *parent, KURL url)
+ : QueueObject(parent, QueueObject::Site),
+ m_group(new QueueGroup(this))
+{
+ url.setPath("/");
+ m_siteUrl = url;
+
+ // Connect to some group signals
+ connect(m_group, SIGNAL(interrupted()), this, SLOT(slotGroupInterrupted()));
+}
+
+void Site::execute()
+{
+ m_completed = 0;
+ m_resumed = 0;
+ m_status = Running;
+
+ // Reset and start the group
+ m_group->reset();
+ m_group->executeNextTransfer();
+}
+
+void Site::abort()
+{
+ // If not running, just return
+ if (!isRunning())
+ return;
+
+ // Set the aborting flag
+ m_aborting = true;
+ emit siteAborted();
+
+ // Signal abort to all child transfers
+ QueueObject *i;
+
+ for (i = m_children.first(); i; i = m_children.next()) {
+ if (i->isRunning() && !i->isAborting())
+ i->abort();
+ }
+
+ // Clear all the stuff
+ m_status = Stopped;
+ m_resumed = 0;
+ m_completed = 0;
+ m_aborting = false;
+ m_size = m_actualSize;
+
+ emit objectUpdated();
+}
+
+void Site::slotGroupInterrupted()
+{
+ if (!m_aborting)
+ abort();
+}
+
+}
+#include "site.moc"
diff --git a/kftpgrabber/src/site.h b/kftpgrabber/src/site.h
new file mode 100644
index 0000000..e574993
--- /dev/null
+++ b/kftpgrabber/src/site.h
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPQUEUESITE_H
+#define KFTPQUEUESITE_H
+
+#include "queueobject.h"
+
+#include <kurl.h>
+
+namespace KFTPQueue {
+
+/**
+ * This class represents a site.
+ *
+ * @author Jernej Kos
+ */
+class Site : public QueueObject
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent object
+ * @param url The site's URL
+ */
+ Site(QueueObject *parent, KURL url);
+
+ /**
+ * Return the site's URL
+ *
+ * @return The URL for this site
+ */
+ KURL getUrl() const { return m_siteUrl; }
+
+ /**
+ * Process all child transfers, one by one (just like in TransferDir).
+ */
+ void execute();
+
+ /**
+ * Abort transfer processing.
+ */
+ void abort();
+private:
+ KURL m_siteUrl;
+ QueueGroup *m_group;
+private slots:
+ void slotGroupInterrupted();
+signals:
+ void siteAborted();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/statistics.cpp b/kftpgrabber/src/statistics.cpp
new file mode 100644
index 0000000..cdd55e2
--- /dev/null
+++ b/kftpgrabber/src/statistics.cpp
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "statistics.h"
+
+#include <kstaticdeleter.h>
+
+namespace KFTPQueue {
+
+Statistics *Statistics::m_self = 0;
+static KStaticDeleter<Statistics> staticStatsDeleter;
+
+StatisticsSite::StatisticsSite()
+ : m_lastFxpSpeed(0.0)
+{
+}
+
+Statistics *Statistics::self()
+{
+ if (!m_self) {
+ staticStatsDeleter.setObject(m_self, new Statistics());
+ }
+
+ return m_self;
+}
+
+Statistics::Statistics()
+{
+ m_sites.setAutoDelete(true);
+}
+
+Statistics::~Statistics()
+{
+ if (m_self == this)
+ staticStatsDeleter.setObject(m_self, 0, false);
+}
+
+StatisticsSite *Statistics::getSite(const KURL &url)
+{
+ // Reset the url's path and grab the site
+ KURL tmp = url;
+ tmp.setPath("/");
+
+ StatisticsSite *site = m_sites[tmp.url()];
+
+ if (!site) {
+ site = new StatisticsSite();
+ m_sites.insert(tmp.url(), site);
+ }
+
+ return site;
+}
+
+}
+
+#include "statistics.moc"
diff --git a/kftpgrabber/src/statistics.h b/kftpgrabber/src/statistics.h
new file mode 100644
index 0000000..1f0f309
--- /dev/null
+++ b/kftpgrabber/src/statistics.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUESTATISTICS_H
+#define KFTPQUEUESTATISTICS_H
+
+#include <qobject.h>
+#include <qdict.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+
+namespace KFTPQueue {
+
+/**
+ * This class represents a statistics for a single site.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class StatisticsSite {
+public:
+ StatisticsSite();
+
+ /**
+ * Set the site's last FXP speed.
+ *
+ * @param speed Site's last FXP speed
+ */
+ void setLastFxpSpeed(double speed) { m_lastFxpSpeed = speed; }
+
+ /**
+ * Get the site's last FXP speed.
+ *
+ * @return Site's last FXP speed
+ */
+ double lastFxpSpeed() const { return m_lastFxpSpeed; }
+private:
+ double m_lastFxpSpeed;
+};
+
+/**
+ * This class provides different kind of per-site statistics.
+ *
+ * @author Jernej Kos <kostko@unimatrix-one.org>
+ */
+class Statistics : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Returns the global statistics class instance.
+ */
+ static Statistics *self();
+
+ /**
+ * Class destructor.
+ */
+ ~Statistics();
+
+ /**
+ * Returns a site that corresponds to the given URL. If the site doesn't
+ * exist it is created.
+ *
+ * @param url The site's URL
+ * @return A valid StatisticsSite pointer
+ */
+ StatisticsSite *getSite(const KURL &url);
+protected:
+ /**
+ * Global class instance.
+ */
+ static Statistics *m_self;
+
+ /**
+ * Class constructor.
+ */
+ Statistics();
+private:
+ QDict<StatisticsSite> m_sites;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/ui/Makefile.am b/kftpgrabber/src/ui/Makefile.am
new file mode 100644
index 0000000..50aff1c
--- /dev/null
+++ b/kftpgrabber/src/ui/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = -I$(top_srcdir) \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../widgets \
+ -I$(srcdir)/../widgets/queueview \
+ $(all_includes)
+
+METASOURCES = AUTO
+noinst_LIBRARIES = libui.a
+
+libui_a_SOURCES = bookmark_properties.ui kftpfilteraddpatternlayout.ui \
+ kftpquickconnectlayout.ui kftpqueueeditorlayout.ui kftpsearchlayout.ui \
+ kftpbookmarkimportlayout.ui kftpbookmarkeditortlswidget.ui config_general.ui \
+ config_transfers.ui config_log.ui config_display.ui config_filters.ui \
+ foobar.cpp checksum_verifier.ui
+
+noinst_HEADERS = foobar.h
diff --git a/kftpgrabber/src/ui/bookmark_properties.ui b/kftpgrabber/src/ui/bookmark_properties.ui
new file mode 100644
index 0000000..a41d18a
--- /dev/null
+++ b/kftpgrabber/src/ui/bookmark_properties.ui
@@ -0,0 +1,620 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>BookmarkProperties</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>BookmarkProperties</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>458</width>
+ <height>475</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>ftpSiteProperties</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>general</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Hostname:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Protocol:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout22</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>FTP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>FTP over TLS/SSL (explicit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>FTP over TLS/SSL (implicit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>SFTP over SSH2</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>serverProtocol</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>protoAdvanced</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="7" column="1">
+ <property name="name">
+ <cstring>defRemoteDir</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>serverAddress</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>serverPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>21</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Site label:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>serverName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Remote directory:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Local directory:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>downUser</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="6" column="1">
+ <property name="name">
+ <cstring>defLocalDir</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="5" column="1">
+ <property name="name">
+ <cstring>downPass</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>anonLogin</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Anonymous login</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5_2</cstring>
+ </property>
+ <property name="title">
+ <string>Description</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTextEdit">
+ <property name="name">
+ <cstring>description</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>advanced</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Advanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>disableEPSV</cstring>
+ </property>
+ <property name="text">
+ <string>Disa&amp;ble use of extended passive mode</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>disablePASV</cstring>
+ </property>
+ <property name="text">
+ <string>Disable use of passive mode</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>pasvSiteIp</cstring>
+ </property>
+ <property name="text">
+ <string>Use site IP for passive mode connections</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>disableForceIp</cstring>
+ </property>
+ <property name="text">
+ <string>Disable "force active mode to use this IP" for this site</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>statListings</cstring>
+ </property>
+ <property name="text">
+ <string>Use STAT for directory listings</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Multiple Transfer Threads</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>threadsDisable</cstring>
+ </property>
+ <property name="text">
+ <string>Do not use multiple threads for this site</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8</cstring>
+ </property>
+ <property name="title">
+ <string>Server Encoding</string>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>21</y>
+ <width>61</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Encoding:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>serverEncoding</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>78</x>
+ <y>21</y>
+ <width>341</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>430</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>keepalive</cstring>
+ </property>
+ <attribute name="title">
+ <string>Retry &amp;&amp; &amp;Keepalive</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>doRetry</cstring>
+ </property>
+ <property name="text">
+ <string>Retr&amp;y to connect on failure</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>retryGroup</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Retry</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Number of retries (0 = infinite):</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Retry delay:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>retryCount</cstring>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ <spacer row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>240</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>retryDelay</cstring>
+ </property>
+ <property name="maxValue">
+ <number>9999</number>
+ </property>
+ <property name="value">
+ <number>60</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>doKeepalive</cstring>
+ </property>
+ <property name="text">
+ <string>Use keepalive packets to keep the connection open</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>keepaliveGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Keepalive</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Keepalive frequency (seconds):</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>141</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>keepaliveTimeout</cstring>
+ </property>
+ <property name="maxValue">
+ <number>600</number>
+ </property>
+ <property name="minValue">
+ <number>30</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>tabList</tabstop>
+ <tabstop>serverName</tabstop>
+ <tabstop>serverAddress</tabstop>
+ <tabstop>serverPort</tabstop>
+ <tabstop>serverProtocol</tabstop>
+ <tabstop>protoAdvanced</tabstop>
+ <tabstop>anonLogin</tabstop>
+ <tabstop>downUser</tabstop>
+ <tabstop>downPass</tabstop>
+ <tabstop>defLocalDir</tabstop>
+ <tabstop>defRemoteDir</tabstop>
+ <tabstop>description</tabstop>
+ <tabstop>disableEPSV</tabstop>
+ <tabstop>disablePASV</tabstop>
+ <tabstop>pasvSiteIp</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/checksum_verifier.ui b/kftpgrabber/src/ui/checksum_verifier.ui
new file mode 100644
index 0000000..be2f8af
--- /dev/null
+++ b/kftpgrabber/src/ui/checksum_verifier.ui
@@ -0,0 +1,340 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>VerifierLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>VerifierLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>500</width>
+ <height>490</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>490</height>
+ </size>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox23</cstring>
+ </property>
+ <property name="title">
+ <string>Progress</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KProgress" row="1" column="0">
+ <property name="name">
+ <cstring>checkProgress</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout57</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;File:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>currentFile</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>none</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox24</cstring>
+ </property>
+ <property name="title">
+ <string>File list</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>fileList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout68</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout64</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Unprocessed</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout65</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel11_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ok</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout66</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel11_3</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>188</red>
+ <green>188</green>
+ <blue>188</blue>
+ </color>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Not found</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout67</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel11_4</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Failed</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>260</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kprogress.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/config_display.ui b/kftpgrabber/src/ui/config_display.ui
new file mode 100644
index 0000000..1734bf1
--- /dev/null
+++ b/kftpgrabber/src/ui/config_display.ui
@@ -0,0 +1,137 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DisplaySettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DisplaySettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>537</width>
+ <height>424</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget6</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Displa&amp;y</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox32</cstring>
+ </property>
+ <property name="title">
+ <string>File &amp;Browser</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showHiddenFiles</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;hidden files and directories</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showTree</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;directory tree</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showSizeInBytes</cstring>
+ </property>
+ <property name="text">
+ <string>Show filesi&amp;ze in bytes (toggle for "human readable" format)</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showOwnerGroup</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;owner and group for each file</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showDirectorySize</cstring>
+ </property>
+ <property name="text">
+ <string>Show directory &amp;size</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Other Interface Elements</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showLeftSidebar</cstring>
+ </property>
+ <property name="text">
+ <string>Show left sidebar</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer20</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kftpgrabber/src/ui/config_filters.ui b/kftpgrabber/src/ui/config_filters.ui
new file mode 100644
index 0000000..f9ac803
--- /dev/null
+++ b/kftpgrabber/src/ui/config_filters.ui
@@ -0,0 +1,137 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ConfigFilterLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigFilterLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>596</width>
+ <height>451</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>KFTPFilterEditorLayout</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>ASCII E&amp;xtensions</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>212</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addExtButton</cstring>
+ </property>
+ <property name="text">
+ <string>Add Extension</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeExtButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Extension:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>newExtension</cstring>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Extension</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>extensionList</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/config_general.ui b/kftpgrabber/src/ui/config_general.ui
new file mode 100644
index 0000000..9fd4f94
--- /dev/null
+++ b/kftpgrabber/src/ui/config_general.ui
@@ -0,0 +1,440 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GeneralSettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GeneralSettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>544</width>
+ <height>398</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget3</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox22</cstring>
+ </property>
+ <property name="title">
+ <string>E-mail &amp;Address</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_globalMail</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use e-mail address from control center</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout26</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>E-mail:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>kcfg_anonMail</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox13</cstring>
+ </property>
+ <property name="title">
+ <string>Startup and Exit</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_confirmExit</cstring>
+ </property>
+ <property name="text">
+ <string>Confirm program e&amp;xit if there are active transfers</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_startMinimized</cstring>
+ </property>
+ <property name="text">
+ <string>Start the program minimi&amp;zed to systray</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showSplash</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;splash screen on startup</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showSystrayIcon</cstring>
+ </property>
+ <property name="text">
+ <string>Show the s&amp;ystray icon</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_exitOnClose</cstring>
+ </property>
+ <property name="text">
+ <string>Ex&amp;it by default when clicking the X button</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Bookmarks</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Site Defaults</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Local directory:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>kcfg_defLocalDir</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Retry delay:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_retryTime</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Number of retries (0 = infinite):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_retryCount</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Encoding:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>cfg_defEncoding</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox11</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_encryptBookmarks</cstring>
+ </property>
+ <property name="text">
+ <string>Encr&amp;ypt bookmark file</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showWalletSites</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show sites from KWallet among bookmarks</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Notification</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox12</cstring>
+ </property>
+ <property name="title">
+ <string>Balloons</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showBalloons</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show balloon when transfer completes</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showBalloonWhenQueueEmpty</cstring>
+ </property>
+ <property name="text">
+ <string>Only show when &amp;queue is empty after transfer</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_showRetrySuccessBalloon</cstring>
+ </property>
+ <property name="text">
+ <string>Show balloon when connection retr&amp;y succeeds</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>111</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/config_log.ui b/kftpgrabber/src/ui/config_log.ui
new file mode 100644
index 0000000..9424672
--- /dev/null
+++ b/kftpgrabber/src/ui/config_log.ui
@@ -0,0 +1,355 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>LogSettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LogSettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>688</width>
+ <height>402</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget5</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Appearance</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox27</cstring>
+ </property>
+ <property name="title">
+ <string>Font &amp;&amp; Colors</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout37</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Font:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester">
+ <property name="name">
+ <cstring>kcfg_logFont</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout32</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Client command color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_logCommandsColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Server response color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_logResponsesColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout34</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Multiline response color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_logMultilineColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout35</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>Error message color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_logErrorColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout36</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel15</cstring>
+ </property>
+ <property name="text">
+ <string>Status message color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_logStatusColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>111</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Output</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox28</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;File Output</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_saveToFile</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Save log to file</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout38</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel16</cstring>
+ </property>
+ <property name="text">
+ <string>Output file:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>kcfg_outputFilename</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>171</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/config_transfers.ui b/kftpgrabber/src/ui/config_transfers.ui
new file mode 100644
index 0000000..22c5b48
--- /dev/null
+++ b/kftpgrabber/src/ui/config_transfers.ui
@@ -0,0 +1,734 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>TransferSettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TransferSettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>646</width>
+ <height>592</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget4</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Co&amp;nnection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox19</cstring>
+ </property>
+ <property name="title">
+ <string>Active Connection IP</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_portForceIp</cstring>
+ </property>
+ <property name="text">
+ <string>Force PORT/EPRT to &amp;use configured IP</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>IP/hostname:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>kcfg_portIp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_ignoreExternalIpForLan</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Ignore external IP for LAN connections</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Active Connection Port Range</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_activeForcePort</cstring>
+ </property>
+ <property name="text">
+ <string>Onl&amp;y use ports from the specified port range</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Minimum port:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_activeMinPort</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum port:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_activeMaxPort</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox6</cstring>
+ </property>
+ <property name="title">
+ <string>Timeouts</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Control connection timeout (in seconds):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_controlTimeout</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Data transfer timeout (in seconds):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_dataTimeout</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox7</cstring>
+ </property>
+ <property name="title">
+ <string>Speed limit</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Download (KB/s):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_downloadSpeedLimit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxValue">
+ <number>99999</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Upload (KB/s):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_uploadSpeedLimit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxValue">
+ <number>99999</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Dis&amp;k Space</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox20</cstring>
+ </property>
+ <property name="title">
+ <string>Free Disk Space Check</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_diskCheckSpace</cstring>
+ </property>
+ <property name="text">
+ <string>Stop transfer if there is &amp;not enough free space</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Interval (sec):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_diskCheckInterval</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout25</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Minimum free space (MiB):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_diskMinFreeSpace</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>191</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Thre&amp;ads</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox23</cstring>
+ </property>
+ <property name="title">
+ <string>Threads</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout27</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Number of threads per session:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_threadCount</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_threadUsePrimary</cstring>
+ </property>
+ <property name="text">
+ <string>Use the primary connection for transfers</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>360</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Miscellaneous</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8</cstring>
+ </property>
+ <property name="title">
+ <string>Failed Transfers</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_failedAutoRetry</cstring>
+ </property>
+ <property name="text">
+ <string>Automatically retry failed transfers</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum number of retries before marking as failed:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_failedAutoRetryCount</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox24</cstring>
+ </property>
+ <property name="title">
+ <string>Other</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_queueOnDND</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Queue files (instead of transferring) when "dragged &amp;&amp; dropped"</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_skipEmptyDirs</cstring>
+ </property>
+ <property name="text">
+ <string>Skip &amp;empty directories when queueing</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_confirmDisconnects</cstring>
+ </property>
+ <property name="text">
+ <string>Confirm disconnects &amp;before disconnecting</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer12</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>260</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/foobar.cpp b/kftpgrabber/src/ui/foobar.cpp
new file mode 100644
index 0000000..675490c
--- /dev/null
+++ b/kftpgrabber/src/ui/foobar.cpp
@@ -0,0 +1,19 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Jernej Kos *
+ * kostko@jweb-network.net *
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "foobar.h"
+
+
+
+
+FooBar::~FooBar()
+{
+}
+
+
diff --git a/kftpgrabber/src/ui/foobar.h b/kftpgrabber/src/ui/foobar.h
new file mode 100644
index 0000000..9fa3619
--- /dev/null
+++ b/kftpgrabber/src/ui/foobar.h
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Jernej Kos *
+ * kostko@jweb-network.net *
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef FOOBAR_H
+#define FOOBAR_H
+
+/**
+@author Jernej Kos
+*/
+class FooBar{
+public:
+
+
+ ~FooBar();
+
+};
+
+#endif
diff --git a/kftpgrabber/src/ui/kftpbookmarkeditortlswidget.ui b/kftpgrabber/src/ui/kftpbookmarkeditortlswidget.ui
new file mode 100644
index 0000000..56ed590
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpbookmarkeditortlswidget.ui
@@ -0,0 +1,173 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPBookmarkEditorTLSWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KFTPBookmarkEditorTLSWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>350</width>
+ <height>155</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Data Connection Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mode:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>Always encrypt the data channel</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Encrypt only for directory listings</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Do not encrypt the data channel</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>tlsMode</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>X509 Certificate</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>useCert</cstring>
+ </property>
+ <property name="text">
+ <string>Use the following SSL certificate when connecting</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Path:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>certPath</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>181</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/kftpbookmarkimportlayout.ui b/kftpgrabber/src/ui/kftpbookmarkimportlayout.ui
new file mode 100644
index 0000000..e181881
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpbookmarkimportlayout.ui
@@ -0,0 +1,280 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPBookmarkImportLayout</class>
+<widget class="QWizard">
+ <property name="name">
+ <cstring>KFTPBookmarkImportLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>629</width>
+ <height>533</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Bookmark Import Wizard</string>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>Step1</cstring>
+ </property>
+ <attribute name="title">
+ <string>Step 1: &lt;b&gt;Select Import Plugin&lt;/b&gt;</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_wizardPixmap</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>170</width>
+ <height>430</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Image</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Please select the appropriate import plugin from the list below. Each plugin can import from one different format.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Available import plugins:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Description</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_pluginList</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>Step2</cstring>
+ </property>
+ <attribute name="title">
+ <string>Step 2: &lt;b&gt;Select Bookmark File to Import&lt;/b&gt;</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_wizardPixmap_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>170</width>
+ <height>430</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Image</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Please select the bookmark file from which you would like to import your bookmarks. A default path has already been determined by the import plugin.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Bookmark path:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_importUrl</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>Step3</cstring>
+ </property>
+ <attribute name="title">
+ <string>Step 3: &lt;b&gt;Importing Bookmarks...&lt;/b&gt;</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_wizardPixmap_3</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>170</width>
+ <height>430</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Image</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Please wait while the bookmarks are being imported.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Import progress:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="KProgress">
+ <property name="name">
+ <cstring>m_progressBar</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer12</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>241</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kprogress.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/kftpfilteraddpatternlayout.ui b/kftpgrabber/src/ui/kftpfilteraddpatternlayout.ui
new file mode 100644
index 0000000..fe02996
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpfilteraddpatternlayout.ui
@@ -0,0 +1,125 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPFilterAddPatternLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KFTPFilterAddPatternLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>380</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>2</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>New Pattern</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Filename pattern:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Color:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>patName</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>patColor</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/kftpqueueeditorlayout.ui b/kftpgrabber/src/ui/kftpqueueeditorlayout.ui
new file mode 100644
index 0000000..83ee815
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpqueueeditorlayout.ui
@@ -0,0 +1,327 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPQueueEditorLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KFTPQueueEditorLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>628</width>
+ <height>290</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>dstPath</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>srcPath</cstring>
+ </property>
+ </widget>
+ <widget class="QTabWidget" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>serverTab</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Source Server Info</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Server Info</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Server name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Host:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="KFTPServerLineEdit" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>srcName</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>srcHost</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>srcUser</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>srcPass</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>srcPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>21</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Destination Server Info</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1_2</cstring>
+ </property>
+ <property name="title">
+ <string>Server Info</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Server name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Host:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="KFTPServerLineEdit" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>dstName</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>dstHost</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>dstUser</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>dstPass</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel8_2</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>dstPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>21</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Source:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Destination:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Transfer type:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="3" column="2">
+ <item>
+ <property name="text">
+ <string>Download</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Upload</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>FXP</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>transferType</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>KFTPServerLineEdit</class>
+ <header location="local">kftpserverlineedit.h</header>
+ <sizehint>
+ <width>0</width>
+ <height>18</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>7</hordata>
+ <verdata>0</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ <signal>serverChanged(QDomNode server)</signal>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/kftpquickconnectlayout.ui b/kftpgrabber/src/ui/kftpquickconnectlayout.ui
new file mode 100644
index 0000000..03648e6
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpquickconnectlayout.ui
@@ -0,0 +1,538 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPQuickConnectLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KFTPQuickConnectLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>385</width>
+ <height>356</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32766</height>
+ </size>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget7</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Recent connections</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Select connection:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>recentConnections</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>clearRecent</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Quick Connect</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>URL:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>urlBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter the whole url into this box</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Host:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>hostBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter ftp's hostname</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>portBox</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>21</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Protocol:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>FTP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>FTP over TLS/SSL (explicit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>FTP over TLS/SSL (implicit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>SFTP over SSH2</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>protocolBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>protoAdvanced</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>anonLogin</cstring>
+ </property>
+ <property name="text">
+ <string>Anon&amp;ymous login</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check for anonymous login</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>usernameBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter account username</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit">
+ <property name="name">
+ <cstring>passwordBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter account password</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>addBookmark</cstring>
+ </property>
+ <property name="text">
+ <string>Add to &amp;bookmarks</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Advanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox14</cstring>
+ </property>
+ <property name="title">
+ <string>Server Encoding</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Encoding:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>serverEncoding</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>421</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>tabWidget7</tabstop>
+ <tabstop>urlBox</tabstop>
+ <tabstop>hostBox</tabstop>
+ <tabstop>portBox</tabstop>
+ <tabstop>protocolBox</tabstop>
+ <tabstop>protoAdvanced</tabstop>
+ <tabstop>anonLogin</tabstop>
+ <tabstop>usernameBox</tabstop>
+ <tabstop>passwordBox</tabstop>
+ <tabstop>addBookmark</tabstop>
+ <tabstop>serverEncoding</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/ui/kftpsearchlayout.ui b/kftpgrabber/src/ui/kftpsearchlayout.ui
new file mode 100644
index 0000000..8c25300
--- /dev/null
+++ b/kftpgrabber/src/ui/kftpsearchlayout.ui
@@ -0,0 +1,245 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KFTPSearchLayout</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KFTPSearchLayout</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>550</width>
+ <height>409</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Queue Search &amp; Replace</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Using this dialog, you can do massive replacing of source/destination paths of the queued transfers. &lt;b&gt;Changes cannot be undone.&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Search What</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Destination:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Source:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>searchServer</cstring>
+ </property>
+ <property name="text">
+ <string>Search only for transfers on specific server</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Server Info</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Server name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Host:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="KFTPServerLineEdit" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>searchServerName</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>searchServerHost</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>searchServerUser</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>searchServerPass</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>searchServerPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>21</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>searchSrcPath</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>searchDstPath</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3_2</cstring>
+ </property>
+ <property name="title">
+ <string>Replace With</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Destination:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Source:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>replaceSrcPath</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>replaceDstPath</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>KFTPServerLineEdit</class>
+ <header location="local">widgets/kftpserverlineedit.h</header>
+ <sizehint>
+ <width>0</width>
+ <height>18</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>7</hordata>
+ <verdata>0</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ <signal>serverChanged(QDomNode server)</signal>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/kftpgrabber/src/widgets/Makefile.am b/kftpgrabber/src/widgets/Makefile.am
new file mode 100644
index 0000000..fa99f84
--- /dev/null
+++ b/kftpgrabber/src/widgets/Makefile.am
@@ -0,0 +1,28 @@
+INCLUDES = -I$(srcdir)/.. \
+ -I$(srcdir)/../engine \
+ -I$(srcdir)/../misc \
+ -I$(srcdir)/../widgets/browser \
+ -I.. \
+ -I../ui \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES =
+noinst_LIBRARIES = libwidgets.a
+libwidgets_a_SOURCES = logview.cpp kftpselectserverdialog.cpp \
+ kftpselectserverdialog.h kftpserverlineedit.cpp kftpserverlineedit.h listview.cpp \
+ quickconnect.cpp systemtray.cpp balloon.cpp searchdialog.cpp \
+ kftpzeroconflistview.cpp trafficgraph.cpp kftptabwidget.cpp failedtransfers.cpp \
+ listviewitem.cpp sidebar.cpp multitabbar.cpp configdialog.cpp \
+ configfilter.cpp verifier.cpp filtereditor.cpp widgetlister.cpp overlaywidget.cpp \
+ popupmessage.cpp
+
+SUBDIRS = queueview browser bookmarks
+
+noinst_HEADERS = listview.h quickconnect.h systemtray.h searchdialog.h \
+ kftpzeroconflistview.h trafficgraph.h kftptabwidget.h \
+ logview.h failedtransfers.h listviewitem.h sidebar.h multitabbar.h \
+ multitabbar_p.h configdialog.h configfilter.h verifier.h balloon.h filtereditor.h \
+ widgetlister.h overlaywidget.h popupmessage.h
+
diff --git a/kftpgrabber/src/widgets/balloon.cpp b/kftpgrabber/src/widgets/balloon.cpp
new file mode 100644
index 0000000..eb10432
--- /dev/null
+++ b/kftpgrabber/src/widgets/balloon.cpp
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <qpointarray.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <kapplication.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kactivelabel.h>
+#include <kglobalsettings.h>
+
+#include "widgets/balloon.h"
+#include "misc.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+Balloon::Balloon(const QString &text, const QString &pix)
+ : QWidget(0L, "KFTPBalloon", WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM)
+{
+ setCaption("");
+
+ QVBoxLayout *BalloonLayout = new QVBoxLayout(this, 22, KDialog::spacingHint(), "BalloonLayout");
+
+ // BEGIN Layout1
+ QHBoxLayout *Layout1 = new QHBoxLayout(BalloonLayout, KDialog::spacingHint(), "Layout1");
+ KActiveLabel *caption = new KActiveLabel(text, this, "caption");
+ caption->setPalette(QToolTip::palette());
+ caption->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ if (!pix.isEmpty()) {
+ QLabel *image = new QLabel(this, "mImage");
+ image->setScaledContents(FALSE);
+ image->setPixmap(loadSmallPixmap(pix));
+
+ Layout1->addWidget(image);
+ }
+
+ Layout1->addWidget(caption);
+ // END Layout1
+
+ setPalette(QToolTip::palette());
+ setAutoMask(true);
+
+ connect(caption, SIGNAL(clicked(int, int)), this, SIGNAL(signalBalloonClicked()));
+ connect(caption, SIGNAL(linkClicked(const QString &)), this, SIGNAL(signalIgnoreButtonClicked()));
+ connect(caption, SIGNAL(linkClicked(const QString &)), this, SLOT(deleteLater()));
+}
+
+void Balloon::mousePressEvent(QMouseEvent *e)
+{
+ emit signalBalloonClicked();
+ e->accept();
+}
+
+void Balloon::setAnchor(const QPoint &anchor)
+{
+ m_anchor = anchor;
+ updateMask();
+}
+
+void Balloon::updateMask()
+{
+ QRegion mask(10, 10, width() - 20, height() - 20);
+
+ QPoint corners[8] = {
+ QPoint(width() - 50, 10),
+ QPoint(10, 10),
+ QPoint(10, height() - 50),
+ QPoint(width() - 50, height() - 50),
+ QPoint(width() - 10, 10),
+ QPoint(10, 10),
+ QPoint(10, height() - 10),
+ QPoint(width() - 10, height() - 10)
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ QPointArray corner;
+ corner.makeArc(corners[i].x(), corners[i].y(), 40, 40, i * 16 * 90, 16 * 90);
+ corner.resize(corner.size() + 1);
+ corner.setPoint(corner.size() - 1, corners[i + 4]);
+ mask -= corner;
+ }
+
+ // get screen-geometry for screen our anchor is on
+ // (geometry can differ from screen to screen!)
+ QRect deskRect = KGlobalSettings::desktopGeometry(m_anchor);
+
+ bool bottom = (m_anchor.y() + height()) > (deskRect.height() - 48);
+ bool right = (m_anchor.x() + width()) > (deskRect.width() - 48);
+
+ QPointArray arrow(4);
+ arrow.setPoint(0, QPoint(right ? width() : 0, bottom ? height() : 0));
+ arrow.setPoint(1, QPoint(right ? width() - 10 : 10, bottom ? height() - 30 : 30));
+ arrow.setPoint(2, QPoint(right ? width() - 30 : 30, bottom ? height() - 10 : 10));
+ arrow.setPoint(3, arrow[0]);
+ mask += arrow;
+ setMask(mask);
+
+ move(right ? m_anchor.x() - width() : m_anchor.x(), bottom ? m_anchor.y() - height() : m_anchor.y());
+}
+
+}
+
+#include "balloon.moc"
+
diff --git a/kftpgrabber/src/widgets/balloon.h b/kftpgrabber/src/widgets/balloon.h
new file mode 100644
index 0000000..c123ae7
--- /dev/null
+++ b/kftpgrabber/src/widgets/balloon.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSBALLOON_H
+#define KFTPWIDGETSBALLOON_H
+
+#include <qwidget.h>
+
+namespace KFTPWidgets {
+
+/**
+ * A little balloon for notifications taken from Kopete with some modifications
+ * for KFTPGrabber.
+ *
+ * @author Malte Starostik <malte@kde.org>
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+class Balloon : public QWidget
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param text The text to display inside the balloon
+ * @param pic Name of the pixmap to display inside the balloon
+ */
+ Balloon(const QString &text, const QString &pic);
+
+ /**
+ * Set widget anchor.
+ *
+ * @param anchor Anchor coordinates
+ */
+ void setAnchor(const QPoint &anchor);
+
+ /**
+ * Intercept mouse press events.
+ */
+ void mousePressEvent(QMouseEvent *e);
+signals:
+ void signalButtonClicked();
+ void signalIgnoreButtonClicked();
+ void signalBalloonClicked();
+protected:
+ void updateMask();
+private:
+ QPoint m_anchor;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/bookmarks/Makefile.am b/kftpgrabber/src/widgets/bookmarks/Makefile.am
new file mode 100644
index 0000000..d735a01
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I$(top_builddir)/kftpgrabber/src \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../engine \
+ -I$(srcdir)/../../misc \
+ -I$(srcdir)/../../widgets \
+ -I$(srcdir)/../../widgets/browser \
+ -I$(srcdir)/../../ui \
+ -I$(top_builddir)/kftpgrabber/src/ui \
+ $(all_includes)
+METASOURCES = AUTO
+noinst_LIBRARIES = libbookmarkwidgets.a
+noinst_HEADERS = editor.h editortls.h listview.h importwizard.h sidebar.h
+libbookmarkwidgets_a_SOURCES = editor.cpp editortls.cpp listview.cpp importwizard.cpp sidebar.cpp
diff --git a/kftpgrabber/src/widgets/bookmarks/editor.cpp b/kftpgrabber/src/widgets/bookmarks/editor.cpp
new file mode 100644
index 0000000..88ba05c
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/editor.cpp
@@ -0,0 +1,492 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qspinbox.h>
+#include <qsplitter.h>
+#include <qtextedit.h>
+#include <qtabwidget.h>
+
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kcombobox.h>
+#include <kurlrequester.h>
+#include <kguiitem.h>
+#include <kfiledialog.h>
+#include <kio/job.h>
+
+#include "kftpapi.h"
+#include "kftpbookmarks.h"
+#include "misc/config.h"
+#include "misc.h"
+
+#include "editor.h"
+#include "listview.h"
+#include "importwizard.h"
+#include "editortls.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+KActionCollection *BookmarkEditor::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+BookmarkEditor::BookmarkEditor(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, "FTP Bookmark Editor", User2|User1|Ok|Apply|Cancel, Ok, true, KGuiItem(i18n("Import...")), KGuiItem(i18n("Export..."))),
+ m_activeSite(0),
+ m_activeItem(0L),
+ m_portChanged(false)
+{
+ // We operate on a copy of the bookmarks
+ m_bookmarks = new KFTPBookmarks::Manager(*KFTPBookmarks::Manager::self());
+
+ QHBoxLayout *winLayout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ QSplitter *splitter = new QSplitter(this);
+ setMainWidget(splitter);
+ winLayout->addWidget(splitter);
+
+ // Create the list view for editing bookmarks
+ m_tree = new ListView(m_bookmarks, splitter);
+
+ m_properties = new BookmarkProperties(splitter);
+
+ initDialog();
+
+ connect(this, SIGNAL(user1Clicked()), this, SLOT(slotImportWizard()));
+ connect(this, SIGNAL(user2Clicked()), this, SLOT(slotExportWizard()));
+
+ connect(m_tree, SIGNAL(bookmarkClicked(QListViewItem*)), this, SLOT(slotTreeClicked(QListViewItem*)));
+ connect(m_tree, SIGNAL(bookmarkNew(ListViewItem*, KFTPBookmarks::Site*)), this, SLOT(slotNewAction(ListViewItem*, KFTPBookmarks::Site*)));
+ connect(m_tree, SIGNAL(bookmarkDuplicated(ListViewItem*, KFTPBookmarks::Site*)), this, SLOT(slotDuplicateAction(ListViewItem*, KFTPBookmarks::Site*)));
+ connect(m_tree, SIGNAL(bookmarkDeleted()), this, SLOT(slotDeleteAction()));
+ connect(m_tree, SIGNAL(bookmarkMoved()), this, SLOT(refresh()));
+ connect(m_tree, SIGNAL(categoryRenamed()), this, SLOT(refresh()));
+
+ // Get the new bookmark data
+ m_tree->fillBookmarkData();
+}
+
+BookmarkEditor::BookmarkEditor(ListViewItem *item, QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("FTP Bookmark Editor"), Ok|Apply|Cancel, Ok, true),
+ m_activeSite(0),
+ m_activeItem(item),
+ m_tree(0L),
+ m_portChanged(false)
+{
+ setCaption(m_activeItem->text(0));
+
+ // We operate on a copy of the bookmarks
+ m_bookmarks = new KFTPBookmarks::Manager(*KFTPBookmarks::Manager::self());
+
+ QHBoxLayout *winLayout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ m_properties = new BookmarkProperties(this);
+ setMainWidget(m_properties);
+ winLayout->addWidget(m_properties);
+
+ initDialog();
+
+ m_activeSite = m_activeItem->m_site;
+
+ showActiveNode();
+}
+
+BookmarkEditor::~BookmarkEditor()
+{
+ delete m_bookmarks;
+}
+
+void BookmarkEditor::initDialog()
+{
+ m_properties->tabList->setTabIconSet(m_properties->general, loadSmallIcon("kfm"));
+ m_properties->tabList->setTabIconSet(m_properties->advanced, loadSmallIcon("contents"));
+ m_properties->tabList->setTabIconSet(m_properties->keepalive, loadSmallIcon("clock"));
+ m_properties->protoAdvanced->setIconSet(loadSmallIcon("configure"));
+ m_properties->retryGroup->setEnabled(false);
+ m_properties->defLocalDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly );
+
+ connect(m_properties->anonLogin, SIGNAL(clicked()), this, SLOT(slotChangeAnonLogin()));
+ connect(m_properties->doRetry, SIGNAL(clicked()), this, SLOT(slotChangeActiveRetryGroup()));
+ connect(m_properties->doKeepalive, SIGNAL(clicked()), this, SLOT(slotChangeActiveKeepaliveGroup()));
+ connect(m_properties->protoAdvanced, SIGNAL(clicked()), this, SLOT(slotProtoAdvancedClicked()));
+ connect(m_properties->serverProtocol, SIGNAL(activated(int)), this, SLOT(slotChangeServerProtocol(int)));
+ connect(m_properties->serverPort, SIGNAL(valueChanged(int)), this, SLOT(slotPortChanged()));
+
+ connect(this, SIGNAL(applyClicked()), this, SLOT(slotSaveActiveNode()));
+ connect(this, SIGNAL(okClicked()), this, SLOT(slotSaveActiveNode()));
+
+ // Populate charsets
+ QStringList charsets = KGlobal::charsets()->descriptiveEncodingNames();
+ m_properties->serverEncoding->insertStringList(charsets);
+
+ // Deactivate everything until an item gets selected
+ m_properties->setEnabled(false);
+}
+
+void BookmarkEditor::refresh()
+{
+ m_tree->clear();
+ m_tree->fillBookmarkData();
+}
+
+void BookmarkEditor::slotImportWizard()
+{
+ ImportWizard *wizard = new ImportWizard(this);
+
+ if (wizard->exec() == QDialog::Accepted) {
+ // Refresh bookmarks
+ m_bookmarks->setBookmarks(KFTPBookmarks::Manager::self());
+
+ m_tree->clear();
+ m_tree->fillBookmarkData();
+ }
+
+ delete wizard;
+}
+
+void BookmarkEditor::slotExportWizard()
+{
+ if (KMessageBox::questionYesNo(0, i18n("<qt>You are about to export your KFTPGrabber bookmarks. They may contain passwords or sensitive X509 certificates; exporting your bookmarks may compromise their safety.<br><br>Are you sure?</qt>"), i18n("Export Bookmarks")) == KMessageBox::No) {
+ return;
+ }
+
+ QString savePath = KFileDialog::getSaveFileName(QString::null, "*.xml|KFTPGrabber Bookmarks", 0, i18n("Export Bookmarks"));
+
+ if (!savePath.isEmpty()) {
+ // Save and copy the bookmarks
+ KFTPBookmarks::Manager::self()->save();
+ KIO::copy(KURL(getStoreDir("bookmarks.xml")), KURL(savePath), false);
+ }
+}
+
+void BookmarkEditor::slotNewAction(ListViewItem *item, KFTPBookmarks::Site *site)
+{
+ // Display the new server's properties
+ m_activeItem = item;
+ m_activeSite = site;
+
+ showActiveNode();
+
+ // Set Focus to servername
+ m_properties->serverName->selectAll();
+ m_properties->serverName->setFocus();
+}
+
+void BookmarkEditor::slotDuplicateAction(ListViewItem *item, KFTPBookmarks::Site *site)
+{
+ m_activeItem = item;
+
+ // Display its properties
+ m_activeSite = site;
+
+ showActiveNode();
+}
+
+void BookmarkEditor::slotDeleteAction()
+{
+ m_activeItem = 0L;
+ m_properties->setEnabled(false);
+}
+
+void BookmarkEditor::slotSaveActiveNode()
+{
+ if (!m_activeItem) {
+ KFTPBookmarks::Manager::self()->setBookmarks(m_bookmarks);
+ return;
+ }
+
+ m_activeSite->setAttribute("name", m_properties->serverName->text());
+ m_activeSite->setProperty("host", m_properties->serverAddress->text());
+ m_activeSite->setProperty("port", m_properties->serverPort->value());
+ m_activeSite->setProperty("protocol", m_properties->serverProtocol->currentItem() == SP_SFTP ? "sftp" : "ftp");
+ m_activeSite->setProperty("use_tls", m_properties->serverProtocol->currentItem() == SP_SSL_EXPLICIT);
+ m_activeSite->setProperty("use_implicit", m_properties->serverProtocol->currentItem() == SP_SSL_IMPLICIT);
+ m_activeSite->setProperty("anonlogin", m_properties->anonLogin->isChecked());
+
+ m_activeSite->setProperty("username", m_properties->downUser->text());
+ m_activeSite->setProperty("password", encodePassword(m_properties->downPass->password()));
+
+ m_activeSite->setProperty("encoding", KGlobal::charsets()->encodingForName(m_properties->serverEncoding->currentText()));
+
+ // Only save the local dir if it differs from the default one
+ if (m_properties->defLocalDir->url() != KFTPCore::Config::defLocalDir())
+ m_activeSite->setProperty("deflocalpath", m_properties->defLocalDir->url());
+
+ m_activeSite->setProperty("defremotepath", m_properties->defRemoteDir->text());
+ m_activeSite->setProperty("description", m_properties->description->text());
+
+ m_activeSite->setProperty("doRetry", m_properties->doRetry->isChecked());
+ m_activeSite->setProperty("retrytime", m_properties->retryDelay->value());
+ m_activeSite->setProperty("retrycount", m_properties->retryCount->value());
+
+ m_activeSite->setProperty("doKeepalive", m_properties->doKeepalive->isChecked());
+ m_activeSite->setProperty("keepaliveTimeout", m_properties->keepaliveTimeout->value());
+
+ m_activeSite->setProperty("disablePASV", m_properties->disablePASV->isChecked());
+ m_activeSite->setProperty("disableEPSV", m_properties->disableEPSV->isChecked());
+ m_activeSite->setProperty("pasvSiteIp", m_properties->pasvSiteIp->isChecked());
+ m_activeSite->setProperty("disableForceIp", m_properties->disableForceIp->isChecked());
+ m_activeSite->setProperty("disableThreads", m_properties->threadsDisable->isChecked());
+ m_activeSite->setProperty("statListings", m_properties->statListings->isChecked());
+
+ // Update the ListView node
+ m_activeItem->setText(0, m_properties->serverName->text());
+
+ if (m_tree)
+ KFTPBookmarks::Manager::self()->setBookmarks(m_bookmarks);
+
+ showActiveNode();
+
+ // Save the bookmarks
+ KFTPBookmarks::Manager::self()->save();
+}
+
+void BookmarkEditor::showActiveNode()
+{
+ if (m_activeItem->m_type == BT_CATEGORY || !m_activeSite) {
+ m_properties->setEnabled(false);
+ return;
+ }
+
+ m_properties->setEnabled(true);
+
+ // Show all the data for the active node
+ m_properties->serverName->setText(m_activeSite->getAttribute("name"));
+ m_properties->serverAddress->setText(m_activeSite->getProperty("host"));
+
+ if (m_activeSite->getProperty("protocol") == "sftp") m_properties->serverProtocol->setCurrentItem(SP_SFTP);
+ else if (m_activeSite->getIntProperty("use_tls")) m_properties->serverProtocol->setCurrentItem(SP_SSL_EXPLICIT);
+ else if (m_activeSite->getIntProperty("use_implicit")) m_properties->serverProtocol->setCurrentItem(SP_SSL_IMPLICIT);
+ else m_properties->serverProtocol->setCurrentItem(SP_FTP);
+
+ slotChangeServerProtocol(m_properties->serverProtocol->currentItem());
+
+
+ m_properties->serverPort->setValue(m_activeSite->getIntProperty("port"));
+ m_portChanged = false;
+
+ m_properties->downUser->erase();
+ m_properties->downPass->erase();
+ m_properties->anonLogin->setChecked(m_activeSite->getIntProperty("anonlogin"));
+
+ slotChangeAnonLogin();
+
+ m_properties->downUser->setText(m_activeSite->getProperty("username"));
+ m_properties->downPass->insert(m_activeSite->getProperty("password"));
+
+ QString defLocalPath = m_activeSite->getProperty("deflocalpath");
+ if (defLocalPath.isEmpty())
+ defLocalPath = KFTPCore::Config::defLocalDir();
+
+ m_properties->defLocalDir->setURL(defLocalPath);
+ m_properties->defRemoteDir->setText(m_activeSite->getProperty("defremotepath"));
+ m_properties->description->setText(m_activeSite->getProperty("description"));
+
+ int retryTime = m_activeSite->getIntProperty("retrytime");
+ int retryCount = m_activeSite->getIntProperty("retrycount");
+
+ m_properties->doRetry->setChecked(m_activeSite->getIntProperty("doRetry"));
+
+ if (m_properties->doRetry->isChecked()) {
+ m_properties->retryGroup->setEnabled(true);
+ m_properties->retryDelay->setValue(retryTime);
+ m_properties->retryCount->setValue(retryCount);
+ } else {
+ m_properties->retryGroup->setEnabled(false);
+ m_properties->retryDelay->setValue(KFTPCore::Config::retryTime());
+ m_properties->retryCount->setValue(KFTPCore::Config::retryCount());
+ }
+
+ m_properties->doKeepalive->setChecked(m_activeSite->getIntProperty("doKeepalive"));
+
+ if (m_properties->doKeepalive->isChecked()) {
+ m_properties->keepaliveGroup->setEnabled(true);
+ m_properties->keepaliveTimeout->setValue(m_activeSite->getIntProperty("keepaliveTimeout"));
+ } else {
+ m_properties->keepaliveGroup->setEnabled(false);
+ m_properties->keepaliveTimeout->setValue(60);
+ }
+
+ QString serverEncoding = m_activeSite->getProperty("encoding");
+ if (serverEncoding.isEmpty())
+ serverEncoding = KFTPCore::Config::defEncoding();
+
+ serverEncoding = QString("%1 ( %2 )").arg(KGlobal::charsets()->languageForEncoding(serverEncoding)).arg(serverEncoding);
+ m_properties->serverEncoding->setCurrentText(serverEncoding);
+
+ m_properties->disablePASV->setChecked(m_activeSite->getIntProperty("disablePASV"));
+ m_properties->disableEPSV->setChecked(m_activeSite->getIntProperty("disableEPSV"));
+ m_properties->pasvSiteIp->setChecked(m_activeSite->getIntProperty("pasvSiteIp"));
+ m_properties->disableForceIp->setChecked(m_activeSite->getIntProperty("disableForceIp"));
+ m_properties->threadsDisable->setChecked(m_activeSite->getIntProperty("disableThreads"));
+ m_properties->statListings->setChecked(m_activeSite->getIntProperty("statListings"));
+}
+
+void BookmarkEditor::slotTreeClicked(QListViewItem *item)
+{
+ m_activeItem = static_cast<ListViewItem*>(item);
+
+ if ( !m_activeItem || m_activeItem->m_type == BT_CATEGORY ) {
+ // Deactivate all controls
+ m_properties->setEnabled(false);
+ return;
+ }
+
+ // Find the node and set it active
+ if (m_activeItem->m_type == BT_CATEGORY) {
+ // Category
+ m_activeSite = 0L;
+ } else {
+ // Server
+ m_activeSite = m_activeItem->m_site;
+ }
+
+ // Show the active node (if any)
+ showActiveNode();
+}
+
+void BookmarkEditor::slotChangeActiveRetryGroup()
+{
+ m_properties->retryGroup->setEnabled(m_properties->doRetry->isChecked());
+}
+
+void BookmarkEditor::slotChangeActiveKeepaliveGroup()
+{
+ m_properties->keepaliveGroup->setEnabled(m_properties->doKeepalive->isChecked());
+}
+
+void BookmarkEditor::slotChangeAnonLogin()
+{
+ static QString tmpUser, tmpPass;
+
+ if (m_properties->anonLogin->isChecked()) {
+ m_properties->downUser->setEnabled(false);
+ m_properties->downPass->setEnabled(false);
+ tmpUser = m_properties->downUser->text();
+ tmpPass = m_properties->downPass->text();
+ m_properties->downUser->setText("anonymous");
+
+ // Use the appropriate e-mail address for anonymous accounts
+ if (!KFTPCore::Config::anonMail().isEmpty())
+ m_properties->downPass->setText(KFTPCore::Config::anonMail());
+ else
+ m_properties->downPass->setText("userlogin@anonymo.us");
+ } else {
+ m_properties->downUser->setEnabled(true);
+ m_properties->downPass->setEnabled(true);
+ m_properties->downUser->setText(tmpUser);
+ m_properties->downPass->setText(tmpPass);
+ }
+}
+
+void BookmarkEditor::slotProtoAdvancedClicked()
+{
+ BookmarkEditorTLS *tlsDialog = new BookmarkEditorTLS(this);
+
+ // Initialize dialog
+ int mode = m_activeSite->getIntProperty("tls_data_mode");
+ bool useCert = m_activeSite->getIntProperty("use_cert");
+ QString certPath = m_activeSite->getProperty("tls_cert_path");
+
+ tlsDialog->setTLSMode(mode);
+ tlsDialog->setCertChecked(useCert);
+ if (useCert)
+ tlsDialog->setCertPath(certPath);
+ tlsDialog->slotChangeActiveX509Group();
+
+ if (tlsDialog->exec()) {
+ // Save the modifications
+ m_activeSite->setProperty("tls_data_mode", tlsDialog->getTLSMode());
+ m_activeSite->setProperty("use_cert", tlsDialog->isCertChecked());
+
+ if (tlsDialog->isCertChecked())
+ m_activeSite->setProperty("tls_cert_path", tlsDialog->getCertPath());
+ }
+
+ delete tlsDialog;
+}
+
+void BookmarkEditor::slotPortChanged()
+{
+ m_portChanged = true;
+}
+
+void BookmarkEditor::slotChangeServerProtocol(int item)
+{
+ // Enable/Disable the SSL/TLS settings if needed
+ m_properties->protoAdvanced->setEnabled( item == 1 || item == 2 );
+
+ // Set the default port
+ if (!m_portChanged) {
+ switch (item) {
+ case SP_SSL_IMPLICIT:
+ if (m_properties->serverPort->value() == 21 || m_properties->serverPort->value() == 22)
+ m_properties->serverPort->setValue(993);
+ break;
+ case SP_SFTP:
+ if (m_properties->serverPort->value() == 21 || m_properties->serverPort->value() == 993)
+ m_properties->serverPort->setValue(22);
+ break;
+ default:
+ if (m_properties->serverPort->value() == 22 || m_properties->serverPort->value() == 993)
+ m_properties->serverPort->setValue(21);
+ break;
+ }
+
+ m_portChanged = false;
+ }
+}
+
+}
+
+}
+
+#include "editor.moc"
diff --git a/kftpgrabber/src/widgets/bookmarks/editor.h b/kftpgrabber/src/widgets/bookmarks/editor.h
new file mode 100644
index 0000000..50b04aa
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/editor.h
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKEDITOR_H
+#define KFTPBOOKMARKEDITOR_H
+
+#include "ui/bookmark_properties.h"
+
+#include <kaction.h>
+#include <kdialogbase.h>
+
+#include <qlistview.h>
+#include <qdom.h>
+
+namespace KFTPBookmarks {
+ class Manager;
+ class Site;
+}
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+class ListViewItem;
+class ListView;
+
+/**
+@author Jernej Kos
+*/
+class BookmarkEditor : public KDialogBase
+{
+Q_OBJECT
+public:
+ BookmarkEditor(QWidget *parent = 0, const char *name = 0);
+ BookmarkEditor(ListViewItem *item, QWidget *parent = 0, const char *name = 0);
+ ~BookmarkEditor();
+private:
+ enum BookmarkType {
+ BT_CATEGORY = 0,
+ BT_SERVER
+ };
+
+ enum ServerProtocol {
+ SP_FTP = 0,
+ SP_SSL_EXPLICIT,
+ SP_SSL_IMPLICIT,
+ SP_SFTP
+ };
+
+ KFTPBookmarks::Manager *m_bookmarks;
+ KFTPBookmarks::Site *m_activeSite;
+
+ ListViewItem *m_activeItem;
+
+ BookmarkProperties *m_properties;
+ ListView *m_tree;
+
+ bool m_portChanged;
+
+ /* Actions */
+ KAction *m_newAction;
+ KAction *m_renameAction;
+ KAction *m_deleteAction;
+ KAction *m_subCatAction;
+ KAction *m_copyAction;
+ KActionCollection *actionCollection();
+
+ // Some common stuff for the constructors
+ void initDialog();
+
+ void showActiveNode();
+public slots:
+ void refresh();
+private slots:
+ /* Action slots */
+ void slotNewAction(ListViewItem *item, KFTPBookmarks::Site *site);
+ void slotDeleteAction();
+ void slotDuplicateAction(ListViewItem *item, KFTPBookmarks::Site *site);
+
+ /* Other slots */
+ void slotChangeAnonLogin();
+ void slotProtoAdvancedClicked();
+ void slotChangeActiveRetryGroup();
+ void slotChangeActiveKeepaliveGroup();
+ void slotChangeServerProtocol(int item);
+ void slotPortChanged();
+
+ void slotTreeClicked(QListViewItem *item);
+
+ void slotImportWizard();
+ void slotExportWizard();
+
+ void slotSaveActiveNode();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/bookmarks/editortls.cpp b/kftpgrabber/src/widgets/bookmarks/editortls.cpp
new file mode 100644
index 0000000..c299288
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/editortls.cpp
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <qcheckbox.h>
+
+#include <kcombobox.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+
+#include "kftpapi.h"
+#include "editortls.h"
+#include "kftpbookmarkeditortlswidget.h"
+#include "misc.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+KActionCollection *BookmarkEditorTLS::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+BookmarkEditorTLS::BookmarkEditorTLS(QWidget *parent, const char *name)
+ : KDialogBase(parent, name)
+{
+ m_mainWidget = new KFTPBookmarkEditorTLSWidget(this);
+ setMainWidget(m_mainWidget);
+
+ setCaption(i18n("SSL/TLS Settings"));
+ showButtonApply(false);
+
+ connect(m_mainWidget->useCert, SIGNAL(clicked()), this, SLOT(slotChangeActiveX509Group()));
+}
+
+BookmarkEditorTLS::~BookmarkEditorTLS()
+{
+ delete m_mainWidget;
+}
+
+void BookmarkEditorTLS::slotChangeActiveX509Group()
+{
+ m_mainWidget->certPath->setEnabled(m_mainWidget->useCert->isChecked());
+}
+
+int BookmarkEditorTLS::getTLSMode()
+{
+ return m_mainWidget->tlsMode->currentItem();
+}
+
+bool BookmarkEditorTLS::isCertChecked()
+{
+ return m_mainWidget->useCert->isChecked();
+}
+
+QString BookmarkEditorTLS::getCertPath()
+{
+ return m_mainWidget->certPath->url();
+}
+
+void BookmarkEditorTLS::setTLSMode(int mode)
+{
+ m_mainWidget->tlsMode->setCurrentItem(mode);
+}
+
+void BookmarkEditorTLS::setCertChecked(const bool checked)
+{
+ m_mainWidget->useCert->setChecked(checked);
+}
+
+void BookmarkEditorTLS::setCertPath(const QString &path)
+{
+ m_mainWidget->certPath->setURL(path);
+}
+
+}
+
+}
+
+#include "editortls.moc"
diff --git a/kftpgrabber/src/widgets/bookmarks/editortls.h b/kftpgrabber/src/widgets/bookmarks/editortls.h
new file mode 100644
index 0000000..3325946
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/editortls.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKEDITORTLS_H
+#define KFTPBOOKMARKEDITORTLS_H
+
+#include "editor.h"
+#include "kftpbookmarkeditortlswidget.h"
+
+#include <kaction.h>
+#include <kdialogbase.h>
+
+#include <qlistview.h>
+#include <qdom.h>
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+/**
+@author Jernej Kos
+*/
+class BookmarkEditorTLS : public KDialogBase
+{
+Q_OBJECT
+public:
+ BookmarkEditorTLS(QWidget *parent = 0, const char *name = 0);
+ ~BookmarkEditorTLS();
+
+ int getTLSMode();
+ bool isCertChecked();
+ QString getCertPath();
+
+ void setTLSMode(int mode);
+ void setCertChecked(const bool checked);
+ void setCertPath(const QString &path);
+private:
+ QDomNode m_activeNode;
+ QDomNode m_activeCategory;
+
+ KFTPBookmarkEditorTLSWidget *m_mainWidget;
+
+ KActionCollection *actionCollection();
+public slots:
+ void slotChangeActiveX509Group();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/bookmarks/importwizard.cpp b/kftpgrabber/src/widgets/bookmarks/importwizard.cpp
new file mode 100644
index 0000000..aa30bee
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/importwizard.cpp
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "importwizard.h"
+#include "kftpapi.h"
+#include "kftppluginmanager.h"
+#include "interfaces/kftpbookmarkimportplugin.h"
+#include "misc.h"
+#include "kftpbookmarks.h"
+
+#include <qfileinfo.h>
+#include <qlabel.h>
+
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kprogress.h>
+#include <kstandarddirs.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+PluginListItem::PluginListItem(KListView *parent, KService::Ptr service)
+ : KListViewItem(parent, service->name(), service->comment()), m_service(service)
+{
+ setPixmap(0, loadSmallPixmap("filter"));
+}
+
+ImportWizard::ImportWizard(QWidget *parent, const char *name)
+ : KFTPBookmarkImportLayout(parent, name)
+{
+ m_pluginList->setFullWidth(true);
+ m_pluginList->setAllColumnsShowFocus(true);
+
+ connect(m_pluginList, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotPluginsSelectionChanged(QListViewItem*)));
+
+ setNextEnabled(Step1, false);
+
+ // Set pixmap
+ QString pixmapPath = locate("appdata", "kftpgrabber-bi-wizard.png");
+ if (!pixmapPath.isNull()) {
+ QPixmap pix(pixmapPath);
+
+ m_wizardPixmap->setPixmap(pix);
+ m_wizardPixmap_2->setPixmap(pix);
+ m_wizardPixmap_3->setPixmap(pix);
+ }
+
+ // Disable useless help buttons
+ setHelpEnabled(Step1, false);
+ setHelpEnabled(Step2, false);
+ setHelpEnabled(Step3, false);
+
+ displayPluginList();
+}
+
+void ImportWizard::next()
+{
+ if (currentPage() == Step1) {
+ // Load the plugin
+ m_plugin = KFTPAPI::getInstance()->pluginManager()->loadImportPlugin(m_service);
+
+ if (!m_plugin) {
+ KMessageBox::error(0, i18n("Unable to load the selected import plugin."));
+ return;
+ } else {
+ // Get the default plugin path
+ m_importUrl->setURL("~/" + m_plugin->getDefaultPath());
+ }
+ } else if (currentPage() == Step2) {
+ // Check if the file exists
+ if (!QFileInfo(m_importUrl->url()).exists() || !QFileInfo(m_importUrl->url()).isReadable()) {
+ KMessageBox::error(0, i18n("The selected file does not exist or is not readable."));
+ return;
+ }
+ }
+
+ QWizard::next();
+
+ if (currentPage() == Step3) {
+ // Start the import
+ setBackEnabled(Step3, false);
+
+ connect(m_plugin, SIGNAL(progress(int)), this, SLOT(slotImportProgress(int)));
+ m_plugin->import(m_importUrl->url());
+ }
+}
+
+void ImportWizard::slotImportProgress(int progress)
+{
+ m_progressBar->setProgress(progress);
+
+ if (progress == 100) {
+ // Import complete
+ KMessageBox::information(0, i18n("Bookmark importing is complete."));
+
+ // Add the imported stuff to the current bookmarks
+ KFTPBookmarks::Manager::self()->importSites(m_plugin->getImportedXml().documentElement());
+
+ accept();
+ }
+}
+
+void ImportWizard::displayPluginList()
+{
+ KTrader::OfferList plugins = KFTPAPI::getInstance()->pluginManager()->getImportPlugins();
+
+ KTrader::OfferList::ConstIterator end(plugins.end());
+ for (KTrader::OfferList::ConstIterator i(plugins.begin()); i != end; ++i) {
+ KService::Ptr service = *i;
+
+ new PluginListItem(m_pluginList, service);
+ }
+}
+
+void ImportWizard::slotPluginsSelectionChanged(QListViewItem *i)
+{
+ if (i) {
+ PluginListItem *item = static_cast<PluginListItem*>(i);
+ m_service = item->m_service;
+
+ setNextEnabled(Step1, true);
+ } else {
+ setNextEnabled(Step1, false);
+ }
+}
+
+}
+
+}
+
+#include "importwizard.moc"
diff --git a/kftpgrabber/src/widgets/bookmarks/importwizard.h b/kftpgrabber/src/widgets/bookmarks/importwizard.h
new file mode 100644
index 0000000..9b74dd3
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/importwizard.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKIMPORTWIZARD_H
+#define KFTPBOOKMARKIMPORTWIZARD_H
+
+#include "kftpbookmarkimportlayout.h"
+
+#include <kparts/componentfactory.h>
+#include <klistview.h>
+
+class KFTPBookmarkImportPlugin;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+class PluginListItem : public KListViewItem
+{
+friend class ImportWizard;
+public:
+ PluginListItem(KListView *parent, KService::Ptr service);
+private:
+ KService::Ptr m_service;
+};
+
+/**
+ * This is a wizard used to import bookmarks via detected KParts plugins.
+ *
+ * @author Jernej Kos
+ */
+class ImportWizard : public KFTPBookmarkImportLayout
+{
+Q_OBJECT
+public:
+ ImportWizard(QWidget *parent = 0, const char *name = 0);
+private:
+ KService::Ptr m_service;
+ KFTPBookmarkImportPlugin *m_plugin;
+
+ void displayPluginList();
+private slots:
+ virtual void next();
+
+ void slotImportProgress(int progress);
+ void slotPluginsSelectionChanged(QListViewItem *i);
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/bookmarks/listview.cpp b/kftpgrabber/src/widgets/bookmarks/listview.cpp
new file mode 100644
index 0000000..4349604
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/listview.cpp
@@ -0,0 +1,532 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpapi.h"
+#include "listview.h"
+#include "kftpbookmarks.h"
+#include "kftpsession.h"
+#include "misc.h"
+
+#include <qdragobject.h>
+
+#include <kaction.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kio/passdlg.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+ListViewItem::ListViewItem(QListView *parent, const QString &t1)
+ : KListViewItem(parent, t1)
+{
+}
+
+ListViewItem::ListViewItem(QListViewItem *parent, const QString &t1)
+ : KListViewItem(parent, t1)
+{
+}
+
+int ListViewItem::compare(QListViewItem *i, int col, bool) const
+{
+ if (m_type != static_cast<ListViewItem*>(i)->m_type) {
+ if (m_type == 0)
+ return -1; // category
+ else
+ return 1; // server
+ }
+
+ return KListViewItem::compare(i, col, false);
+}
+
+ListView::ListView(KFTPBookmarks::Manager *bookmarks, QWidget *parent, const char *name)
+ : KFTPWidgets::ListView(parent, name),
+ m_autoUpdate(false),
+ m_connectBookmark(false),
+ m_editMenuItem(false),
+ m_bookmarks(bookmarks),
+ m_activeSite(0),
+ m_activeCategory(0),
+ m_activeItem(0)
+{
+ // Set some stuff
+ setMinimumWidth(150);
+ setFullWidth(true);
+ addColumn(i18n("Bookmarks"));
+ setRootIsDecorated(true);
+ setEmptyListText(i18n("No bookmarks."));
+ setItemsRenameable(false);
+
+ // Drag & drop
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropVisualizer(false);
+
+ // Init auto open timer
+ m_openTimer = new QTimer(this);
+
+ connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), this, SLOT(slotDropped(QDropEvent*, QListViewItem*)));
+ connect(this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*)));
+ connect(this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotDoubleClicked(QListViewItem*)));
+ connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)), this, SLOT(slotContextMenu(QListViewItem*, const QPoint&, int)));
+
+ /* Init the actions */
+ m_newAction = new KAction(i18n("&New..."), "filenew", KShortcut(), this, SLOT(slotNewAction()), actionCollection(), "bookmark_new");
+ m_renameAction = new KAction(i18n("&Rename"), KShortcut(), this, SLOT(slotRenameAction()), actionCollection(), "bookmark_rename");
+ m_deleteAction = new KAction(i18n("&Delete"), "editdelete", KShortcut(), this, SLOT(slotDeleteAction()), actionCollection(), "bookmark_delete");
+ m_subCatAction = new KAction(i18n("&Create Subcategory..."), "folder_new", KShortcut(), this, SLOT(slotSubCatAction()), actionCollection(), "bookmark_subcat");
+ m_copyAction = new KAction(i18n("&Duplicate"), KShortcut(), this, SLOT(slotDuplicateAction()), actionCollection(), "bookmark_duplicate");
+}
+
+KActionCollection *ListView::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+void ListView::setAutoUpdate(bool value)
+{
+ m_autoUpdate = value;
+
+ if (value)
+ connect(m_bookmarks, SIGNAL(update()), this, SLOT(slotAutoUpdate()));
+
+ slotAutoUpdate();
+}
+
+void ListView::setConnectBookmark(bool value)
+{
+ m_connectBookmark = value;
+
+ if (value)
+ connect(this, SIGNAL(executed(QListViewItem*)), this, SLOT(slotBookmarkExecuted(QListViewItem*)));
+}
+
+void ListView::setEditMenuItem(bool value)
+{
+ m_editMenuItem = value;
+}
+
+void ListView::slotBookmarkExecuted(QListViewItem *item)
+{
+ if (!item || !m_connectBookmark || static_cast<ListViewItem*>(item)->m_type == BT_CATEGORY)
+ return;
+
+ KFTPBookmarks::Site *site = static_cast<ListViewItem*>(item)->m_site;
+ KURL siteUrl = site->getUrl();
+
+ // Handle empty usernames and passwords for non-anonymous sites
+ if (!siteUrl.hasUser() || !siteUrl.hasPass() && siteUrl.user() != "anonymous") {
+ KIO::PasswordDialog *dlg = new KIO::PasswordDialog(i18n("Please provide your username and password for connecting to this site."), siteUrl.user(), true);
+ dlg->addCommentLine(i18n("Site:"), QString("%1:%2").arg(siteUrl.host()).arg(siteUrl.port()));
+
+ if (dlg->exec() == KDialogBase::Accepted) {
+ siteUrl.setUser(dlg->username());
+ siteUrl.setPass(dlg->password());
+
+ if (dlg->keepPassword()) {
+ // Save password to the bookmarked site
+ site->setProperty("username", dlg->username());
+ site->setProperty("password", encodePassword(dlg->password()));
+ }
+
+ delete dlg;
+ } else {
+ // Abort connection attempt
+ delete dlg;
+ return;
+ }
+ }
+
+ // Just spawn a new session
+ KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl, site);
+ m_bookmarks->setupClient(site, session->getClient());
+}
+
+void ListView::slotAutoUpdate()
+{
+ if (m_autoUpdate) {
+ // Update the bookmark list
+ fillBookmarkData();
+ }
+}
+
+QDragObject *ListView::dragObject()
+{
+ // Determine hotspot and pixmap
+ QPoint hotspot;
+ QPixmap pixmap = *selectedItem()->pixmap(0);
+ hotspot.setX(pixmap.width() / 2);
+ hotspot.setY(pixmap.height() / 2);
+
+ QString id = static_cast<ListViewItem*>(selectedItem())->m_site->id();
+ m_dragObject = new QStoredDrag("application/x-qlistviewitem", this);
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << id;
+
+ // Start the drag
+ static_cast<QStoredDrag*>(m_dragObject)->setEncodedData(data);
+ m_dragObject->setPixmap(pixmap, hotspot);
+
+ return m_dragObject;
+}
+
+bool ListView::acceptDrag(QDropEvent *e) const
+{
+ // If it is a local drag, accept it
+ return e->source() == static_cast<QWidget*>(const_cast<ListView*>(this));
+}
+
+void ListView::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ if (!acceptDrag(e)) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+
+ m_currentBeforeDropItem = selectedItem();
+ QListViewItem *item = itemAt(contentsToViewport(e->pos()));
+
+ if (item) {
+ m_dropItem = item;
+ m_openTimer->start(750);
+ } else {
+ m_dropItem = 0L;
+ }
+}
+
+void ListView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (!acceptDrag(e)) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+
+ QListViewItem *item = itemAt(contentsToViewport(e->pos()));
+ if (item) {
+ setSelected(item, true);
+ if (item != m_dropItem) {
+ m_openTimer->stop();
+ m_dropItem = item;
+ m_openTimer->start(750);
+ }
+ } else {
+ if (selectedItem()) {
+ setSelected(selectedItem(), false);
+ }
+
+ m_openTimer->stop();
+ m_dropItem = 0L;
+ }
+}
+
+void ListView::contentsDragLeaveEvent(QDragLeaveEvent*)
+{
+ if (m_currentBeforeDropItem) {
+ setSelected(m_currentBeforeDropItem, true);
+ ensureItemVisible(m_currentBeforeDropItem);
+ } else {
+ setSelected(m_dropItem, false);
+ }
+
+ m_openTimer->stop();
+ m_dropItem = 0L;
+}
+
+void ListView::slotOpenTimer()
+{
+ m_openTimer->stop();
+ if (m_dropItem && m_dropItem->isExpandable()) {
+ // Expand the category
+ m_dropItem->setOpen(true);
+ }
+}
+
+void ListView::slotDropped(QDropEvent *e, QListViewItem*)
+{
+ KFTPBookmarks::Site *parentSite = 0L;
+
+ if (selectedItem()) {
+ QListViewItem *newParent = 0L;
+
+ // Get the new path
+ if (static_cast<ListViewItem*>(selectedItem())->m_type == 0)
+ newParent = selectedItem();
+ else
+ newParent = selectedItem()->parent();
+
+ if (newParent)
+ parentSite = static_cast<ListViewItem*>(newParent)->m_site;
+ else
+ parentSite = m_bookmarks->findCategory("root");
+ } else {
+ parentSite = m_bookmarks->findCategory("root");
+ }
+
+ QString id;
+
+ // Decode the data
+ QDataStream arg(e->encodedData("application/x-qlistviewitem"), IO_ReadOnly);
+ arg >> id;
+
+ // Move the site
+ KFTPBookmarks::Site *originalSite = m_bookmarks->findSite(id);
+ parentSite->reparentSite(originalSite);
+
+ emit bookmarkMoved();
+
+ // Notify the bookmark manager
+ m_bookmarks->emitUpdate();
+}
+
+void ListView::fillBookmarkData()
+{
+ // Fill the tree with data
+ m_bookmarks->guiPopulateBookmarksTree(this);
+}
+
+void ListView::slotClicked(QListViewItem *item)
+{
+ m_activeItem = static_cast<ListViewItem*>(item);
+
+ if (m_activeItem) {
+ if (m_activeItem->m_type == BT_CATEGORY) {
+ // Category
+ m_activeCategory = m_activeItem->m_site;
+ m_activeSite = 0L;
+ } else {
+ // Server
+ m_activeSite = m_activeItem->m_site;
+ m_activeCategory = m_activeSite->getParentSite();
+ }
+ }
+
+ emit bookmarkClicked(item);
+}
+
+void ListView::slotDoubleClicked(QListViewItem *item)
+{
+ if (!item)
+ return;
+
+ setOpen(item, !item->isOpen());
+}
+
+KFTPBookmarks::Manager *ListView::getBookmarks()
+{
+ return m_bookmarks;
+}
+
+void ListView::slotRenameAction()
+{
+ // Rename a subcategory
+ ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
+ KFTPBookmarks::Site *site = item->m_site;
+
+ bool ok;
+ QString newName = KInputDialog::getText(i18n("Category Name"), i18n("Rename category:"), item->text(0), &ok, this);
+
+ if (ok) {
+ // Ok, let's rename it
+ site->setAttribute("name", newName);
+ item->setText(0, newName);
+ }
+}
+
+void ListView::slotSubCatAction()
+{
+ // Create a subcategory
+ ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
+ KFTPBookmarks::Site *site = item ? item->m_site : m_bookmarks->findCategory("root");
+
+ bool ok;
+ QString newName = KInputDialog::getText(i18n("New Category Name"), i18n("New category:"), "", &ok, this);
+
+ if (ok) {
+ // Let's create the sub category
+ site->addCategory(newName);
+
+ emit categoryRenamed();
+ m_bookmarks->emitUpdate();
+ }
+}
+
+void ListView::slotNewAction()
+{
+ if (!m_activeCategory) {
+ // Set the starting category to document root
+ m_activeCategory = m_bookmarks->findCategory("root");
+ }
+
+ // Create the new node
+ KFTPBookmarks::Site *site = m_activeCategory->addSite();
+ site->setAttribute("name", i18n("New server"));
+ site->setProperty("port", 21);
+
+ // Create the ListViewItem
+ ListViewItem *serv = 0L;
+ if (!m_activeItem || (!m_activeItem->parent() && m_activeItem->m_type == BT_SERVER)) {
+ serv = new ListViewItem(this, i18n("New Server"));
+ } else if (m_activeItem->m_type == BT_CATEGORY) {
+ serv = new ListViewItem(m_activeItem, i18n("New Server"));
+ m_activeItem->setOpen(true);
+ } else {
+ serv = new ListViewItem(m_activeItem->parent(), i18n("New Server"));
+ }
+
+ serv->m_type = BT_SERVER;
+ serv->m_site = site;
+
+ serv->setPixmap(0, loadSmallPixmap("ftp"));
+
+ // Set the new ListViewItem as active
+ setSelected(serv, true);
+ m_activeItem = serv;
+
+ // Display its properties
+ m_activeSite = site;
+
+ emit bookmarkNew(serv, site);
+}
+
+void ListView::slotDuplicateAction()
+{
+ // Copy a bookmark
+ ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
+
+ // Clone the node
+ KFTPBookmarks::Site *copy = item->m_site->duplicate();
+
+ // Create the ListViewItem
+ ListViewItem *serv = 0L;
+ if (item->parent())
+ serv = new ListViewItem(item->parent(), copy->getAttribute("name"));
+ else
+ serv = new ListViewItem(this, copy->getAttribute("name"));
+
+ serv->m_type = BT_SERVER;
+ serv->m_site = copy;
+
+ serv->setPixmap(0, loadSmallPixmap("ftp"));
+
+ // Set the new ListViewItem as active
+ setSelected(serv, true);
+ m_activeItem = serv;
+
+ // Display its properties
+ m_activeSite = copy;
+
+ emit bookmarkDuplicated(serv, copy);
+
+ // Update the bookmarks
+ m_bookmarks->emitUpdate();
+}
+
+void ListView::slotDeleteAction()
+{
+ // Delete a server or a category
+ ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
+
+ // What do we have here ?
+ if (item->m_type == BT_CATEGORY) {
+ // Category
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove this category?")) == KMessageBox::Yes) {
+ m_bookmarks->delSite(item->m_site);
+ } else {
+ return;
+ }
+ } else {
+ // Server
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove this server?")) == KMessageBox::Yes) {
+ m_bookmarks->delSite(item->m_site);
+ } else {
+ return;
+ }
+ }
+
+ if ( !m_autoUpdate )
+ delete item;
+
+ m_activeItem = 0L;
+ emit bookmarkDeleted();
+}
+
+void ListView::slotContextMenu(QListViewItem *item, const QPoint &pos, int)
+{
+ slotClicked(item);
+
+ KPopupMenu *context = new KPopupMenu(this);
+
+ if (item) {
+ context->insertTitle(item->text(0));
+
+ // Server or category
+ if (static_cast<ListViewItem*>(item)->m_type == BT_CATEGORY) {
+ m_newAction->plug(context);
+ m_deleteAction->plug(context);
+ m_subCatAction->plug(context);
+ m_renameAction->plug(context);
+ } else {
+ if (m_editMenuItem)
+ actionCollection()->action("bookmark_edit2")->plug(context);
+
+ m_newAction->plug(context);
+ m_copyAction->plug(context);
+ m_deleteAction->plug(context);
+ }
+ } else {
+ // Nothing selected
+ m_newAction->plug(context);
+ m_subCatAction->plug(context);
+ }
+
+ context->exec(pos);
+
+ delete context;
+}
+
+}
+
+}
+
+#include "listview.moc"
diff --git a/kftpgrabber/src/widgets/bookmarks/listview.h b/kftpgrabber/src/widgets/bookmarks/listview.h
new file mode 100644
index 0000000..213ec03
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/listview.h
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKEDITORLISTVIEW_H
+#define KFTPBOOKMARKEDITORLISTVIEW_H
+
+#include <qdom.h>
+#include <qtimer.h>
+
+#include "../listview.h"
+
+class KAction;
+class KActionCollection;
+class Sidebar;
+class KFTPSelectServerDialog;
+
+namespace KFTPBookmarks {
+ class Site;
+ class Manager;
+ class BookmarkEditor;
+ class Sidebar;
+}
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+class ListView;
+
+class ListViewItem : public KListViewItem
+{
+friend class BookmarkEditor;
+friend class KFTPWidgets::Bookmarks::ListView;
+friend class Sidebar;
+friend class ::KFTPSelectServerDialog;
+public:
+ ListViewItem(QListView *parent, const QString &t1);
+ ListViewItem(QListViewItem *parent, const QString &t1);
+
+ int compare(QListViewItem *i, int col, bool) const;
+
+ void setSite(KFTPBookmarks::Site *site) { m_site = site; }
+ void setType(int type) { m_type = type; }
+private:
+ int m_type;
+ KFTPBookmarks::Site *m_site;
+};
+
+/**
+@author Jernej Kos
+*/
+class ListView : public KFTPWidgets::ListView
+{
+Q_OBJECT
+public:
+ ListView(KFTPBookmarks::Manager *bookmarks, QWidget *parent = 0, const char *name = 0);
+
+ /**
+ * Fill current treeview with all the bookmarks.
+ */
+ void fillBookmarkData();
+
+ /**
+ * This changes the auto-update behaviour of the ListView. If set to true, this
+ * list view will automaticly update when bookmarks are changed. Defaults
+ * to false.
+ *
+ * @param value True to enable auto update, false otherwise
+ */
+ void setAutoUpdate(bool value);
+
+ /**
+ * This changes the connect-on-click behaviour of the ListView. If set to true,
+ * a new remote session will be spawned whenever the users double clicks the
+ * bookmark.
+ *
+ * @param value True to enable connect on click, false otherwise
+ */
+ void setConnectBookmark(bool value);
+
+ /**
+ * Get the bookmarks, the listview operates on
+ */
+ KFTPBookmarks::Manager *getBookmarks();
+
+ /**
+ * Specifiy, whether an edit menuitem should be placed in the contextmenu
+ *
+ * @param value True to enable the edit menuitem, false otherwise
+ */
+ void setEditMenuItem(bool value);
+
+private:
+ enum BookmarkType {
+ BT_CATEGORY = 0,
+ BT_SERVER
+ };
+
+ bool m_autoUpdate;
+ bool m_connectBookmark;
+ bool m_editMenuItem;
+ QDragObject *m_dragObject;
+
+ /* Actions */
+ KAction *m_newAction;
+ KAction *m_renameAction;
+ KAction *m_deleteAction;
+ KAction *m_subCatAction;
+ KAction *m_copyAction;
+
+ /* Auto open categories support */
+ QListViewItem *m_dropItem;
+ QListViewItem* m_currentBeforeDropItem;
+ QTimer *m_openTimer;
+
+ /* Specifies, on which bookmarks we operate */
+ KFTPBookmarks::Manager *m_bookmarks;
+
+ KFTPBookmarks::Site *m_activeSite;
+ KFTPBookmarks::Site *m_activeCategory;
+ KFTPWidgets::Bookmarks::ListViewItem *m_activeItem;
+
+ KActionCollection *actionCollection();
+
+ /* Drag & drop support */
+ QDragObject *dragObject();
+ bool acceptDrag(QDropEvent *e) const;
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent*);
+private slots:
+ void slotDropped(QDropEvent *e, QListViewItem*);
+ void slotDoubleClicked(QListViewItem *item);
+ void slotOpenTimer();
+
+ void slotClicked(QListViewItem*);
+ void slotNewAction();
+ void slotRenameAction();
+ void slotDeleteAction();
+ void slotSubCatAction();
+ void slotDuplicateAction();
+ void slotContextMenu(QListViewItem*, const QPoint&, int col);
+
+ void slotAutoUpdate();
+ void slotBookmarkExecuted(QListViewItem*);
+signals:
+ void bookmarkMoved();
+ void bookmarkNew(ListViewItem*, KFTPBookmarks::Site*);
+ void bookmarkDeleted();
+ void bookmarkDuplicated(ListViewItem*, KFTPBookmarks::Site*);
+ void bookmarkClicked(QListViewItem*);
+
+ void categoryRenamed();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/bookmarks/sidebar.cpp b/kftpgrabber/src/widgets/bookmarks/sidebar.cpp
new file mode 100644
index 0000000..21ee0d9
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/sidebar.cpp
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <qlayout.h>
+#include <qsplitter.h>
+#include <qvbox.h>
+
+#include <kaction.h>
+#include <klocale.h>
+#include <ktoolbar.h>
+
+#include "kftpapi.h"
+#include "kftpbookmarks.h"
+#include "editor.h"
+#include "listview.h"
+#include "misc.h"
+
+#include "sidebar.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+KActionCollection *Sidebar::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+Sidebar::Sidebar(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ m_toolBar = new KToolBar(this, "bookmarkToolBar");
+ m_toolBar->setIconSize(16);
+ layout->addWidget(m_toolBar);
+
+ // Create the list view for editing bookmarks
+ m_tree = new ListView(KFTPBookmarks::Manager::self(), this);
+ m_tree->setAutoUpdate(true);
+ m_tree->setConnectBookmark(true);
+ m_tree->setEditMenuItem(true);
+
+ layout->addWidget(m_tree);
+
+ m_editAction = new KAction(i18n("&Edit..."), "edit", KShortcut(), this, SLOT(slotEditAction()), actionCollection(), "bookmark_edit2");
+ connect(m_tree, SIGNAL(bookmarkClicked(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*)));
+ connect(m_tree, SIGNAL(bookmarkNew(ListViewItem*, KFTPBookmarks::Site*)), this, SLOT(slotNewAction(ListViewItem*, KFTPBookmarks::Site*)));
+
+ // Get the new bookmark data
+ m_tree->fillBookmarkData();
+
+ // Init the Actions
+ slotClicked(0L);
+
+ setMinimumWidth(200);
+}
+
+Sidebar::~Sidebar()
+{
+}
+
+void Sidebar::refresh()
+{
+ m_tree->clear();
+ m_tree->fillBookmarkData();
+}
+
+void Sidebar::slotEditAction()
+{
+ ListViewItem* item = static_cast<ListViewItem*>(m_tree->selectedItems().at(0));
+
+ if (item) {
+ BookmarkEditor *editor = new BookmarkEditor(item, this);
+
+ editor->exec();
+ delete editor;
+
+ // Update the bookmarks globaly
+ KFTPBookmarks::Manager::self()->emitUpdate();
+ }
+}
+
+void Sidebar::slotNewAction(ListViewItem*, KFTPBookmarks::Site *site)
+{
+ BookmarkEditor *editor = new BookmarkEditor(static_cast<ListViewItem*>(m_tree->selectedItems().at(0)), this);
+
+ if (!editor->exec()) {
+ // If the user clicks Abort, remove the newly created server
+ KFTPBookmarks::Manager::self()->delSite(site);
+ }
+
+ delete editor;
+}
+
+void Sidebar::slotClicked(QListViewItem *item)
+{
+ // When nodes are expanded, item is 0, although an item is still selected, so grab it here
+ item = m_tree->selectedItems().at(0);
+
+ // Enable/Disable actions for the toolbar
+ if (!item) {
+ actionCollection()->action("bookmark_delete")->setEnabled(false);
+ actionCollection()->action("bookmark_subcat")->setEnabled(true);
+ m_editAction->setEnabled(false);
+ return;
+ }
+
+ actionCollection()->action("bookmark_delete")->setEnabled(true);
+
+ if (static_cast<ListViewItem*>(item)->m_type == BT_CATEGORY) {
+ m_editAction->setEnabled(false);
+ actionCollection()->action("bookmark_subcat")->setEnabled(true);
+ } else {
+ m_editAction->setEnabled(true);
+ actionCollection()->action("bookmark_subcat")->setEnabled(false);
+ }
+}
+
+}
+
+}
+
+#include "sidebar.moc"
diff --git a/kftpgrabber/src/widgets/bookmarks/sidebar.h b/kftpgrabber/src/widgets/bookmarks/sidebar.h
new file mode 100644
index 0000000..ef08d3e
--- /dev/null
+++ b/kftpgrabber/src/widgets/bookmarks/sidebar.h
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPBOOKMARKSIDEBAR_H
+#define KFTPBOOKMARKSIDEBAR_H
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+class KAction;
+class KToolBar;
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+
+class ListViewItem;
+class ListView;
+
+/**
+@author Markus Brueffer
+*/
+class Sidebar : public QWidget
+{
+Q_OBJECT
+public:
+ Sidebar(QWidget *parent = 0, const char *name = 0);
+ ~Sidebar();
+
+private:
+ enum BookmarkType {
+ BT_CATEGORY = 0,
+ BT_SERVER
+ };
+
+ KToolBar *m_toolBar;
+ ListView *m_tree;
+
+ KAction *m_editAction;
+ KActionCollection *actionCollection();
+public slots:
+ void refresh();
+private slots:
+ void slotClicked(QListViewItem*);
+
+ /* Action slots */
+ void slotEditAction();
+ void slotNewAction(ListViewItem *item, KFTPBookmarks::Site *site);
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/Makefile.am b/kftpgrabber/src/widgets/browser/Makefile.am
new file mode 100644
index 0000000..2b9c987
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I$(srcdir)/../.. -I../.. \
+ -I$(srcdir)/../../engine \
+ -I$(srcdir)/../../misc \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../../ui -I../../ui \
+ $(all_includes)
+
+METASOURCES = AUTO
+noinst_LIBRARIES = libbrowser.a
+noinst_HEADERS = view.h actions.h treeview.h propsplugin.h \
+ filterwidget.h detailsview.h locationnavigator.h dirlister.h
+libbrowser_a_SOURCES = view.cpp actions.cpp treeview.cpp \
+ propsplugin.cpp filterwidget.cpp detailsview.cpp locationnavigator.cpp dirlister.cpp
diff --git a/kftpgrabber/src/widgets/browser/actions.cpp b/kftpgrabber/src/widgets/browser/actions.cpp
new file mode 100644
index 0000000..cc9fc8c
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/actions.cpp
@@ -0,0 +1,764 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "browser/actions.h"
+#include "browser/view.h"
+#include "browser/detailsview.h"
+#include "browser/propsplugin.h"
+#include "browser/filterwidget.h"
+
+#include "widgets/quickconnect.h"
+#include "widgets/popupmessage.h"
+
+#include "kftpbookmarks.h"
+#include "kftpqueue.h"
+#include "kftpapi.h"
+#include "kftpsession.h"
+#include "verifier.h"
+#include "mainactions.h"
+
+#include "misc.h"
+#include "misc/config.h"
+#include "misc/filter.h"
+#include "misc/customcommands/manager.h"
+
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kapplication.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kinputdialog.h>
+#include <kpropertiesdialog.h>
+#include <kio/job.h>
+#include <kshred.h>
+#include <klineedit.h>
+#include <kfiledialog.h>
+#include <kstandarddirs.h>
+
+#include <qdir.h>
+#include <qclipboard.h>
+
+using namespace KFTPGrabberBase;
+using namespace KFTPEngine;
+using namespace KFTPCore;
+using namespace KFTPCore::Filter;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+Actions::Actions(View *parent)
+ : QObject(parent),
+ m_view(parent)
+{
+}
+
+void Actions::initActions()
+{
+ m_actionCollection = new KActionCollection(m_view, this);
+
+ // Create all the actions
+ m_goUpAction = KStdAction::up(this, SLOT(slotGoUp()), m_actionCollection, "go_up");
+ m_goBackAction = KStdAction::back(this, SLOT(slotGoBack()), m_actionCollection, "go_back");
+ m_goForwardAction = KStdAction::forward(this, SLOT(slotGoForward()), m_actionCollection, "go_forward");
+ m_goHomeAction = KStdAction::home(this, SLOT(slotGoHome()), m_actionCollection, "go_home");
+
+ m_reloadAction = KStdAction::redisplay(this, SLOT(slotReload()), m_actionCollection, "reload");
+ m_reloadAction->setText(i18n("&Reload"));
+ m_reloadAction->setShortcut(KShortcut(Qt::Key_F5));
+
+ m_abortAction = new KAction(i18n("&Abort"), "stop", KShortcut(), this, SLOT(slotAbort()), m_actionCollection, "abort");
+ m_toggleTreeViewAction = new KToggleAction(i18n("&Show Tree View"), "view_tree", KShortcut(), this, SLOT(slotShowHideTree()), m_actionCollection, "toggle_tree_view");
+ m_toggleFilterAction = new KToggleAction(i18n("Show &Filter"), "filter", KShortcut(), this, SLOT(slotShowHideFilter()), m_actionCollection, "toggle_filter");
+
+ m_renameAction = new KAction(i18n("&Rename"), KShortcut(Qt::Key_F2), this, SLOT(slotRename()), m_actionCollection, "edit_rename");
+ m_deleteAction = new KAction(i18n("&Delete"), "editdelete", KShortcut(Qt::Key_Delete), this, SLOT(slotDelete()), m_actionCollection, "edit_delete");
+ m_propsAction = new KAction(i18n("&Properties"), KShortcut(), this, SLOT(slotProps()), m_actionCollection, "edit_properties");
+ m_shredAction = new KAction(i18n("&Shred"), "editshred", KShortcut(), this, SLOT(slotShred()), m_actionCollection, "edit_shred");
+
+ m_copyAction = KStdAction::copy(this, SLOT(slotCopy()), m_actionCollection, "edit_copy");
+ m_pasteAction = KStdAction::paste(this, SLOT(slotPaste()), m_actionCollection, "edit_paste");
+
+ m_filterActions = new KActionMenu(i18n("&Filter Options"), "", m_actionCollection, "edit_filter_options");
+ m_alwaysSkipAction = new KAction(i18n("Always &skip this file when queuing"), KShortcut(), this, SLOT(slotAlwaysSkip()), m_actionCollection);
+ m_topPriorityAction = new KAction(i18n("Make this file &top priority"), KShortcut(), this, SLOT(slotTopPriority()), m_actionCollection);
+ m_lowPriorityAction = new KAction(i18n("Make this file &lowest priority"), KShortcut(), this, SLOT(slotLowPriority()), m_actionCollection);
+
+ m_filterActions->insert(m_alwaysSkipAction);
+ m_filterActions->insert(m_topPriorityAction);
+ m_filterActions->insert(m_lowPriorityAction);
+
+ m_transferAction = new KAction(i18n("&Transfer"), KShortcut(), this, SLOT(slotTransfer()), m_actionCollection, "transfer");
+ m_queueTransferAction = new KAction(i18n("&Queue Transfer"), "queue", KShortcut(), this, SLOT(slotQueueTransfer()), m_actionCollection, "queue_transfer");
+ m_createDirAction = new KAction(i18n("&Create Directory..."), "folder_new", KShortcut(), this, SLOT(slotCreateDir()), m_actionCollection, "create_dir");
+ m_fileEditAction = new KAction(i18n("&Open file"), "fileopen", KShortcut(), this, SLOT(slotFileEdit()), m_actionCollection, "open_file");
+ m_verifyAction = new KAction(i18n("&Verify..."), "ok", KShortcut(), this, SLOT(slotVerify()), m_actionCollection, "verify");
+
+ populateEncodings();
+
+ m_moreActions = new KActionMenu(i18n("&More Actions"), "configure", this);
+ m_rawCmdAction = new KAction(i18n("&Manual Command Entry..."), "openterm", KShortcut(), this, SLOT(slotRawCmd()), m_actionCollection, "send_raw_cmd");
+ m_exportListingAction = new KAction(i18n("&Export Directory Listing..."), "", KShortcut(), this, SLOT(slotExportListing()), m_actionCollection, "export_listing");
+ m_showHiddenFilesAction = new KToggleAction(i18n("Show &Hidden Files && Directories"), KShortcut(), this, SLOT(slotShowHiddenFiles()), m_actionCollection, "show_hidden");
+ m_openExternalAction = new KAction(i18n("Open current directory in &Konqueror..."), "konqueror", KShortcut(), this, SLOT(slotOpenExternal()), m_actionCollection, "open_konqi");
+
+ m_markItemsAction = new KAction(i18n("Compare &selected items"), "", KShortcut(Qt::Key_Space), this, SLOT(slotMarkItems()), m_actionCollection, "compare_selected");
+ m_compareAction = new KAction(i18n("Compare &directories"), "", KShortcut(), this, SLOT(slotCompare()), m_actionCollection, "compare_dirs");
+
+ m_showHiddenFilesAction->setChecked(KFTPCore::Config::showHiddenFiles());
+
+ m_rawCommandsMenu = CustomCommands::Manager::self()->categories(i18n("Send &Raw Command"), m_view->getSession());
+ m_rawCommandsMenu->insert(m_rawCmdAction, 0);
+ m_rawCommandsMenu->popupMenu()->insertSeparator(1);
+
+ m_moreActions->insert(m_rawCommandsMenu);
+ m_moreActions->insert(m_changeEncodingAction);
+ m_moreActions->popupMenu()->insertSeparator();
+ m_moreActions->insert(m_exportListingAction);
+ m_moreActions->insert(m_openExternalAction);
+ m_moreActions->insert(m_markItemsAction);
+ m_moreActions->insert(m_compareAction);
+ m_moreActions->popupMenu()->insertSeparator();
+ m_moreActions->insert(m_showHiddenFilesAction);
+
+ m_moreActions->setStickyMenu(true);
+ m_moreActions->setDelayed(false);
+
+ m_siteChangeAction = new KActionMenu(i18n("&Change Site"), "goto", this);
+ m_quickConnectAction = new KAction(i18n("&Quick Connect..."), "connect_creating", KShortcut(), this, SLOT(slotQuickConnect()), m_actionCollection, "quick_connect");
+ m_connectAction = new KActionMenu(i18n("&Connect To"), this);
+ m_disconnectAction = new KAction(i18n("&Disconnect"), "connect_no", KShortcut(), this, SLOT(slotDisconnect()), m_actionCollection, "disconnect");
+
+ m_siteChangeAction->insert(m_quickConnectAction);
+ m_siteChangeAction->insert(m_connectAction);
+ m_siteChangeAction->insert(m_disconnectAction);
+ m_siteChangeAction->setStickyMenu(true);
+ m_siteChangeAction->setDelayed(false);
+
+ // Populate bookmarks
+ KFTPBookmarks::Manager::self()->guiPopulateBookmarksMenu(m_connectAction, QDomNode(), false, m_view->m_session);
+}
+
+void Actions::populateEncodings()
+{
+ // Charsets
+ m_changeEncodingAction = new KActionMenu(i18n("Change Remote &Encoding"), "charset", m_actionCollection, "changeremoteencoding");
+ m_changeEncodingAction->setDelayed(false);
+
+ KPopupMenu *menu = m_changeEncodingAction->popupMenu();
+ menu->clear();
+
+ QStringList charsets = KGlobal::charsets()->descriptiveEncodingNames();
+ int count = 0;
+ for (QStringList::iterator i = charsets.begin(); i != charsets.end(); ++i)
+ menu->insertItem(*i, this, SLOT(slotCharsetChanged(int)), 0, ++count);
+
+ menu->insertSeparator();
+ menu->insertItem(i18n("Default"), this, SLOT(slotCharsetReset(int)), 0, ++count);
+ menu->setItemChecked(count, true);
+
+ m_defaultCharsetOption = count;
+ m_curCharsetOption = count;
+}
+
+void Actions::updateActions()
+{
+ m_goUpAction->setEnabled(m_view->url().upURL() != m_view->url());
+
+ // History
+ int index = 0;
+ const QValueList<LocationNavigator::Element> list = m_view->history(index);
+
+ m_goBackAction->setEnabled(index < static_cast<int>(list.count()) - 1);
+ m_goForwardAction->setEnabled(index > 0);
+
+ m_abortAction->setEnabled(m_view->m_ftpClient->socket()->isBusy());
+ m_toggleTreeViewAction->setEnabled(true);
+ m_toggleFilterAction->setEnabled(true);
+
+ m_quickConnectAction->setEnabled(m_view->url().isLocalFile());
+ m_connectAction->setEnabled(true);
+ m_disconnectAction->setEnabled(m_view->m_ftpClient->socket()->isConnected());
+
+ const KFileItemList *selectedItems = m_view->selectedItems();
+
+ if (selectedItems->count() == 1) {
+ m_fileEditAction->setEnabled(!selectedItems->getFirst()->isDir());
+ m_verifyAction->setEnabled(selectedItems->getFirst()->isLocalFile() && selectedItems->getFirst()->name(true).right(3) == "sfv");
+ } else {
+ m_fileEditAction->setEnabled(false);
+ m_verifyAction->setEnabled(false);
+ }
+
+ // Check if we can transfer anything
+ KFTPSession::Session *session = m_view->m_session;
+ KFTPSession::Session *opposite = KFTPSession::Manager::self()->getActive(oppositeSide(m_view->m_session->getSide()));
+
+ m_renameAction->setEnabled(session->isConnected());
+ m_deleteAction->setEnabled(session->isConnected());
+ m_propsAction->setEnabled(true);
+ m_shredAction->setEnabled(!session->isRemote());
+ m_copyAction->setEnabled(true);
+ m_pasteAction->setEnabled(true);
+
+ if ((!session->isRemote() && !opposite->isRemote()) ||
+ (
+ (session->isRemote() && opposite->isRemote()) &&
+ (
+ session->getClient()->socket()->protocolName() != opposite->getClient()->socket()->protocolName() ||
+ !(session->getClient()->socket()->features() & SF_FXP_TRANSFER)
+ )
+ )
+ ) {
+ m_queueTransferAction->setEnabled(false);
+ m_transferAction->setEnabled(false);
+ } else {
+ m_queueTransferAction->setEnabled(true);
+ m_transferAction->setEnabled(true);
+ }
+
+ if (!session->isRemote() || session->getClient()->socket()->isConnected())
+ m_createDirAction->setEnabled(true);
+ else
+ m_createDirAction->setEnabled(false);
+
+ m_changeEncodingAction->setEnabled(session->isRemote());
+ m_rawCmdAction->setEnabled(!m_view->url().isLocalFile() && m_view->m_ftpClient->socket()->features() & SF_RAW_COMMAND);
+ m_rawCommandsMenu->setEnabled(m_rawCmdAction->isEnabled());
+ m_openExternalAction->setEnabled(!session->isRemote());
+}
+
+void Actions::slotGoUp()
+{
+ m_view->goUp();
+}
+
+void Actions::slotGoBack()
+{
+ m_view->goBack();
+}
+
+void Actions::slotGoForward()
+{
+ m_view->goForward();
+}
+
+void Actions::slotReload()
+{
+ m_view->reload();
+}
+
+void Actions::slotGoHome()
+{
+ m_view->goHome();
+}
+
+void Actions::slotQuickConnect()
+{
+ // Create/get the new dialog
+ QuickConnectDialog *quickConnect = new QuickConnectDialog(m_view);
+
+ if (quickConnect->exec() == KDialogBase::Accepted) {
+ // Get the url and connect
+ if (m_view->m_ftpClient->socket()->isConnected()) {
+ if (KFTPCore::Config::confirmDisconnects() && KMessageBox::warningYesNo(0, i18n("Do you want to drop current connection?")) == KMessageBox::No)
+ return;
+
+ m_view->m_session->disconnectAllConnections();
+ }
+
+ m_view->m_session->setSite(0);
+
+ quickConnect->setupClient(m_view->m_ftpClient);
+ m_view->m_ftpClient->connect(quickConnect->getUrl());
+ }
+
+ delete quickConnect;
+}
+
+void Actions::slotDisconnect()
+{
+ if (m_view->m_ftpClient->socket()->isConnected()) {
+ if (KFTPCore::Config::confirmDisconnects() && KMessageBox::warningYesNo(0, i18n("Do you want to drop current connection?")) == KMessageBox::No)
+ return;
+
+ m_view->m_session->disconnectAllConnections();
+ }
+}
+
+void Actions::slotShred()
+{
+ // Shred the file
+ if (KMessageBox::warningContinueCancel(0, i18n("Are you sure you want to SHRED this file?"), i18n("Shred File"),KGuiItem(i18n("&Shred"), "editshred")) == KMessageBox::Cancel)
+ return;
+
+ KShred::shred(m_view->selectedItems()->getFirst()->url().path());
+}
+
+void Actions::slotRename()
+{
+ KFTPWidgets::Browser::DetailsView *view = m_view->getDetailsView();
+
+ // Rename the first file in the current selection
+ view->rename(view->KListView::selectedItems().at(0), 0);
+
+ // Enhanced rename: Don't highlight the file extension. (from Konqueror)
+ KLineEdit *le = view->renameLineEdit();
+
+ if (le) {
+ const QString txt = le->text();
+ QString pattern;
+ KMimeType::diagnoseFileName(txt, pattern);
+
+ if (!pattern.isEmpty() && pattern.at(0) == '*' && pattern.find('*',1) == -1)
+ le->setSelection(0, txt.length()-pattern.stripWhiteSpace().length()+1);
+ else {
+ int lastDot = txt.findRev('.');
+
+ if (lastDot > 0)
+ le->setSelection(0, lastDot);
+ }
+ }
+}
+
+void Actions::slotDelete()
+{
+ KFTPSession::Session *session = m_view->getSession();
+
+ // Delete a file or directory
+ KURL::List selection = m_view->selectedURLs();
+ KURL::List::ConstIterator i = selection.begin();
+ QStringList prettyList;
+ for (; i != selection.end(); ++i) {
+ prettyList.append((*i).pathOrURL());
+ }
+
+ if (KMessageBox::warningContinueCancelList(0,
+ i18n("Do you really want to delete this item?", "Do you really want to delete these %n items?", prettyList.count()),
+ prettyList,
+ i18n("Delete Files"),
+ KStdGuiItem::del(),
+ QString::null,
+ KMessageBox::Dangerous) == KMessageBox::Cancel)
+ return;
+
+ // Go trough all files and delete them
+ if (!session->isRemote()) {
+ KIO::del(selection);
+ } else {
+ KURL::List::Iterator end(selection.end());
+
+ for (KURL::List::Iterator i(selection.begin()); i != end; ++i) {
+ if (!(*i).isLocalFile())
+ session->getClient()->remove(KURL((*i).url()));
+ }
+ }
+}
+
+void Actions::slotCopy()
+{
+ QClipboard *cb = QApplication::clipboard();
+ cb->setData(m_view->getDetailsView()->dragObject(), QClipboard::Clipboard);
+}
+
+void Actions::slotPaste()
+{
+ // Decode the data and try to init transfer
+ KIO::MetaData p_meta;
+ KURL::List p_urls;
+
+ if (KURLDrag::decode(QApplication::clipboard()->data(), p_urls, p_meta)) {
+ // Add destination url and call the QueueManager
+ p_meta.insert("DestURL", m_view->url().url());
+ KURLDrag *drag = new KURLDrag(p_urls, p_meta, m_view, name());
+ KFTPQueue::Manager::self()->insertTransfer(drag);
+ }
+}
+
+void Actions::slotProps()
+{
+ // Show file properties
+ const KFileItemList *selectedItems = m_view->selectedItems();
+ KFileItem *item = selectedItems->getFirst();
+
+ if (selectedItems->count() == 0) {
+ if (m_view->url().isLocalFile())
+ item = new KFileItem(m_view->url(), 0, 0);
+ else
+ return;
+ }
+
+ // Show the dialog
+ KPropertiesDialog *propsDialog;
+
+ if (item->isLocalFile()) {
+ if (selectedItems->count() == 0)
+ propsDialog = new KPropertiesDialog(item);
+ else
+ propsDialog = new KPropertiesDialog(*selectedItems);
+ } else {
+ propsDialog = new KPropertiesDialog(item->name());
+ propsDialog->insertPlugin(new KFTPWidgets::Browser::PropsPlugin(propsDialog, *selectedItems));
+ propsDialog->insertPlugin(new KFTPWidgets::Browser::PermissionsPropsPlugin(propsDialog, *selectedItems, m_view->getSession()));
+ }
+
+ propsDialog->exec();
+}
+
+void Actions::addPriorityItems(int priority)
+{
+ // Add the files to skiplist
+ KURL::List selection = m_view->selectedURLs();
+ KURL::List::Iterator end(selection.end());
+
+ for (KURL::List::Iterator i(selection.begin()); i != end; ++i) {
+ Rule *rule = new Rule();
+
+ if (priority == 0) {
+ rule->setName(i18n("Skip '%1'").arg((*i).filename()));
+ const_cast<ConditionChain*>(rule->conditions())->append(new Condition(Filename, Condition::Is, (*i).filename()));
+ const_cast<ActionChain*>(rule->actions())->append(new Action(Action::Skip, QVariant()));
+ } else {
+ rule->setName(i18n("Priority '%1'").arg((*i).filename()));
+ const_cast<ConditionChain*>(rule->conditions())->append(new Condition(Filename, Condition::Is, (*i).filename()));
+ const_cast<ActionChain*>(rule->actions())->append(new Action(Action::Priority, priority));
+ }
+
+ Filters::self()->append(rule);
+ }
+}
+
+void Actions::slotAlwaysSkip()
+{
+ addPriorityItems(0);
+}
+
+void Actions::slotTopPriority()
+{
+ addPriorityItems(1);
+}
+
+void Actions::slotLowPriority()
+{
+ addPriorityItems(-1);
+}
+
+void Actions::slotTransfer()
+{
+ // Queue a transfer
+ KFileItemList list(*m_view->selectedItems());
+ KFileItemListIterator i(list);
+ KFileItem *item;
+ KFTPSession::Session *opposite = KFTPSession::Manager::self()->getActive(oppositeSide(m_view->m_session->getSide()));
+ KFTPQueue::Transfer *transfer = 0L;
+
+ while ((item = i.current()) != 0) {
+ KURL destinationUrl = opposite->getFileView()->url();
+ destinationUrl.addPath(item->name());
+
+ transfer = KFTPQueue::Manager::self()->spawnTransfer(
+ item->url(),
+ destinationUrl,
+ item->size(),
+ item->isDir(),
+ list.count() == 1,
+ true,
+ 0L,
+ true
+ );
+
+ ++i;
+ }
+
+ // Execute transfer
+ if (transfer)
+ static_cast<KFTPQueue::Site*>(transfer->parentObject())->delayedExecute();
+}
+
+void Actions::slotQueueTransfer()
+{
+ // Queue a transfer
+ KFileItemList list(*m_view->selectedItems());
+ KFileItemListIterator i(list);
+ KFileItem *item;
+ KFTPSession::Session *opposite = KFTPSession::Manager::self()->getActive(oppositeSide(m_view->m_session->getSide()));
+
+ while ((item = i.current()) != 0) {
+ KURL destinationUrl = opposite->getFileView()->url();
+ destinationUrl.addPath(item->name());
+
+ KFTPQueue::Manager::self()->spawnTransfer(
+ item->url(),
+ destinationUrl,
+ item->size(),
+ item->isDir(),
+ list.count() == 1,
+ true,
+ 0L,
+ list.count() > 1
+ );
+
+ ++i;
+ }
+}
+
+void Actions::slotCreateDir()
+{
+ // Create new directory
+ bool ok;
+ QString newDirName = KInputDialog::getText(i18n("Create Directory"), i18n("Directory name:"), "", &ok);
+
+ if (ok) {
+ KURL url = m_view->url();
+ url.addPath(newDirName);
+
+ if (url.isLocalFile())
+ KIO::mkdir(url);
+ else
+ m_view->m_ftpClient->mkdir(url);
+ }
+}
+
+void Actions::slotFileEdit()
+{
+ KFileItem *item = m_view->selectedItems()->getFirst();
+
+ if (!item->isDir()) {
+ if (item->isLocalFile()) {
+ item->run();
+ } else {
+ // Create a new transfer to download the file and open it
+ KFTPQueue::TransferFile *transfer = new KFTPQueue::TransferFile(KFTPQueue::Manager::self());
+ transfer->setSourceUrl(item->url());
+ transfer->setDestUrl(KURL(KGlobal::dirs()->saveLocation("tmp") + QString("%1-%2").arg(KApplication::randomString(7)).arg(item->name())));
+ transfer->addSize(item->size());
+ transfer->setTransferType(KFTPQueue::Download);
+ transfer->setOpenAfterTransfer(true);
+ KFTPQueue::Manager::self()->insertTransfer(transfer);
+
+ // Execute the transfer
+ transfer->delayedExecute();
+ }
+ }
+}
+
+void Actions::slotAbort()
+{
+ KFTPSession::Session *session = KFTPSession::Manager::self()->find(m_view);
+
+ // Abort the session
+ if (session)
+ session->abort();
+}
+
+void Actions::slotRawCmd()
+{
+ bool ok;
+ QString rawCmd = KInputDialog::getText(i18n("Send Raw Command"), i18n("Command:"), "", &ok);
+
+ if (ok)
+ m_view->m_ftpClient->raw(rawCmd);
+}
+
+void Actions::slotShowHideTree()
+{
+ m_view->setTreeVisible(m_toggleTreeViewAction->isChecked());
+}
+
+void Actions::slotShowHideFilter()
+{
+ if (m_toggleFilterAction->isChecked()) {
+ m_view->m_searchToolBar->show();
+ m_view->m_searchFilter->clear();
+ m_view->m_searchFilter->setFocus();
+ } else {
+ m_view->m_searchFilter->clear();
+ m_view->m_searchToolBar->hide();
+ }
+}
+
+void Actions::slotCharsetChanged(int id)
+{
+ if (!m_changeEncodingAction->popupMenu()->isItemChecked(id)) {
+ QStringList charsets = KGlobal::charsets()->descriptiveEncodingNames();
+ QString charset = KGlobal::charsets()->encodingForName(charsets[id - 1]);
+
+ // Set the current socket's charset
+ m_view->m_ftpClient->socket()->changeEncoding(charset);
+
+ // Update checked items
+ m_changeEncodingAction->popupMenu()->setItemChecked(id, true);
+ m_changeEncodingAction->popupMenu()->setItemChecked(m_curCharsetOption, false);
+ m_curCharsetOption = id;
+ }
+}
+
+void Actions::slotCharsetReset(int id)
+{
+ // Revert to default charset if possible
+ KFTPBookmarks::Site *site = m_view->m_session->getSite();
+
+ if (site) {
+ // Set the current socket's charset
+ m_view->m_ftpClient->socket()->changeEncoding(site->getProperty("encoding"));
+
+ // Update checked items
+ m_changeEncodingAction->popupMenu()->setItemChecked(id, true);
+ m_changeEncodingAction->popupMenu()->setItemChecked(m_curCharsetOption, false);
+ m_curCharsetOption = id;
+ }
+}
+
+void Actions::slotExportListing()
+{
+ QString savePath = KFileDialog::getSaveFileName(QString::null, "*.txt|Directory Dump", 0, i18n("Export Directory Listing"));
+
+ if (!savePath.isEmpty()) {
+ QFile file(savePath);
+
+ if (!file.open(IO_WriteOnly))
+ return;
+
+ QTextStream stream(&file);
+
+ KFileItemList list(*m_view->items());
+ KFileItemListIterator i(list);
+ KFileItem *item;
+
+ while ((item = i.current()) != 0) {
+ stream << item->permissionsString() << "\t";
+ stream << item->user() << "\t" << item->group() << "\t";
+ stream << item->timeString() << "\t";
+ stream << item->name() << "\t";
+ stream << "\n";
+
+ ++i;
+ }
+
+ file.flush();
+ file.close();
+ }
+}
+
+void Actions::slotVerify()
+{
+ KFTPWidgets::Verifier *verifier = new KFTPWidgets::Verifier();
+ verifier->setFile(m_view->selectedItems()->getFirst()->url().path());
+ verifier->exec();
+
+ delete verifier;
+}
+
+void Actions::slotShowHiddenFiles()
+{
+ m_view->setShowHidden(m_showHiddenFilesAction->isChecked());
+ m_view->reload();
+}
+
+void Actions::slotOpenExternal()
+{
+ KFileItem *folder = new KFileItem(m_view->url(), "inode/directory", S_IFDIR);
+ folder->run();
+}
+
+void Actions::slotMarkItems()
+{
+ KFileItemList list(*m_view->selectedItems());
+ KFileItemListIterator i(list);
+ KFileItem *item;
+ KFTPSession::Session *opposite = KFTPSession::Manager::self()->getActive(oppositeSide(m_view->m_session->getSide()));
+
+ DetailsView *tView = m_view->getDetailsView();
+ DetailsView *oView = opposite->getFileView()->getDetailsView();
+
+ while ((item = i.current()) != 0) {
+ tView->markItem(item);
+ oView->markItem(item->name());
+ ++i;
+ }
+}
+
+void Actions::slotCompare()
+{
+ KFTPSession::Session *opposite = KFTPSession::Manager::self()->getActive(oppositeSide(m_view->m_session->getSide()));
+
+ DetailsView *tView = m_view->getDetailsView();
+ DetailsView *oView = opposite->getFileView()->getDetailsView();
+
+ // All items in the other list view should be visible by default
+ QListViewItemIterator j(oView);
+ while (j.current()) {
+ KFileItem *oItem = static_cast<KFileListViewItem*>(*j)->fileInfo();
+ oView->setItemVisibility(oItem, true);
+
+ ++j;
+ }
+
+ // Compare the two listviews
+ QListViewItemIterator i(tView);
+ while (i.current()) {
+ KFileItem *tItem = static_cast<KFileListViewItem*>(*i)->fileInfo();
+
+ if (tItem) {
+ KFileItem *oItem = oView->fileItem(tItem->name());
+
+ if (oItem && (oItem->size() == tItem->size() || oItem->isDir())) {
+ tView->setItemVisibility(tItem, false);
+ oView->setItemVisibility(oItem, false);
+ } else {
+ tView->setItemVisibility(tItem, true);
+ }
+ }
+
+ ++i;
+ }
+
+ PopupMessage *popup = new PopupMessage(m_view->getStatusLabel(), m_view);
+ popup->setText(i18n("Identical files on both sides have been hidden. Only <b>different files</b> are now visible."));
+ popup->setImage(SmallIcon("info"));
+ popup->setShowCloseButton(false);
+ popup->setShowCounter(false);
+
+ popup->reposition();
+ popup->display();
+}
+
+}
+
+}
+
+#include "actions.moc"
diff --git a/kftpgrabber/src/widgets/browser/actions.h b/kftpgrabber/src/widgets/browser/actions.h
new file mode 100644
index 0000000..69434e4
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/actions.h
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPFILEDIRVIEWACTIONS_H
+#define KFTPFILEDIRVIEWACTIONS_H
+
+#include <qobject.h>
+#include <kaction.h>
+#include <krun.h>
+#include <kservice.h>
+#include <kuserprofile.h>
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class View;
+
+/**
+ * This class contains all per-view actions.
+ *
+ * @author Jernej Kos
+ */
+class Actions : public QObject
+{
+Q_OBJECT
+friend class View;
+friend class DetailsView;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent view widget
+ */
+ Actions(View *parent);
+
+ /**
+ * Initialize view's action collection and it's actions.
+ */
+ void initActions();
+
+ /**
+ * Properly enable/disable the available actions.
+ */
+ void updateActions();
+private:
+ KActionCollection *m_actionCollection;
+ View *m_view;
+
+ int m_curCharsetOption;
+ int m_defaultCharsetOption;
+
+ KAction *m_goUpAction;
+ KAction *m_goBackAction;
+ KAction *m_goForwardAction;
+ KAction *m_goHomeAction;
+ KAction *m_reloadAction;
+
+ KAction *m_abortAction;
+ KToggleAction *m_toggleTreeViewAction;
+ KToggleAction *m_toggleFilterAction;
+
+ KAction *m_renameAction;
+ KAction *m_deleteAction;
+ KAction *m_propsAction;
+ KAction *m_shredAction;
+
+ KAction *m_copyAction;
+ KAction *m_pasteAction;
+
+ KActionMenu *m_filterActions;
+ KAction *m_alwaysSkipAction;
+ KAction *m_topPriorityAction;
+ KAction *m_lowPriorityAction;
+
+ KAction *m_transferAction;
+ KAction *m_queueTransferAction;
+ KAction *m_createDirAction;
+ KAction *m_fileEditAction;
+ KAction *m_verifyAction;
+
+ KActionMenu *m_moreActions;
+ KActionMenu *m_rawCommandsMenu;
+ KAction *m_rawCmdAction;
+ KActionMenu *m_changeEncodingAction;
+ KAction *m_exportListingAction;
+ KToggleAction *m_showHiddenFilesAction;
+ KAction *m_openExternalAction;
+
+ KAction *m_markItemsAction;
+ KAction *m_compareAction;
+
+ KActionMenu *m_siteChangeAction;
+ KAction *m_quickConnectAction;
+ KActionMenu *m_connectAction;
+ KAction *m_disconnectAction;
+private:
+ /**
+ * Populates the encodings list.
+ */
+ void populateEncodings();
+
+ /**
+ * A helper function to add the currently selected item(s) to the
+ * priority list with the given priority.
+ *
+ * @param priority The priority to use
+ */
+ void addPriorityItems(int priority);
+private slots:
+ void slotGoUp();
+ void slotGoBack();
+ void slotGoForward();
+ void slotGoHome();
+ void slotReload();
+
+ void slotAbort();
+ void slotShowHideTree();
+ void slotShowHideFilter();
+
+ void slotRename();
+ void slotDelete();
+ void slotProps();
+ void slotShred();
+
+ void slotCopy();
+ void slotPaste();
+
+ void slotAlwaysSkip();
+ void slotTopPriority();
+ void slotLowPriority();
+
+ void slotTransfer();
+ void slotQueueTransfer();
+ void slotCreateDir();
+ void slotFileEdit();
+ void slotVerify();
+
+ void slotRawCmd();
+ void slotCharsetChanged(int);
+ void slotCharsetReset(int);
+ void slotExportListing();
+ void slotShowHiddenFiles();
+ void slotOpenExternal();
+
+ void slotMarkItems();
+ void slotCompare();
+
+ void slotQuickConnect();
+ void slotDisconnect();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/detailsview.cpp b/kftpgrabber/src/widgets/browser/detailsview.cpp
new file mode 100644
index 0000000..5206d3d
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/detailsview.cpp
@@ -0,0 +1,596 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "browser/detailsview.h"
+#include "browser/treeview.h"
+#include "browser/view.h"
+#include "browser/locationnavigator.h"
+#include "browser/dirlister.h"
+#include "browser/actions.h"
+
+#include "misc/config.h"
+#include "misc/kftpapi.h"
+#include "misc/filter.h"
+
+#include "kftpqueue.h"
+
+#include <qheader.h>
+#include <qpainter.h>
+
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+
+using namespace KFTPCore::Filter;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+DetailsView::DetailsView(QWidget *parent, View *view, KFTPSession::Session *session)
+ : KFileDetailView(parent, 0),
+ m_view(view),
+ m_treeView(0),
+ m_refreshing(false),
+ m_shouldDisableResize(true),
+ m_autoResizeEnabled(true)
+{
+ m_resizeTimer = new QTimer(this);
+ connect(m_resizeTimer, SIGNAL(timeout()), this, SLOT(updateColumnWidths()));
+
+ m_dirLister = new DirLister(this);
+ m_dirLister->setSession(session);
+
+ connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
+ connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotCompleted()));
+ connect(m_dirLister, SIGNAL(deleteItem(KFileItem*)), this, SLOT(slotDeleteItem(KFileItem*)));
+ connect(m_dirLister, SIGNAL(refreshItems()), this, SLOT(slotRefreshItems()));
+ connect(m_dirLister, SIGNAL(siteChanged(const KURL&)), this, SLOT(slotSiteChanged(const KURL&)));
+
+ m_navigator = new LocationNavigator(this);
+
+ connect(m_navigator, SIGNAL(urlChanged(const KURL&)), this, SLOT(slotUrlChanged(const KURL&)));
+
+ connect(this, SIGNAL(executed(QListViewItem*)), this, SLOT(slotItemExecuted()));
+ connect(this, SIGNAL(returnPressed(QListViewItem*)), this, SLOT(slotItemExecuted()));
+ connect(this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)));
+ connect(this, SIGNAL(itemRenamed(QListViewItem*, const QString&, int)), this, SLOT(slotItemRenamed(QListViewItem*, const QString&)));
+
+ // Setup the header
+ QHeader *viewHeader = header();
+ viewHeader->setResizeEnabled(true);
+ viewHeader->setMovingEnabled(false);
+
+ connect(viewHeader, SIGNAL(sizeChange(int, int, int)), this, SLOT(slotHeaderResized(int)));
+
+ // Set column width
+ setColumnWidthMode(NameColumn, QListView::Manual);
+ setColumnWidthMode(SizeColumn, QListView::Manual);
+ setColumnWidthMode(DateColumn, QListView::Manual);
+ setColumnWidthMode(PermissionsColumn, QListView::Manual);
+
+ // Set column alignments
+ setColumnAlignment(SizeColumn, Qt::AlignRight);
+ setColumnAlignment(DateColumn, Qt::AlignHCenter);
+ setColumnAlignment(PermissionsColumn, Qt::AlignHCenter);
+
+ if (KFTPCore::Config::showOwnerGroup()) {
+ setColumnAlignment(OwnerColumn, Qt::AlignHCenter);
+ setColumnAlignment(GroupColumn, Qt::AlignHCenter);
+ } else {
+ // Only show owner/group if set in the config
+ removeColumn(4);
+ removeColumn(4);
+ }
+
+ setColumnWidth(NameColumn, 140);
+ setColumnWidth(DateColumn, 100);
+
+ setAcceptDrops(true);
+ setSelectionMode(KFile::Extended);
+ setHScrollBarMode(QScrollView::AlwaysOff);
+
+ // Set the defaults
+ setHomeUrl(KURL(KFTPCore::Config::defLocalDir()));
+ setShowHidden(KFTPCore::Config::showHiddenFiles());
+}
+
+DetailsView::~DetailsView()
+{
+}
+
+const KURL &DetailsView::url() const
+{
+ return m_navigator->url();
+}
+
+const QValueList<LocationNavigator::Element> DetailsView::history(int &index) const
+{
+ return m_navigator->history(index);
+}
+
+bool DetailsView::isSelected(const KFileItem *i) const
+{
+ if (!i)
+ return false;
+
+ const KFileListViewItem *item = static_cast<const KFileListViewItem*>(i->extraData(this));
+ return (item && item->isSelected() && item->isVisible());
+}
+
+void DetailsView::setTreeView(TreeView *tree)
+{
+ m_treeView = tree;
+
+ connect(m_treeView, SIGNAL(pathChanged(const KURL&)), this, SLOT(openUrl(const KURL&)));
+}
+
+void DetailsView::setHomeUrl(const KURL &url)
+{
+ m_navigator->setHomeUrl(url);
+}
+
+void DetailsView::goBack()
+{
+ m_navigator->goBack();
+}
+
+void DetailsView::goForward()
+{
+ m_navigator->goForward();
+}
+
+void DetailsView::goUp()
+{
+ m_navigator->goUp();
+}
+
+void DetailsView::goHome()
+{
+ m_navigator->goHome();
+}
+
+void DetailsView::endItemUpdates()
+{
+ const KFileListViewItem *item = static_cast<const KFileListViewItem*>(firstChild());
+ if (item)
+ setCurrentItem(item->fileInfo());
+
+ int index = 0;
+ const QValueList<LocationNavigator::Element> history = m_navigator->history(index);
+ if (!history.isEmpty()) {
+ KFileView::setCurrentItem(history[index].currentFilename());
+ KFileDetailView::setSelected(currentFileItem(), true);
+
+ // Scroll the contents to last known coordinates
+ setContentsPos(history[index].contentsX(), history[index].contentsY());
+ }
+
+ m_treeView->openUrl(m_navigator->url());
+ m_treeView->endUpdate(m_navigator->url());
+ setFocus();
+}
+
+void DetailsView::openUrl(const KURL &url)
+{
+ m_navigator->setUrl(url);
+}
+
+void DetailsView::slotClear()
+{
+ clearView();
+}
+
+void DetailsView::slotDeleteItem(KFileItem *item)
+{
+ removeItem(item);
+
+ if (item->isDir())
+ m_treeView->removeFolder(item->url());
+}
+
+void DetailsView::slotCompleted()
+{
+ m_refreshing = true;
+
+ clearView();
+
+ KFileItemList items(m_dirLister->items());
+ KFileItemListIterator i(items);
+
+ KFileItem *item = 0;
+ while ((item = i.current()) != 0) {
+ insertItem(item);
+
+ if (item->isDir())
+ m_treeView->createFolder(item->url(), item->pixmap(16));
+
+ ++i;
+ }
+
+ endItemUpdates();
+ m_refreshing = false;
+
+ m_view->updateActions();
+}
+
+void DetailsView::slotRefreshItems()
+{
+ QTimer::singleShot(0, this, SLOT(reload()));
+}
+
+void DetailsView::reload()
+{
+ fetchLocation(m_navigator->url(), true);
+}
+
+void DetailsView::slotUrlChanged(const KURL &url)
+{
+ fetchLocation(url);
+}
+
+void DetailsView::slotSiteChanged(const KURL &url)
+{
+ m_navigator->clear();
+ m_treeView->resetView(url);
+}
+
+void DetailsView::fetchLocation(const KURL &url, bool reload)
+{
+ m_dirLister->setShowingDotFiles(m_showHidden);
+ m_dirLister->fetchLocation(url, reload);
+}
+
+void DetailsView::slotContentsMoving(int x, int y)
+{
+ if (!m_refreshing)
+ emit contentsMoved(x, y);
+}
+
+void DetailsView::slotItemExecuted()
+{
+ KFileItem *item = currentFileItem();
+
+ if (item) {
+ if (item->isDir())
+ openUrl(item->url());
+ }
+}
+
+void DetailsView::slotHeaderResized(int section)
+{
+ if (m_autoResizeEnabled && m_shouldDisableResize && section == 0) {
+ setHScrollBarMode(QScrollView::Auto);
+ m_autoResizeEnabled = false;
+ }
+}
+
+void DetailsView::resizeContents(int width, int height)
+{
+ m_shouldDisableResize = false;
+
+ KFileDetailView::resizeContents(width, height);
+
+ // Update the column widths
+ if (m_autoResizeEnabled) {
+ m_resizeTimer->stop();
+ m_resizeTimer->start(50, true);
+ }
+}
+
+void DetailsView::resizeEvent(QResizeEvent *event)
+{
+ m_shouldDisableResize = false;
+
+ KFileDetailView::resizeEvent(event);
+
+ // Update the column widths
+ if (m_autoResizeEnabled) {
+ m_resizeTimer->stop();
+ m_resizeTimer->start(50, true);
+ }
+}
+
+void DetailsView::updateColumnWidths()
+{
+ // The code below is based on Dolphin, Copyright (C) 2006 by Peter Penz
+ const int columnCount = columns();
+ int requiredWidth = 0;
+
+ for (int i = 1; i < columnCount; ++i) {
+ // When a directory contains no items, a minimum width for
+ // the column must be available, so that the header is readable.
+ int columnWidth = 64;
+ QFontMetrics fontMetrics(font());
+
+ for (QListViewItem* item = firstChild(); item != 0; item = item->nextSibling()) {
+ const int width = item->width(fontMetrics, this, i);
+
+ if (width > columnWidth) {
+ columnWidth = width;
+ }
+ }
+
+ // Add custom margin
+ columnWidth += 16;
+ setColumnWidth(i, columnWidth);
+ requiredWidth += columnWidth;
+ }
+
+ // Resize the first column in a way that the whole available width is used
+ int firstColumnWidth = visibleWidth() - requiredWidth;
+ if (firstColumnWidth < 128) {
+ firstColumnWidth = 128;
+ }
+
+ setColumnWidth(0, firstColumnWidth);
+ m_shouldDisableResize = true;
+}
+
+void DetailsView::insertItem(KFileItem *fileItem)
+{
+ const ActionChain *actionChain = Filters::self()->process(fileItem->url(), fileItem->size(), fileItem->isDir());
+ const Action *action;
+
+ if ((actionChain && actionChain->getAction(Action::Hide)))
+ return;
+
+ KFileView::insertItem(fileItem);
+
+ ListViewItem *item = new ListViewItem(this, fileItem);
+
+ if (actionChain && (action = actionChain->getAction(Action::Colorize)))
+ item->setColor(action->value().toColor());
+
+ QDir::SortSpec spec = KFileView::sorting();
+ if (spec & QDir::Time) {
+ item->setKey(sortingKey(fileItem->time(KIO::UDS_MODIFICATION_TIME), fileItem->isDir(), spec));
+ } else if (spec & QDir::Size) {
+ item->setKey(sortingKey(fileItem->size(), fileItem->isDir(), spec));
+ } else {
+ item->setKey(sortingKey(fileItem->text(), fileItem->isDir(), spec));
+ }
+
+ fileItem->setExtraData(this, item);
+}
+
+void DetailsView::slotContextMenu(KListView*, QListViewItem *i, const QPoint &p)
+{
+ m_view->updateActions();
+
+ // Create the popup menu
+ KPopupMenu *menu = new KPopupMenu(this);
+
+ Actions *actions = m_view->m_actions;
+
+ // Always show create directory
+ actions->m_createDirAction->plug(menu);
+ menu->insertSeparator();
+
+ // If nothing is selected, show the navigation menus
+ if (!i) {
+ actions->m_goUpAction->plug(menu);
+ actions->m_goBackAction->plug(menu);
+ actions->m_goForwardAction->plug(menu);
+ actions->m_reloadAction->plug(menu);
+ } else {
+ actions->m_transferAction->plug(menu);
+ actions->m_queueTransferAction->plug(menu);
+ actions->m_renameAction->plug(menu);
+ actions->m_deleteAction->plug(menu);
+ actions->m_shredAction->plug(menu);
+ actions->m_fileEditAction->plug(menu);
+ actions->m_verifyAction->plug(menu);
+ menu->insertSeparator();
+ actions->m_copyAction->plug(menu);
+ actions->m_pasteAction->plug(menu);
+ menu->insertSeparator();
+ actions->m_filterActions->plug(menu);
+ }
+
+ // Always show properties
+ menu->insertSeparator();
+ actions->m_propsAction->plug(menu);
+
+ menu->exec(p);
+}
+
+void DetailsView::slotItemRenamed(QListViewItem *item, const QString &name)
+{
+ KFileItem *fileItem = static_cast<KFileListViewItem*>(item)->fileInfo();
+ m_view->rename(fileItem->url(), name);
+}
+
+QDragObject *DetailsView::dragObject()
+{
+ KURLDrag *object = static_cast<KURLDrag*>(KFileDetailView::dragObject());
+
+ // Add some metadata
+ const KFileItemList *list = KFileView::selectedItems();
+
+ if (list) {
+ KFileItemListIterator i(*list);
+ KFileItem *item;
+
+ while ((item = i.current()) != 0) {
+ QString type = item->isDir() ? "D" : "F";
+ object->metaData().insert(item->url().htmlURL().local8Bit(), type + ":" + KIO::number(item->size()));
+ ++i;
+ }
+ }
+
+ return object;
+}
+
+bool DetailsView::acceptDrag(QDropEvent *event) const
+{
+ return KURLDrag::canDecode(event) &&
+ (event->action() == QDropEvent::Copy ||
+ event->action() == QDropEvent::Move ||
+ event->action() == QDropEvent::Link) &&
+ event->source() != this;
+}
+
+void DetailsView::contentsDropEvent(QDropEvent *event)
+{
+ if (!acceptDrag(event))
+ return;
+
+ KIO::MetaData meta;
+ KURL::List urls;
+ KURLDrag::decode(event, urls, meta);
+
+ meta.insert("DestURL", url().url());
+ KFTPQueue::Manager::self()->insertTransfer(new KURLDrag(urls, meta, this, name()));
+}
+
+KFileItem *DetailsView::fileItem(const QString &filename)
+{
+ if (!filename.isNull()) {
+ KFileItem *item;
+
+ for (item = firstFileItem(); item; item = nextItem(item)) {
+ if (item->name() == filename)
+ return item;
+ }
+ }
+
+ return 0;
+}
+
+void DetailsView::setItemVisibility(KFileItem *item, int visibility)
+{
+ ListViewItem *i = static_cast<ListViewItem*>(item->extraData(this));
+
+ if (i) {
+ if (visibility != 0 && visibility != 1)
+ visibility = !i->isVisible();
+
+ i->setVisible(visibility);
+ }
+}
+
+void DetailsView::setItemColor(KFileItem *item, const QColor &text, const QColor &background)
+{
+ ListViewItem *i = static_cast<ListViewItem*>(item->extraData(this));
+
+ if (i) {
+ i->setColor(text);
+ i->setBackground(background);
+ i->repaint();
+ }
+}
+
+void DetailsView::markItem(KFileItem *item)
+{
+ ListViewItem *i = static_cast<ListViewItem*>(item->extraData(this));
+
+ if (i) {
+ i->markItem(!i->marked());
+ }
+}
+
+void DetailsView::markItem(const QString &filename)
+{
+ if (KFileItem *item = fileItem(filename))
+ markItem(item);
+}
+
+void DetailsView::unmarkItems()
+{
+ KFileItem *item;
+
+ for (item = firstFileItem(); item; item = nextItem(item)) {
+ static_cast<ListViewItem*>(item->extraData(this))->markItem(false);
+ }
+}
+
+DetailsView::ListViewItem::ListViewItem(QListView *parent, KFileItem *fileItem)
+ : KFileListViewItem(parent, fileItem),
+ m_marked(false)
+{
+ if (fileItem->isDir() && !KFTPCore::Config::showDirectorySize()) {
+ setText(SizeColumn, " - ");
+ } else {
+ QString sizeText;
+ sizeText = KFTPCore::Config::showSizeInBytes() ? KIO::number(fileItem->size()) : KIO::convertSize(fileItem->size());
+ sizeText.append(" ");
+
+ setText(SizeColumn, sizeText);
+ }
+}
+
+void DetailsView::ListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ QColorGroup colorGroup(cg);
+ QColor textColor = colorGroup.text();
+
+ // Set custom file item color if set
+ if (m_textColor.isValid())
+ colorGroup.setColor(QColorGroup::Text, m_textColor);
+
+ // Set custom file item font
+ QFont font = p->font();
+ font.setBold(m_marked);
+ p->setFont(font);
+
+ if (m_backgroundColor.isValid()) {
+ colorGroup.setColor(QColorGroup::Base, m_backgroundColor);
+
+ // Override the KListViewItem since it resets the background color
+ QListViewItem::paintCell(p, colorGroup, column, width, alignment);
+ } else {
+ KFileListViewItem::paintCell(p, colorGroup, column, width, alignment);
+ }
+
+ if (column < listView()->columns() - 1) {
+ // Draw a separator between columns
+ p->setPen(KGlobalSettings::buttonBackground());
+ p->drawLine(width - 1, 0, width - 1, height() - 1);
+ }
+}
+
+void DetailsView::ListViewItem::markItem(bool marked)
+{
+ if (m_marked != marked) {
+ m_marked = marked;
+ repaint();
+ }
+}
+
+}
+
+}
+
+#include "detailsview.moc"
diff --git a/kftpgrabber/src/widgets/browser/detailsview.h b/kftpgrabber/src/widgets/browser/detailsview.h
new file mode 100644
index 0000000..eb60b70
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/detailsview.h
@@ -0,0 +1,374 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETS_BROWSERDETAILSVIEW_H
+#define KFTPWIDGETS_BROWSERDETAILSVIEW_H
+
+#include "browser/locationnavigator.h"
+
+#include <kfiledetailview.h>
+#include <kdirlister.h>
+
+namespace KFTPSession {
+ class Session;
+}
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class DirLister;
+class TreeView;
+class View;
+
+/**
+ * This class represents a detailed list view for displaying local and
+ * remote directory contents. It is based upon KFileDetailView but uses
+ * a custom (wrapped) DirLister for actual listings.
+ *
+ * @author Jernej Kos
+ */
+class DetailsView : public KFileDetailView {
+Q_OBJECT
+public:
+ /**
+ * Maps the column indices of KFileDetailView to a
+ * descriptive column name.
+ */
+ enum ColumnName {
+ NameColumn = 0,
+ SizeColumn = 1,
+ DateColumn = 2,
+ PermissionsColumn = 3,
+ OwnerColumn = 4,
+ GroupColumn = 5
+ };
+
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ * @param view Main view container
+ * @param session Current session
+ */
+ DetailsView(QWidget *parent, View *view, KFTPSession::Session *session);
+
+ /**
+ * Class destructor.
+ */
+ ~DetailsView();
+
+ /**
+ * Returns the navigator object for navigating this view.
+ */
+ const LocationNavigator *locationNavigator() const { return m_navigator; }
+
+ /**
+ * Returns the current URL.
+ */
+ const KURL &url() const;
+
+ /**
+ * Returns the current history elements.
+ *
+ * @param index Variable to save the current history position to
+ * @return Current history element list
+ */
+ const QValueList<LocationNavigator::Element> history(int &index) const;
+
+ /**
+ * @overload
+ * Reimplemented from KFileView to support filtered views.
+ */
+ bool isSelected(const KFileItem *i) const;
+
+ /**
+ * Set the tree view widget to use for the tree.
+ *
+ * @param tree A valid tree view widget
+ */
+ void setTreeView(TreeView *tree);
+
+ /**
+ * Set the home URL.
+ *
+ * @param url URL to use as home URL
+ */
+ void setHomeUrl(const KURL &url);
+
+ /**
+ * Changes the "show hidden files" setting.
+ *
+ * @param value True to enable showing hidden files, false otherwise
+ */
+ void setShowHidden(bool value) { m_showHidden = value; }
+
+ /**
+ * Go one history hop back.
+ */
+ void goBack();
+
+ /**
+ * Go one history hop forward.
+ */
+ void goForward();
+
+ /**
+ * Go up in the directory structure.
+ */
+ void goUp();
+
+ /**
+ * Go the the predefined home URL.
+ */
+ void goHome();
+
+ /**
+ * Do item post-processing.
+ */
+ void endItemUpdates();
+
+ /**
+ * @overload
+ * Reimplemented from KFileDetailView.
+ */
+ void insertItem(KFileItem *fileItem);
+
+ /**
+ * Fetch a file item by its filename.
+ *
+ * @param file Item's filename
+ */
+ KFileItem *fileItem(const QString &filename);
+
+ /**
+ * Changes item's colors in the list view. Note that this overrides any
+ * highlight colors predefined by user filters.
+ *
+ * @param item The item to change colors for
+ * @param text Text color
+ * @param background Background color
+ */
+ void setItemColor(KFileItem *item, const QColor &text, const QColor &background);
+
+ /**
+ * Changes item's visibility in the list view.
+ *
+ * @param item The item tho change visiblity for
+ * @param visibility 1 if the item should be visible, 0 if it should be hidden, reverse otherwise
+ */
+ void setItemVisibility(KFileItem *item, int visibility = -1);
+
+ /**
+ * Mark or unmark the item. If the item is already marked it will be
+ * unmarked and vice-versa.
+ *
+ * @param item The item to mark
+ */
+ void markItem(KFileItem *item);
+
+ /**
+ * Mark or unmark the item identified by its filename. If the item is
+ * already marked it will be unmarked and vice-versa.
+ *
+ * @param filename Item's filename
+ */
+ void markItem(const QString &filename);
+
+ /**
+ * Unmark all items.
+ */
+ void unmarkItems();
+public slots:
+ /**
+ * Open an URL. Note that if a remote URL is specified the session needs to
+ * be connected to the specified host!
+ *
+ * @param url URL to open
+ */
+ void openUrl(const KURL &url);
+
+ /**
+ * Reload the current directory listing.
+ */
+ void reload();
+
+ /**
+ * Calculates new widths for all the columns.
+ */
+ void updateColumnWidths();
+
+ /**
+ * This method gets called when the widget is resized. It is used for automatic
+ * resize handling.
+ *
+ * @param width The new width
+ * @param height The new height
+ */
+ void resizeContents(int width, int height);
+
+ /**
+ * Returns the drag object to use for copy/paste and "drag and drop".
+ */
+ QDragObject *dragObject();
+protected:
+ /**
+ * Instruct the directory lister object to fetch the specified URL.
+ *
+ * @param url URL to fetch
+ * @param reload Should the cache be invalidated
+ */
+ void fetchLocation(const KURL &url, bool reload = false);
+
+ /**
+ * This method gets called when the widget is resized. It is used for automatic
+ * resize handling.
+ */
+ void resizeEvent(QResizeEvent *event);
+
+ /**
+ * Returns true if the drop event should be accepted.
+ */
+ bool acceptDrag(QDropEvent *event) const;
+
+ /**
+ * Called when something acceptable gets dropped on us.
+ */
+ void contentsDropEvent(QDropEvent *event);
+private:
+ class ListViewItem : public KFileListViewItem {
+ public:
+ ListViewItem(QListView *parent, KFileItem *fileItem);
+
+ /**
+ * @overload
+ * Reimplemented from KListView to implement nicer items.
+ */
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+
+ /**
+ * Set a text color for this item.
+ *
+ * @param color Text color
+ */
+ void setColor(QColor color) { m_textColor = color; }
+
+ /**
+ * Set a background color for this item.
+ *
+ * @param color Background color
+ */
+ void setBackground(QColor color) { m_backgroundColor = color; }
+
+ /**
+ * Set marked state for this item.
+ *
+ * @param marked True if this item should be marked, false otherwise
+ */
+ void markItem(bool marked);
+
+ /**
+ * Returns true if this item is marked.
+ */
+ bool marked() const { return m_marked; }
+ private:
+ QColor m_textColor;
+ QColor m_backgroundColor;
+ bool m_marked;
+ };
+
+ View *m_view;
+ DirLister *m_dirLister;
+ LocationNavigator *m_navigator;
+ TreeView *m_treeView;
+ QTimer *m_resizeTimer;
+
+ bool m_refreshing;
+ bool m_showHidden;
+ bool m_shouldDisableResize;
+ bool m_autoResizeEnabled;
+protected slots:
+ void slotClear();
+ void slotCompleted();
+ void slotDeleteItem(KFileItem *item);
+ void slotRefreshItems();
+ void slotSiteChanged(const KURL &url);
+
+ void slotUrlChanged(const KURL &url);
+
+ /**
+ * This slot gets called when an item has been renamed.
+ */
+ void slotItemRenamed(QListViewItem *item, const QString &name);
+
+ void slotContentsMoving(int x, int y);
+
+ /**
+ * This slot gets called when a context menu should be displayed.
+ */
+ void slotContextMenu(KListView*, QListViewItem *i, const QPoint &p);
+
+ /**
+ * This slot gets called when an item has been clicked.
+ */
+ void slotItemExecuted();
+
+ /**
+ * This slot gets called when a section in listview's header is resized. This
+ * is needed for proper automatic resize handling.
+ *
+ * @param section The section that was resized
+ */
+ void slotHeaderResized(int section);
+signals:
+ /**
+ * This signals gets emitted when user scrolls the widget.
+ *
+ * @param x New X position
+ * @param y New Y position
+ */
+ void contentsMoved(int x, int y);
+
+ /**
+ * This signal is emitted when items change.
+ */
+ void itemsChanged();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/dirlister.cpp b/kftpgrabber/src/widgets/browser/dirlister.cpp
new file mode 100644
index 0000000..4d539e4
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/dirlister.cpp
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "dirlister.h"
+
+#include "engine/thread.h"
+#include "engine/cache.h"
+
+#include "kftpsession.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+
+using namespace KFTPEngine;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+DirLister::DirLister(QObject *parent)
+ : QObject(parent)
+{
+ m_localLister = new KDirLister();
+ m_localLister->setAutoUpdate(true);
+
+ enableLocal();
+}
+
+DirLister::~DirLister()
+{
+ delete m_localLister;
+}
+
+void DirLister::setSession(KFTPSession::Session *session)
+{
+ m_remoteSession = session;
+}
+
+void DirLister::fetchLocation(const KURL &url, bool reload)
+{
+ if (m_url.isLocalFile() != url.isLocalFile())
+ emit siteChanged(url);
+
+ m_url = url;
+
+ if (url.isLocalFile()) {
+ enableLocal();
+
+ m_localLister->stop();
+ m_localLister->setShowingDotFiles(m_showHidden);
+ m_localLister->openURL(url, false, reload);
+ } else {
+ enableRemote();
+
+ if (reload) {
+ KURL tmp = url;
+ Cache::self()->invalidateEntry(tmp);
+ }
+
+ emit clear();
+ m_remoteSession->getClient()->list(url);
+ }
+}
+
+void DirLister::enableLocal()
+{
+ m_localLister->stop();
+ m_localLister->QObject::disconnect(this);
+
+ connect(m_localLister, SIGNAL(clear()), this, SIGNAL(clear()));
+ connect(m_localLister, SIGNAL(completed()), this, SIGNAL(completed()));
+ connect(m_localLister, SIGNAL(deleteItem(KFileItem*)), this, SIGNAL(deleteItem(KFileItem*)));
+ connect(m_localLister, SIGNAL(refreshItems(const KFileItemList&)), this, SIGNAL(refreshItems()));
+}
+
+void DirLister::enableRemote()
+{
+ m_localLister->stop();
+ m_localLister->QObject::disconnect(this);
+ m_remoteSession->getClient()->eventHandler()->QObject::disconnect(this);
+
+ connect(m_remoteSession->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotRemoteEngineEvent(KFTPEngine::Event*)));
+}
+
+void DirLister::disableRemote()
+{
+ m_remoteSession->getClient()->eventHandler()->QObject::disconnect(this);
+}
+
+void DirLister::stop()
+{
+ if (m_url.isLocalFile())
+ m_localLister->stop();
+}
+
+KFileItemList DirLister::items() const
+{
+ if (m_url.isLocalFile())
+ return m_localLister->items();
+ else
+ return m_items;
+}
+
+void DirLister::slotRemoteEngineEvent(KFTPEngine::Event *event)
+{
+ switch (event->type()) {
+ case Event::EventError: {
+ KMessageBox::error(0, i18n("Could not enter folder %1.").arg(m_url.path()), i18n("Error"));
+ disableRemote();
+ break;
+ }
+ case Event::EventDirectoryListing: {
+ m_items.clear();
+
+ // Populate the item list
+ QValueList<DirectoryEntry> list = event->getParameter(0).asDirectoryListing().list();
+ QValueList<DirectoryEntry>::ConstIterator end(list.end());
+ for (QValueList<DirectoryEntry>::ConstIterator i(list.begin()); i != end; ++i) {
+ if (!m_showHidden && (*i).filename().at(0) == '.')
+ continue;
+
+ m_items.append(new KFileItem((*i).toUdsEntry(), m_url, false, true));
+ }
+
+ disableRemote();
+ emit completed();
+ break;
+ }
+ default: break;
+ }
+}
+
+}
+
+}
+
+#include "dirlister.moc"
diff --git a/kftpgrabber/src/widgets/browser/dirlister.h b/kftpgrabber/src/widgets/browser/dirlister.h
new file mode 100644
index 0000000..730704f
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/dirlister.h
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETS_BROWSERDIRLISTER_H
+#define KFTPWIDGETS_BROWSERDIRLISTER_H
+
+#include <qobject.h>
+
+#include <kurl.h>
+#include <kdirlister.h>
+
+namespace KFTPSession {
+ class Session;
+}
+
+namespace KFTPEngine {
+ class Event;
+}
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+/**
+ * This class is a wrapper around KDirLister to support remote listings
+ * via engine sockets.
+ *
+ * @author Jernej Kos
+ */
+class DirLister : public QObject {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent object
+ */
+ DirLister(QObject *parent);
+
+ /**
+ * Class destructor.
+ */
+ ~DirLister();
+
+ /**
+ * Set the remote session. You have to call this method before anything else
+ * in this class!
+ *
+ * @param session A valid session
+ */
+ void setSession(KFTPSession::Session *session);
+
+ /**
+ * Changes the "show hidden files" setting.
+ *
+ * @param value True to enable showing hidden files, false otherwise
+ */
+ void setShowingDotFiles(bool value) { m_showHidden = value; }
+
+ /**
+ * Fetch a specific location.
+ *
+ * @param url The URL to fetch (can be local or remote)
+ * @param reload If set to true listing cache will be invalidated
+ */
+ void fetchLocation(const KURL &url, bool reload);
+
+ /**
+ * Stop the current listing operation.
+ */
+ void stop();
+
+ /**
+ * Returns the items listed for the current URL. You should call this when receiving
+ * the finished() signal.
+ *
+ * @return The items listed for the current URL
+ */
+ KFileItemList items() const;
+protected:
+ void enableLocal();
+ void enableRemote();
+ void disableRemote();
+private:
+ KDirLister *m_localLister;
+ KFTPSession::Session *m_remoteSession;
+ KFileItemList m_items;
+ KURL m_url;
+ bool m_showHidden;
+private slots:
+ void slotRemoteEngineEvent(KFTPEngine::Event *event);
+signals:
+ /**
+ * Emitted when all items should be cleared.
+ */
+ void clear();
+
+ /**
+ * Emitted when the listing operation has been completed.
+ */
+ void completed();
+
+ /**
+ * Emitted when an item has to be removed.
+ */
+ void deleteItem(KFileItem *item);
+
+ /**
+ * Emitted when items should be refreshed.
+ */
+ void refreshItems();
+
+ /**
+ * Emitted when site changes from local to remote or vice-versa.
+ *
+ * @param url New site URL
+ */
+ void siteChanged(const KURL &url);
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/filterwidget.cpp b/kftpgrabber/src/widgets/browser/filterwidget.cpp
new file mode 100644
index 0000000..47f88d9
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/filterwidget.cpp
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "browser/filterwidget.h"
+#include "browser/detailsview.h"
+
+#include <qlistview.h>
+#include <qregexp.h>
+#include <qpopupmenu.h>
+
+#include <klocale.h>
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+FilterWidget::FilterWidget(QWidget *parent, DetailsView *view)
+ : KListViewSearchLine(parent, view),
+ m_filterDirectories(true),
+ m_filterSymlinks(true),
+ m_caseSensitive(false)
+{
+ connect(view, SIGNAL(itemsChanged()), this, SLOT(updateSearch()));
+}
+
+bool FilterWidget::itemMatches(const QListViewItem *item, const QString &pattern) const
+{
+ if (!pattern.isEmpty()) {
+ const KFileListViewItem *i = dynamic_cast<const KFileListViewItem*>(item);
+
+ if (i) {
+ if (i->fileInfo()->isDir() && !m_filterDirectories)
+ return true;
+ else if (i->fileInfo()->isLink() && !m_filterSymlinks)
+ return true;
+ }
+
+ QRegExp filter(pattern);
+ filter.setCaseSensitive(m_caseSensitive);
+ filter.setWildcard(true);
+
+ return filter.search(item->text(0)) > -1;
+ }
+
+ return true;
+}
+
+QPopupMenu *FilterWidget::createPopupMenu()
+{
+ QPopupMenu *popup = KLineEdit::createPopupMenu();
+
+ QPopupMenu *subMenu = new QPopupMenu(popup);
+ connect(subMenu, SIGNAL(activated(int)), this, SLOT(slotOptionsMenuActivated(int)));
+
+ popup->insertSeparator();
+ popup->insertItem(i18n("Filter Options"), subMenu);
+
+ subMenu->insertItem(i18n("Filter Directories"), FilterWidget::FilterDirectories);
+ subMenu->setItemChecked(FilterWidget::FilterDirectories, m_filterDirectories);
+
+ subMenu->insertItem(i18n("Filter Symlinks"), FilterWidget::FilterSymlinks);
+ subMenu->setItemChecked(FilterWidget::FilterSymlinks, m_filterSymlinks);
+
+ subMenu->insertItem(i18n("Case Sensitive"), FilterWidget::CaseSensitive);
+ subMenu->setItemChecked(FilterWidget::CaseSensitive, m_caseSensitive);
+
+ return popup;
+}
+
+void FilterWidget::slotOptionsMenuActivated(int id)
+{
+ switch (id) {
+ case FilterDirectories: m_filterDirectories = !m_filterDirectories; break;
+ case FilterSymlinks: m_filterSymlinks = !m_filterSymlinks; break;
+ case CaseSensitive: m_caseSensitive = !m_caseSensitive; break;
+ default: break;
+ }
+
+ updateSearch();
+}
+
+}
+
+}
+
+#include "filterwidget.moc"
diff --git a/kftpgrabber/src/widgets/browser/filterwidget.h b/kftpgrabber/src/widgets/browser/filterwidget.h
new file mode 100644
index 0000000..f5429cf
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/filterwidget.h
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETS_BROWSERFILTERWIDGET_H
+#define KFTPWIDGETS_BROWSERFILTERWIDGET_H
+
+#include <klistviewsearchline.h>
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class DetailsView;
+
+/**
+ * This class is a simple filtering widget that accepts wildcard
+ * patterns and filters listviews. Note that this widget only
+ * filters on the first column.
+ *
+ * @author Jernej Kos <kostko@jweb-network.net>
+ */
+class FilterWidget : public KListViewSearchLine {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent widget
+ * @param view The view you want to filter
+ */
+ FilterWidget(QWidget *parent, DetailsView *view);
+protected:
+ enum {
+ FilterDirectories = 1,
+ FilterSymlinks = 2,
+ CaseSensitive = 3
+ };
+
+ /**
+ * @overload
+ * Reimplemented from KListViewSearchLine to support wildcard
+ * matching schemes.
+ */
+ bool itemMatches(const QListViewItem *item, const QString &pattern) const;
+
+ /**
+ * @overload
+ * Reimplemented from KListViewSearchLine to remove multiple
+ * columns selection, since this widget only operates on the
+ * first column.
+ */
+ QPopupMenu *createPopupMenu();
+private:
+ bool m_filterDirectories;
+ bool m_filterSymlinks;
+ bool m_caseSensitive;
+private slots:
+ void slotOptionsMenuActivated(int id);
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/locationnavigator.cpp b/kftpgrabber/src/widgets/browser/locationnavigator.cpp
new file mode 100644
index 0000000..5be8004
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/locationnavigator.cpp
@@ -0,0 +1,173 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "locationnavigator.h"
+#include "detailsview.h"
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+LocationNavigator::Element::Element()
+ : m_url(),
+ m_currentFilename(),
+ m_contentsX(0),
+ m_contentsY(0)
+{
+}
+
+LocationNavigator::Element::Element(const KURL &url)
+ : m_url(url),
+ m_currentFilename(),
+ m_contentsX(0),
+ m_contentsY(0)
+{
+}
+
+LocationNavigator::LocationNavigator(DetailsView *view)
+ : m_view(view),
+ m_historyIndex(0)
+{
+ connect(view, SIGNAL(contentsMoved(int, int)), this, SLOT(slotContentsMoved(int, int)));
+}
+
+void LocationNavigator::setUrl(const KURL &url)
+{
+ if (m_historyIndex > 0) {
+ const KURL &nextUrl = m_history[m_historyIndex - 1].url();
+
+ if (url == nextUrl) {
+ goForward();
+ return;
+ }
+ }
+
+ // Check for duplicates
+ const KURL &currentUrl = m_history[m_historyIndex].url();
+ if (currentUrl == url)
+ return;
+
+ updateCurrentElement();
+
+ const QValueListIterator<LocationNavigator::Element> i = m_history.at(m_historyIndex);
+ m_history.insert(i, Element(url));
+
+ emit urlChanged(url);
+ emit historyChanged();
+
+ // Cleanup history when it becomes too big
+ if (m_historyIndex > 100) {
+ m_history.erase(m_history.begin());
+ m_historyIndex--;
+ }
+}
+
+const KURL& LocationNavigator::url() const
+{
+ return m_history[m_historyIndex].url();
+}
+
+const QValueList<LocationNavigator::Element> LocationNavigator::history(int &index) const
+{
+ index = m_historyIndex;
+ return m_history;
+}
+
+void LocationNavigator::goBack()
+{
+ updateCurrentElement();
+ const int count = m_history.count();
+
+ if (m_historyIndex < count - 1) {
+ m_historyIndex++;
+
+ emit urlChanged(url());
+ emit historyChanged();
+ }
+}
+
+void LocationNavigator::goForward()
+{
+ if (m_historyIndex > 0) {
+ m_historyIndex--;
+
+ emit urlChanged(url());
+ emit historyChanged();
+ }
+}
+
+void LocationNavigator::goUp()
+{
+ setUrl(url().upURL());
+}
+
+void LocationNavigator::goHome()
+{
+ setUrl(m_homeUrl);
+}
+
+void LocationNavigator::clear()
+{
+ Element element = m_history[m_historyIndex];
+
+ m_history.clear();
+ m_historyIndex = 0;
+
+ m_history.append(element);
+}
+
+void LocationNavigator::slotContentsMoved(int x, int y)
+{
+ m_history[m_historyIndex].setContentsX(x);
+ m_history[m_historyIndex].setContentsY(y);
+}
+
+void LocationNavigator::updateCurrentElement()
+{
+ const KFileItem *item = m_view->currentFileItem();
+
+ if (item)
+ m_history[m_historyIndex].setCurrentFilename(item->name());
+
+ m_history[m_historyIndex].setContentsX(m_view->contentsX());
+ m_history[m_historyIndex].setContentsY(m_view->contentsY());
+}
+
+}
+
+}
+
+#include "locationnavigator.moc"
+
diff --git a/kftpgrabber/src/widgets/browser/locationnavigator.h b/kftpgrabber/src/widgets/browser/locationnavigator.h
new file mode 100644
index 0000000..475a3af
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/locationnavigator.h
@@ -0,0 +1,210 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETS_BROWSERLOCATIONNAVIGATOR_H
+#define KFTPWIDGETS_BROWSERLOCATIONNAVIGATOR_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include <kurl.h>
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class DetailsView;
+
+/**
+ * This class contains the current navigational history and enables
+ * moving through it.
+ *
+ * @author Jernej Kos
+ */
+class LocationNavigator : public QObject {
+Q_OBJECT
+public:
+ /**
+ * An Element instance represents one history element. The class contains
+ * information about the URL, the selected item and the contents position.
+ */
+ class Element {
+ public:
+ /**
+ * Class constructor.
+ */
+ Element();
+
+ /**
+ * Class constructor.
+ *
+ * @param url Element's URL
+ */
+ Element(const KURL &url);
+
+ /**
+ * Returns the element's URL.
+ */
+ const KURL &url() const { return m_url; }
+
+ /**
+ * Set currently selected filename.
+ *
+ * @param filename The filename of the currently selected item
+ */
+ void setCurrentFilename(const QString &filename) { m_currentFilename = filename; }
+
+ /**
+ * Returns the selected filename.
+ */
+ const QString &currentFilename() const { return m_currentFilename; }
+
+ /**
+ * Set current contents X position.
+ *
+ * @param x Contents X position
+ */
+ void setContentsX(int x) { m_contentsX = x; }
+
+ /**
+ * Returns the saved contents X position.
+ */
+ int contentsX() const { return m_contentsX; }
+
+ /**
+ * Set current contents Y position.
+ *
+ * @param y Contents Y position
+ */
+ void setContentsY(int y) { m_contentsY = y; }
+
+ /**
+ * Returns the saved contents Y position.
+ */
+ int contentsY() const { return m_contentsY; }
+ private:
+ KURL m_url;
+ QString m_currentFilename;
+
+ int m_contentsX;
+ int m_contentsY;
+ };
+
+ /**
+ * Class constructor.
+ *
+ * @param view Parent view
+ */
+ LocationNavigator(DetailsView *view);
+
+ /**
+ * Set a new current URL. Calling this will emit the urlChanged signal.
+ *
+ * @param url Wanted URL
+ */
+ void setUrl(const KURL &url);
+
+ /**
+ * Returns the current URL.
+ */
+ const KURL &url() const;
+
+ /**
+ * Returns the current history elements.
+ *
+ * @param index Variable to save the current history position to
+ * @return Current history element list
+ */
+ const QValueList<Element> history(int &index) const;
+
+ /**
+ * Go one history hop back.
+ */
+ void goBack();
+
+ /**
+ * Go one history hop forward.
+ */
+ void goForward();
+
+ /**
+ * Go up in the directory structure.
+ */
+ void goUp();
+
+ /**
+ * Go the the predefined home URL.
+ */
+ void goHome();
+
+ /**
+ * Set the home URL.
+ *
+ * @param url URL to use as home URL
+ */
+ void setHomeUrl(const KURL &url) { m_homeUrl = url; }
+
+ /**
+ * Clear current history.
+ */
+ void clear();
+signals:
+ /**
+ * This signal is emitted whenever the current URL changes.
+ *
+ * @param url The new URL
+ */
+ void urlChanged(const KURL &url);
+
+ /**
+ * This signal is emitted whenever the history is updated.
+ */
+ void historyChanged();
+private slots:
+ void slotContentsMoved(int x, int y);
+private:
+ DetailsView *m_view;
+ int m_historyIndex;
+ QValueList<Element> m_history;
+ KURL m_homeUrl;
+
+ void updateCurrentElement();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/propsplugin.cpp b/kftpgrabber/src/widgets/browser/propsplugin.cpp
new file mode 100644
index 0000000..b6e688d
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/propsplugin.cpp
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "browser/propsplugin.h"
+#include "kftpsession.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qstyle.h>
+#include <qgroupbox.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kseparator.h>
+#include <ksqueezedtextlabel.h>
+
+using namespace KFTPEngine;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+PropsPlugin::PropsPlugin(KPropertiesDialog *props, KFileItemList items)
+ : KPropsDlgPlugin(props)
+{
+ QFrame *frame = properties->addPage(i18n("&General"));
+ frame->setMinimumWidth(320);
+ frame->setMinimumHeight(300);
+
+ // Some differences between a single file and multiple files
+ KFileItem *item = items.at(0);
+ KURL fileUrl = item->url();
+ filesize_t fileSize = item->size();
+ QString nameText;
+ QString iconText;
+ QString mimeComment;
+
+ if (items.count() == 1) {
+ bool isDir = false;
+
+ // Guess file type
+ if (item->isDir()) {
+ iconText = "folder";
+ isDir = true;
+ mimeComment = i18n("Remote folder");
+ } else if (item->isLink()) {
+ // We can't know if the sym-linked file is realy a directory, but most of
+ // the time it is. So if we can't determine the MIME type, set it to directory.
+ KMimeType::Ptr mimeType = KMimeType::findByURL(fileUrl, 0, false, true);
+
+ if (mimeType->name() == KMimeType::defaultMimeType()) {
+ iconText = "folder";
+ isDir = true;
+ mimeComment = i18n("Remote folder");
+ } else {
+ iconText = mimeType->icon(QString::null, false);
+ mimeComment = mimeType->comment();
+ }
+ } else {
+ KMimeType::Ptr mimeType = KMimeType::findByURL(fileUrl, 0, false, true);
+ iconText = mimeType->icon(QString::null, false);
+ mimeComment = mimeType->comment();
+ }
+
+ if (mimeComment.isEmpty()) {
+ mimeComment = i18n("Unknown");
+ }
+
+ nameText = item->name();
+ } else {
+ // Count files and folders selected
+ int countFiles = 0;
+ int countFolders = 0;
+ fileSize = 0;
+
+ KFileItemListIterator i(items);
+ for (; i.current(); ++i) {
+ if ((*i)->isDir())
+ countFolders++;
+ else
+ countFiles++;
+
+ fileSize += (*i)->size();
+ }
+
+ iconText = "kmultiple";
+ nameText = KIO::itemsSummaryString(countFiles + countFolders, countFiles, countFolders, 0, false);
+ }
+
+ QVBoxLayout *vbl = new QVBoxLayout(frame, 0, KDialog::spacingHint(), "vbl");
+ QGridLayout *grid = new QGridLayout(0, 3);
+ grid->setColStretch(0, 0);
+ grid->setColStretch(1, 0);
+ grid->setColStretch(2, 1);
+ grid->addColSpacing(1, KDialog::spacingHint());
+ vbl->addLayout(grid);
+
+ // Display file name and icon
+ QLabel *iconLabel = new QLabel(frame);
+ int bsize = 66 + 2 * iconLabel->style().pixelMetric(QStyle::PM_ButtonMargin);
+ iconLabel->setFixedSize(bsize, bsize);
+ iconLabel->setPixmap(DesktopIcon(iconText));
+ grid->addWidget(iconLabel, 0, 0, AlignLeft);
+
+ QLabel *nameLabel = new QLabel(frame);
+ nameLabel->setText(nameText);
+ grid->addWidget(nameLabel, 0, 2);
+
+ KSeparator *sep = new KSeparator(KSeparator::HLine, frame);
+ grid->addMultiCellWidget(sep, 2, 2, 0, 2);
+
+ // Display file information
+ QLabel *l;
+ int currentRow = 3;
+
+ if (items.count() == 1) {
+ l = new QLabel(i18n("Type:"), frame);
+ grid->addWidget(l, currentRow, 0);
+
+ l = new QLabel(mimeComment, frame);
+ grid->addWidget(l, currentRow++, 2);
+ }
+
+ l = new QLabel(i18n("Location:"), frame);
+ grid->addWidget(l, currentRow, 0);
+
+ l = new KSqueezedTextLabel(frame);
+ l->setText(fileUrl.directory());
+ grid->addWidget(l, currentRow++, 2);
+
+ l = new QLabel(i18n("Size:"), frame);
+ grid->addWidget(l, currentRow, 0);
+
+ l = new QLabel(frame);
+ grid->addWidget(l, currentRow++, 2);
+
+ l->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(fileSize))
+ .arg(KGlobal::locale()->formatNumber(fileSize, 0)));
+
+ sep = new KSeparator(KSeparator::HLine, frame);
+ grid->addMultiCellWidget(sep, currentRow, currentRow, 0, 2);
+ currentRow++;
+
+ // Display modification time
+ if (items.count() == 1) {
+ l = new QLabel(i18n("Created:"), frame);
+ grid->addWidget(l, currentRow, 0);
+
+ QDateTime dt;
+ dt.setTime_t(item->time(KIO::UDS_MODIFICATION_TIME));
+ l = new QLabel(KGlobal::locale()->formatDateTime(dt), frame);
+ grid->addWidget(l, currentRow++, 2);
+ }
+
+ vbl->addStretch(1);
+}
+
+void PropsPlugin::applyChanges()
+{
+}
+
+mode_t PermissionsPropsPlugin::fperm[3][4] = {
+ {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
+ {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
+ {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
+};
+
+PermissionsPropsPlugin::PermissionsPropsPlugin(KPropertiesDialog *_props, KFileItemList items, KFTPSession::Session *session)
+ : KPropsDlgPlugin(_props),
+ m_items(items),
+ m_session(session),
+ m_cbRecursive(0)
+{
+ QFrame *frame = properties->addPage(i18n("&Permissions"));
+ frame->setMinimumWidth(320);
+ frame->setMinimumHeight(300);
+
+ // Some differences between a single file and multiple files
+ KFileItem *item = items.at(0);
+ KURL fileUrl = item->url();
+ bool isDir = false;
+
+ if (items.count() == 1) {
+ // Guess file type
+ if (item->isDir()) {
+ isDir = true;
+ } else if (item->isLink()) {
+ // We can't know if the sym-linked file is realy a directory, but most of
+ // the time it is. So if we can't determine the MIME type, set it to directory.
+ KMimeType::Ptr mimeType = KMimeType::findByURL(fileUrl, 0, false, true);
+
+ if (mimeType->name() == KMimeType::defaultMimeType())
+ isDir = true;
+ }
+ } else {
+ // Check for directories
+ KFileItemListIterator i(items);
+ for (; i.current(); ++i) {
+ if ((*i)->isDir()) {
+ isDir = true;
+ break;
+ }
+ }
+ }
+
+ QBoxLayout *box = new QVBoxLayout(frame, 0, KDialog::spacingHint());
+
+ QGroupBox *gb = new QGroupBox(0, Qt::Vertical, i18n("Access Permissions"), frame);
+ gb->layout()->setSpacing(KDialog::spacingHint());
+ gb->layout()->setMargin(KDialog::marginHint());
+ box->addWidget(gb);
+
+ QGridLayout *gl = new QGridLayout(gb->layout(), 6, 6, 15);
+
+ QLabel *l = new QLabel(i18n("Class"), gb);
+ gl->addWidget(l, 1, 0);
+
+ if (isDir)
+ l = new QLabel(i18n("Show\nEntries"), gb);
+ else
+ l = new QLabel(i18n("Read"), gb);
+ gl->addWidget(l, 1, 1);
+
+ if (isDir)
+ l = new QLabel(i18n("Write\nEntries"), gb);
+ else
+ l = new QLabel(i18n("Write"), gb);
+ gl->addWidget(l, 1, 2);
+
+ if (isDir)
+ l = new QLabel(i18n("Enter folder", "Enter"), gb);
+ else
+ l = new QLabel(i18n("Exec"), gb);
+
+ QSize size = l->sizeHint();
+ size.setWidth(size.width() + 15);
+ l->setFixedSize(size);
+ gl->addWidget(l, 1, 3);
+
+ l = new QLabel(i18n("Special"), gb);
+ gl->addMultiCellWidget(l, 1, 1, 4, 5);
+
+ l = new QLabel(i18n("User"), gb);
+ gl->addWidget(l, 2, 0);
+
+ l = new QLabel(i18n("Group"), gb);
+ gl->addWidget(l, 3, 0);
+
+ l = new QLabel(i18n("Others"), gb);
+ gl->addWidget(l, 4, 0);
+
+ l = new QLabel(i18n("Set UID"), gb);
+ gl->addWidget(l, 2, 5);
+
+ l = new QLabel(i18n("Set GID"), gb);
+ gl->addWidget(l, 3, 5);
+
+ l = new QLabel(i18n("Sticky"), gb);
+ gl->addWidget(l, 4, 5);
+
+ mode_t permissions = item->permissions();
+
+ // Display checkboxes
+ for (int row = 0; row < 3; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ QCheckBox *cb = new QCheckBox(gb);
+ connect(cb, SIGNAL(clicked()), this, SLOT(setDirty()));
+ m_permsCheck[row][col] = cb;
+ cb->setChecked(permissions & fperm[row][col]);
+
+ gl->addWidget(cb, row + 2, col + 1);
+ }
+ }
+
+ gl->setColStretch(6, 10);
+ box->addStretch(10);
+
+ if (isDir) {
+ m_cbRecursive = new QCheckBox(i18n("Apply changes to all subfolders and their contents"), frame);
+ connect(m_cbRecursive, SIGNAL(clicked()), this, SLOT(changed()));
+ box->addWidget(m_cbRecursive);
+ }
+}
+
+void PermissionsPropsPlugin::applyChanges()
+{
+ // Generate new permissions =)
+ int newPerms[4] = {0,};
+
+ for (int row = 0; row < 3; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ if (!m_permsCheck[row][col]->isChecked()) continue;
+
+ int x = col < 3 ? col : row;
+ int c = 0;
+
+ switch (x) {
+ case 0: c = 4; break;
+ case 1: c = 2; break;
+ case 2: c = 1; break;
+ }
+
+ if (col < 3) {
+ newPerms[row + 1] += c;
+ } else {
+ newPerms[0] += c;
+ }
+ }
+ }
+
+ // Actually do a chmod
+ int mode = newPerms[0] * 1000 + newPerms[1] * 100 + newPerms[2] * 10 + newPerms[3];
+ bool recursive = m_cbRecursive && m_cbRecursive->isChecked();
+
+ KFileItemListIterator i(m_items);
+ for (; i.current(); ++i) {
+ if ((*i)->isDir())
+ m_session->getClient()->chmod((*i)->url(), mode, recursive);
+ else
+ m_session->getClient()->chmod((*i)->url(), mode);
+ }
+}
+
+}
+
+}
+
+#include "propsplugin.moc"
diff --git a/kftpgrabber/src/widgets/browser/propsplugin.h b/kftpgrabber/src/widgets/browser/propsplugin.h
new file mode 100644
index 0000000..9283f85
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/propsplugin.h
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPFILEPROPSPLUGIN_H
+#define KFTPFILEPROPSPLUGIN_H
+
+#include "engine/directorylisting.h"
+
+#include <qcheckbox.h>
+#include <kpropertiesdialog.h>
+
+namespace KFTPSession {
+ class Session;
+}
+
+namespace KFTPEngine {
+ class Thread;
+}
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+/**
+ * This is a plugin for displaying remote file properties using the
+ * standard KDE file properties dialog.
+ *
+ * @author Jernej Kos
+ */
+class PropsPlugin : public KPropsDlgPlugin
+{
+Q_OBJECT
+public:
+ PropsPlugin(KPropertiesDialog *props, KFileItemList items);
+
+ void applyChanges();
+};
+
+/**
+ * This is a plugin for displaying remote file permissions and their
+ * changing using the standard KDE file properties dialog.
+ *
+ * @author Jernej Kos
+ */
+class PermissionsPropsPlugin : public KPropsDlgPlugin
+{
+Q_OBJECT
+public:
+ PermissionsPropsPlugin(KPropertiesDialog *props, KFileItemList items, KFTPSession::Session *session);
+
+ void applyChanges();
+private:
+ KFileItemList m_items;
+ KFTPSession::Session *m_session;
+
+ static mode_t fperm[3][4];
+ QString m_perms[3];
+ QCheckBox *m_permsCheck[3][4];
+ QCheckBox *m_cbRecursive;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/treeview.cpp b/kftpgrabber/src/widgets/browser/treeview.cpp
new file mode 100644
index 0000000..0f2a824
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/treeview.cpp
@@ -0,0 +1,520 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "browser/treeview.h"
+#include "browser/view.h"
+
+#include "engine/thread.h"
+
+#include "misc.h"
+
+#include <qheader.h>
+#include <qtimer.h>
+
+#include <kio/job.h>
+#include <kurl.h>
+#include <kurldrag.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+/*
+ * TreeViewItem class
+ */
+
+TreeViewItem::TreeViewItem(TreeView *parent, const KURL &url)
+ : QListViewItem(parent),
+ m_tree(parent),
+ m_url(url),
+ m_dirty(false)
+{
+ // Set the name
+ setText(0, path2Name(url.path(-1)));
+}
+
+TreeViewItem::TreeViewItem(TreeView *tree, QListViewItem *parent, const KURL &url)
+ : QListViewItem(parent),
+ m_tree(tree),
+ m_url(url),
+ m_dirty(false)
+{
+ // Set the name
+ setText(0, path2Name(url.path(-1)));
+}
+
+TreeViewItem::~TreeViewItem()
+{
+ // The item should be removed from the list as well
+ m_tree->m_treeItems.remove(m_url.path());
+}
+
+int TreeViewItem::compare(QListViewItem *i, int col, bool) const
+{
+ // Hidden files must be on top
+ if (m_url.fileName().at(0) == '.')
+ return -1;
+
+ return QListViewItem::compare(i, col, false);
+}
+
+/*
+ * TreeView class
+ */
+
+TreeView::TreeView(QWidget *parent)
+ : KListView(parent)
+{
+ // Create the columns
+ addColumn(i18n("Directory"));
+
+ // General tree settings
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setMinimumWidth(150);
+ setRootIsDecorated(false);
+ header()->hide();
+
+ // Reset the view
+ resetView(KURL("/"));
+ m_noItemOpen = false;
+
+ // Connect signals
+ connect(this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*)));
+ connect(this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotDoubleClicked(QListViewItem*)));
+
+ // Drag and drop
+ m_dropItem = 0L;
+}
+
+
+TreeView::~TreeView()
+{
+ // Free the item index
+ m_treeItems.clear();
+}
+
+void TreeView::resetView(const KURL &url)
+{
+ // Free the item index
+ m_treeItems.clear();
+
+ // Clear the view
+ clear();
+ header()->resizeSection(0, 0);
+
+ // Create the root item
+ TreeViewItem *rootItem = new TreeViewItem(this, remoteUrl("/", url));
+ rootItem->setText(0, url.isLocalFile() ? i18n("Root directory") : url.host());
+ rootItem->setPixmap(0, loadSmallPixmap("folder_red"));
+ setOpen(rootItem, true);
+}
+
+int TreeView::openUrl(const KURL &url, QListViewItem *parent)
+{
+ // Root item should always be open
+ setOpen(firstChild(), true);
+
+ // Go trough all items in the list and try to find the url
+ QListViewItem *item;
+ if (!parent)
+ item = firstChild();
+ else
+ item = parent;
+
+ while (item) {
+ // Check if the item is correct
+ if (static_cast<TreeViewItem*>(item)->m_url.path(-1) == url.path(-1)) {
+ // We have found it
+ ensureItemVisible(item);
+ setCurrentItem(item);
+ setSelected(item, true);
+
+ // Change viewport
+ QRect r = itemRect(item);
+ if (r.isValid()) {
+ int x, y;
+ viewportToContents(contentsX(), r.y(), x, y);
+ setContentsPos(x, y);
+ }
+
+ return 1;
+ }
+
+ if (item->firstChild()) {
+ // If item has children, go after them
+ if (openUrl(url, item->firstChild()) == 1)
+ return 1;
+ }
+
+ item = item->nextSibling();
+ }
+
+ return 0;
+}
+
+QListViewItem *TreeView::findItem(QListViewItem *parent, const QString &name)
+{
+ QListViewItem *item = parent->firstChild();
+
+ while (item) {
+ if (item->text(0) == name)
+ return item;
+
+ item = item->nextSibling();
+ }
+
+ // If nothing is found, parent should be returned
+ return parent;
+}
+
+void TreeView::createFolder(const KURL &url, QPixmap icon)
+{
+ int numDirs = url.path(1).contains('/', false);
+ QListViewItem *item = firstChild();
+
+ for (int i = 1; i < numDirs; i++) {
+ QString itemUrl = url.path().section('/', 0, i);
+
+ if (m_treeItems[itemUrl]) {
+ // Item exists for this URL
+ item = m_treeItems[itemUrl];
+ } else {
+ // Item not yet created
+ KURL tmp = url;
+ tmp.setPath(itemUrl);
+
+ item = new TreeViewItem(this, item, tmp);
+ if (i == numDirs - 1)
+ item->setPixmap(0, icon);
+ else
+ item->setPixmap(0, loadSmallPixmap("folder"));
+
+ m_treeItems.insert(itemUrl, static_cast<TreeViewItem*>(item));
+ }
+
+ // Mark it as dirty
+ static_cast<TreeViewItem*>(item)->m_dirty = true;
+
+ // Open it
+ if (!m_noItemOpen)
+ setOpen(item, true);
+ }
+
+ // Root item should always be open
+ setOpen(firstChild(), true);
+}
+
+void TreeView::endUpdate(const KURL &url)
+{
+ // Remove all items in the selected dir, not marked as dirty
+ TreeViewItem *top = static_cast<TreeViewItem*>(firstChild());
+ TreeViewItem *tmp = 0L;
+ int numDirs = url.path(1).contains('/', false);
+
+ if (url.path() == "/") {
+ TreeViewItem *i = static_cast<TreeViewItem*>(top->firstChild());
+
+ while (i) {
+ tmp = static_cast<TreeViewItem*>(i->nextSibling());
+
+ if (!i->m_dirty) {
+ // Remove item from the index
+ delete i;
+ } else {
+ i->m_dirty = false;
+ }
+
+ i = tmp;
+ }
+
+ return;
+ }
+
+ // URL for items
+ KURL itemURL = url;
+ itemURL.setPath("/");
+
+ for (int i = 1; i < numDirs; i++) {
+ QString sect = url.path().section('/', i, i);
+ itemURL.setPath(url.path().section('/', 0, i));
+
+ if (!m_treeItems[itemURL.path()]) {
+ // Item not yet created
+ return;
+ } else {
+ // Item is already present
+ top = m_treeItems[itemURL.path()];
+
+ // Check for URL match
+ if (itemURL.path(-1) == url.path(-1)) {
+ // URL match, delete the item's children
+ TreeViewItem *i = static_cast<TreeViewItem*>(top->firstChild());
+
+ while (i) {
+ tmp = static_cast<TreeViewItem*>(i->nextSibling());
+
+ if (!i->m_dirty) {
+ // Remove item from the index
+ delete i;
+ } else {
+ i->m_dirty = false;
+ }
+
+ i = tmp;
+ }
+ return;
+ }
+ }
+ }
+}
+
+void TreeView::clearFolder(const KURL &url)
+{
+ // Remove url's children
+ QListViewItem *top = firstChild();
+ int numDirs = url.path(1).contains('/', false);
+
+ if (url.path() == "/") {
+ QListViewItem *i = top->firstChild();
+
+ while (i) {
+ // Remove item from the index
+ delete i;
+
+ i = top->firstChild();
+ }
+
+ return;
+ }
+
+ // URL for items
+ KURL itemURL = url;
+ itemURL.setPath("/");
+
+ for (int i = 1; i < numDirs; i++) {
+ QString sect = url.path().section('/', i, i);
+ itemURL.setPath(url.path().section('/', 0, i));
+
+ if (!m_treeItems[itemURL.path()]) {
+ // Item not yet created
+ return;
+ } else {
+ // Item is already present
+ top = m_treeItems[itemURL.path()];
+
+ // Check for URL match
+ if (itemURL.path(-1) == url.path(-1)) {
+ // URL match, delete the item's children
+ QListViewItem *i = top->firstChild();
+
+ while (i) {
+ // Remove item from the index
+ delete i;
+
+ i = top->firstChild();
+ }
+ return;
+ }
+ }
+ }
+}
+
+void TreeView::removeFolder(const KURL &url)
+{
+ // Removes a folder at url
+ QListViewItem *top = firstChild();
+ int numDirs = url.path(1).contains('/', false);
+
+ // URL for items
+ KURL itemURL = url;
+ itemURL.setPath("/");
+
+ for (int i = 1; i < numDirs; i++) {
+ QString sect = url.path().section('/', i, i);
+ itemURL.setPath(url.path().section('/', 0, i));
+
+ if (!m_treeItems[itemURL.path()]) {
+ // Item not yet created
+ return;
+ } else {
+ // Item is already present
+ top = m_treeItems[itemURL.path()];
+
+ // Check for URL match
+ if (itemURL.path(-1) == url.path(-1)) {
+ // Remove item from the index
+ delete top;
+
+ return;
+ }
+ }
+ }
+}
+
+void TreeView::slotClicked(QListViewItem *item)
+{
+ if (!item)
+ return;
+
+ m_noItemOpen = true;
+ emit pathChanged(static_cast<TreeViewItem*>(item)->m_url);
+ m_noItemOpen = false;
+}
+
+void TreeView::slotDoubleClicked(QListViewItem *item)
+{
+ if (!item)
+ return;
+
+ setOpen(item, !item->isOpen());
+}
+
+void TreeView::startDrag()
+{
+ dragObject()->drag();
+}
+
+bool TreeView::acceptDrag(QDropEvent *e)
+{
+ return KURLDrag::canDecode(e) &&
+ ( e->action() == QDropEvent::Copy
+ || e->action() == QDropEvent::Move
+ || e->action() == QDropEvent::Link )
+ && acceptDrops()
+ && dragEnabled();
+}
+
+QDragObject *TreeView::dragObject()
+{
+ TreeViewItem *item = static_cast<TreeViewItem*>(selectedItem());
+
+ // Set the correct pixmap
+ QPixmap pix = *item->pixmap(0);
+
+ KURL::List urls;
+ urls.append(item->m_url);
+
+ KIO::MetaData meta;
+ meta.insert(item->m_url.htmlURL().local8Bit(), "D:0");
+
+ m_dragObject = new KURLDrag(urls, meta, this, name());
+ m_dragObject->setPixmap(pix, QPoint(pix.width() / 2, pix.height() / 2));
+
+ return m_dragObject;
+}
+
+void TreeView::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ if (!acceptDrag(e)) {
+ e->accept(false);
+ return;
+ }
+
+ e->acceptAction();
+ QListViewItem *i = itemAt(contentsToViewport(e->pos()));
+ if (i)
+ m_dropItem = i;
+}
+
+void TreeView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (!acceptDrag(e)) {
+ e->accept(false);
+ return;
+ }
+
+ e->acceptAction();
+ QListViewItem *i = itemAt(contentsToViewport(e->pos()));
+
+ if (i && i != m_dropItem)
+ m_dropItem = i;
+}
+
+void TreeView::contentsDragLeaveEvent(QDragLeaveEvent*)
+{
+ m_dropItem = 0L;
+}
+
+void TreeView::contentsDropEvent(QDropEvent *e)
+{
+ if (!m_dropItem)
+ return;
+
+ if (!acceptDrag(e)) {
+ e->acceptAction(false);
+ return;
+ }
+ e->acceptAction();
+
+ // Decode the data and try to init transfer
+ KIO::MetaData meta;
+ KURL::List urls;
+ KURLDrag::decode(e, urls, meta);
+
+ // Move the specified file(s)
+ for (KURL::List::iterator i = urls.begin(); i != urls.end(); ++i) {
+ KURL destUrl = static_cast<TreeViewItem*>(m_dropItem)->m_url;
+
+ if (destUrl != (*i)) {
+ if (destUrl.isLocalFile() != (*i).isLocalFile()) {
+ // TODO Queue up a transfer
+ } else {
+ // Rename/move the file
+ destUrl.addPath((*i).fileName());
+
+ if ((*i).isLocalFile()) {
+ KIO::move((*i), destUrl, false);
+
+ // Reload the listing
+ static_cast<View*>(parent()->parent())->reload();
+ } else {
+ static_cast<View*>(parent()->parent())->m_ftpClient->rename((*i), destUrl);
+ }
+ }
+ }
+ }
+
+ // Invalidate the drop item
+ m_dropItem = 0L;
+}
+
+}
+
+}
+
+#include "treeview.moc"
diff --git a/kftpgrabber/src/widgets/browser/treeview.h b/kftpgrabber/src/widgets/browser/treeview.h
new file mode 100644
index 0000000..220ac88
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/treeview.h
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPFILEDIRTREEVIEW_H
+#define KFTPFILEDIRTREEVIEW_H
+
+#include <klistview.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include <qpixmap.h>
+#include <qdict.h>
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class TreeViewItem;
+
+/**
+ * This class represents a tree view showing the directory structure. It
+ * should usually be used together with a ListView.
+ *
+ * @author Jernej Kos
+ */
+class TreeView : public KListView
+{
+Q_OBJECT
+friend class ListView;
+friend class TreeViewItem;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent widget
+ */
+ TreeView(QWidget *parent);
+
+ /**
+ * Class destructor.
+ */
+ ~TreeView();
+
+ /**
+ * Open an URL.
+ *
+ * @param url The url to open
+ */
+ int openUrl(const KURL &url, QListViewItem *parent = 0);
+
+ /**
+ * Remove the folder's child items.
+ *
+ * @param url The folder's URL
+ */
+ void clearFolder(const KURL &url);
+
+ /**
+ * Remove the folder and it's child items.
+ *
+ * @param url The folder's URL
+ */
+ void removeFolder(const KURL &url);
+
+ /**
+ * Create a new folder identified by it's URL and any folders missing
+ * in the hierarchy.
+ *
+ * @param url The folder's URL
+ * @param icon The icon to use
+ */
+ void createFolder(const KURL &url, QPixmap icon);
+
+ /**
+ * Clear all items in the view and create a new root item.
+ *
+ * @param url The root's new URL
+ */
+ void resetView(const KURL &url);
+
+ /**
+ * End item update and clear all non-dirty items.
+ *
+ * @param url The url to purge
+ */
+ void endUpdate(const KURL &url);
+private:
+ QDict<TreeViewItem> m_treeItems;
+ bool m_noItemOpen;
+
+ /* Drag & drop support */
+ QListViewItem *m_dropItem;
+ QDragObject *m_dragObject;
+
+ QDragObject *dragObject();
+ void startDrag();
+ bool acceptDrag(QDropEvent *e);
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent*);
+ void contentsDropEvent(QDropEvent *e);
+protected:
+ QListViewItem *findItem(QListViewItem *parent, const QString &name);
+private slots:
+ void slotClicked(QListViewItem *item);
+ void slotDoubleClicked(QListViewItem *item);
+signals:
+ void pathChanged(const KURL &url);
+};
+
+/**
+ * This class represents an item in the tree view.
+ *
+ * @author Jernej Kos
+ */
+class TreeViewItem : public QListViewItem
+{
+friend class TreeView;
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent tree view
+ * @param url This item's URL
+ */
+ TreeViewItem(TreeView *parent, const KURL &url);
+
+ /**
+ * Class constructor.
+ *
+ * @param tree The tree view instance
+ * @param parent The parent list view item
+ * @param url This item's URL
+ */
+ TreeViewItem(TreeView *tree, QListViewItem *parent, const KURL &url);
+
+ /**
+ * Class destructor.
+ */
+ ~TreeViewItem();
+protected:
+ /**
+ * @overload
+ * Reimplemented from QListViewItem to provide proper comparation.
+ */
+ int compare(QListViewItem *i, int col, bool) const;
+private:
+ TreeView *m_tree;
+ KURL m_url;
+ bool m_dirty;
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/browser/view.cpp b/kftpgrabber/src/widgets/browser/view.cpp
new file mode 100644
index 0000000..4b3b0e5
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/view.cpp
@@ -0,0 +1,456 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "browser/view.h"
+#include "browser/detailsview.h"
+#include "browser/treeview.h"
+#include "browser/actions.h"
+#include "browser/filterwidget.h"
+
+#include "kftpbookmarks.h"
+#include "misc/config.h"
+#include "kftpsession.h"
+#include "ftpsocket.h"
+
+#include "misc.h"
+
+#include <klocale.h>
+#include <ktoolbar.h>
+#include <kstatusbar.h>
+#include <kcombobox.h>
+#include <kaction.h>
+#include <kmessagebox.h>
+#include <ksslinfodlg.h>
+#include <klineedit.h>
+#include <kpixmapprovider.h>
+
+#include <kio/job.h>
+
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+using namespace KFTPGrabberBase;
+using namespace KFTPEngine;
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class HistoryPixmapProvider : public KPixmapProvider
+{
+public:
+ QPixmap pixmapFor(const QString&, int)
+ {
+ return loadSmallPixmap("folder");
+ }
+};
+
+View::View(QWidget *parent, const char *name, KFTPEngine::Thread *client, KFTPSession::Session *session)
+ : QWidget(parent, name),
+ m_session(session),
+ m_ftpClient(client)
+{
+ m_connTimer = new QTimer(this);
+
+ // Create the GUI
+ init();
+ populateToolbar();
+
+ // Hide/show the tree acoording to configuration
+ setTreeVisible(KFTPCore::Config::showTree());
+
+ // Let us be up to date with bookmark changes
+ connect(KFTPBookmarks::Manager::self(), SIGNAL(update()), this, SLOT(updateBookmarks()));
+
+ // Some other stuff
+ connect(m_ftpClient->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
+ connect(m_connTimer, SIGNAL(timeout()), this, SLOT(slotDurationUpdate()));
+
+ // Config updates to hide/show the tree
+ connect(KFTPCore::Config::self(), SIGNAL(configChanged()), this, SLOT(slotConfigUpdate()));
+}
+
+View::~View()
+{
+}
+
+void View::init()
+{
+ // Init actions
+ m_actions = new Actions(this);
+ m_actions->initActions();
+
+ // Layout
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ // Create the toolbars
+ m_toolBarFirst = new KToolBar(this, "first toolbar", false, false);
+ m_toolBarSecond = new KToolBar(this, "second toolbar", false, false);
+ m_searchToolBar = new KToolBar(this, "search toolbar", false, false);
+
+ m_toolBarFirst->setEnableContextMenu(false);
+ m_toolBarFirst->setMovingEnabled(false);
+ m_toolBarFirst->setFullSize(true);
+
+ m_toolBarSecond->setEnableContextMenu(false);
+ m_toolBarSecond->setMovingEnabled(false);
+ m_toolBarSecond->setFullSize(true);
+
+ m_searchToolBar->setEnableContextMenu(false);
+ m_searchToolBar->setMovingEnabled(false);
+ m_searchToolBar->setFullSize(true);
+
+ QLabel *filterLabel = new QLabel(i18n("Filter: "), m_searchToolBar);
+ m_searchToolBar->insertWidget(1, 35, filterLabel);
+
+ // Create the erase button
+ m_toolBarSecond->insertButton(QApplication::reverseLayout() ? "clear_left" :"locationbar_erase", 0, SIGNAL(clicked()), this, SLOT(slotHistoryEraseClicked()), true);
+
+ // Create the labels
+ QLabel *pathLabel = new QLabel(i18n("Path: "), m_toolBarSecond);
+ m_toolBarSecond->insertWidget(1, 35, pathLabel);
+
+ // Create the history combo
+ m_historyCombo = new KHistoryCombo(true, m_toolBarSecond);
+ m_toolBarSecond->insertWidget(2, 0, m_historyCombo);
+ m_historyCombo->setPixmapProvider(new HistoryPixmapProvider());
+ m_historyCombo->setMaxCount(25);
+ m_historyCombo->setSizeLimit(25);
+ m_historyCombo->setDuplicatesEnabled(false);
+ m_historyCombo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+
+ connect(m_historyCombo, SIGNAL(activated(const QString&)), this, SLOT(slotHistoryActivated(const QString&)));
+
+ // Do some more stuff
+ m_toolBarSecond->setItemAutoSized(1, true);
+ m_toolBarSecond->setStretchableWidget(m_historyCombo);
+ m_toolBarSecond->updateRects(true);
+ m_toolBarFirst->updateRects(true);
+
+ // Create a splitter
+ m_splitter = new QSplitter(this);
+ m_splitter->setOpaqueResize(true);
+ m_splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+
+ // Create a status bar
+ QHBoxLayout *statusLayout = new QHBoxLayout(this);
+
+ m_connDurationMsg = new QLabel(this);
+ m_connDurationMsg->setAlignment(AlignAuto | AlignVCenter | AlignHCenter | ExpandTabs);
+ m_connDurationMsg->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ m_connDurationMsg->setMinimumWidth(100);
+
+ m_sslIcon = new QPushButton(this);
+ m_sslIcon->setFlat(true);
+ m_sslIcon->setIconSet(SmallIconSet("decrypted"));
+ m_sslIcon->setEnabled(false);
+
+ connect(m_sslIcon, SIGNAL(clicked()), this, SLOT(slotDisplayCertInfo()));
+
+ m_statusMsg = new QLabel(this);
+ m_statusMsg->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ m_statusMsg->setText(i18n("Idle."));
+
+ statusLayout->addWidget(m_statusMsg, 1);
+ statusLayout->addWidget(m_connDurationMsg);
+ statusLayout->addWidget(m_sslIcon);
+
+ // Add toolbars to the layout
+ layout->addWidget(m_toolBarFirst);
+ layout->addWidget(m_toolBarSecond);
+ layout->addWidget(m_splitter, 10);
+ layout->addWidget(m_searchToolBar);
+ layout->addLayout(statusLayout);
+
+ // Now add some stuff to the splitter
+ m_detailsView = new DetailsView(m_splitter, this, m_session);
+ m_splitter->moveToLast(m_detailsView);
+
+ connect(m_detailsView->locationNavigator(), SIGNAL(historyChanged()), this, SLOT(slotHistoryChanged()));
+
+ // Create the tree view
+ m_tree = new TreeView(m_splitter);
+ m_splitter->moveToFirst(m_tree);
+
+ m_detailsView->setTreeView(m_tree);
+ m_detailsView->openUrl(KURL(KFTPCore::Config::defLocalDir()));
+
+ // Create the filter widget
+ m_searchFilter = new FilterWidget(m_searchToolBar, m_detailsView);
+ m_searchToolBar->setItemAutoSized(2, true);
+ m_searchToolBar->setStretchableWidget(m_searchFilter);
+
+ m_searchToolBar->updateRects(true);
+ m_searchToolBar->hide();
+}
+
+const KURL &View::url() const
+{
+ return m_detailsView->url();
+}
+
+const QValueList<LocationNavigator::Element> View::history(int &index) const
+{
+ return m_detailsView->history(index);
+}
+
+const KFileItemList *View::selectedItems() const
+{
+ return m_detailsView->KFileView::selectedItems();
+}
+
+KURL::List View::selectedURLs() const
+{
+ KURL::List urls;
+ const KFileItemList *list = selectedItems();
+
+ if (list) {
+ KFileItemListIterator i(*list);
+ KFileItem *item;
+
+ while ((item = i.current()) != 0) {
+ urls.append(item->url());
+ ++i;
+ }
+ }
+
+ return urls;
+}
+
+const KFileItemList *View::items() const
+{
+ return m_detailsView->KFileView::items();
+}
+
+void View::openUrl(const KURL &url)
+{
+ m_detailsView->openUrl(url);
+}
+
+void View::setShowHidden(bool value)
+{
+ m_detailsView->setShowHidden(value);
+}
+
+void View::setHomeUrl(const KURL &url)
+{
+ m_detailsView->setHomeUrl(url);
+}
+
+void View::goBack()
+{
+ m_detailsView->goBack();
+}
+
+void View::goForward()
+{
+ m_detailsView->goForward();
+}
+
+void View::goUp()
+{
+ m_detailsView->goUp();
+}
+
+void View::goHome()
+{
+ m_detailsView->goHome();
+}
+
+void View::reload()
+{
+ m_detailsView->reload();
+}
+
+void View::rename(const KURL &source, const QString &name)
+{
+ KURL dest(source.upURL());
+ dest.addPath(name);
+
+ if (source.isLocalFile())
+ KIO::rename(source, dest, false);
+ else
+ m_session->getClient()->rename(source, dest);
+}
+
+void View::slotConfigUpdate()
+{
+ setTreeVisible(KFTPCore::Config::showTree());
+}
+
+void View::setTreeVisible(bool visible)
+{
+ if (visible)
+ m_tree->show();
+ else
+ m_tree->hide();
+
+ m_actions->m_toggleTreeViewAction->setChecked(visible);
+}
+
+void View::populateToolbar()
+{
+ // Add the actions to the toolbar
+ m_actions->m_siteChangeAction->plug(m_toolBarFirst);
+
+ m_toolBarFirst->insertSeparator();
+
+ m_actions->m_goUpAction->plug(m_toolBarFirst);
+ m_actions->m_goBackAction->plug(m_toolBarFirst);
+ m_actions->m_goForwardAction->plug(m_toolBarFirst);
+ m_actions->m_reloadAction->plug(m_toolBarFirst);
+
+ m_toolBarFirst->insertSeparator();
+
+ m_actions->m_goHomeAction->plug(m_toolBarFirst);
+ m_actions->m_createDirAction->plug(m_toolBarFirst);
+
+ m_toolBarFirst->insertSeparator();
+
+ m_actions->m_abortAction->plug(m_toolBarFirst);
+ m_actions->m_toggleTreeViewAction->plug(m_toolBarFirst);
+ m_actions->m_toggleFilterAction->plug(m_toolBarFirst);
+
+ m_toolBarFirst->insertSeparator();
+
+ m_actions->m_moreActions->plug(m_toolBarFirst);
+}
+
+void View::updateActions()
+{
+ // Force action update
+ m_actions->updateActions();
+}
+
+void View::updateBookmarks()
+{
+ // Repopulate bookmarks menu on updates
+ m_actions->m_connectAction->popupMenu()->clear();
+ KFTPBookmarks::Manager::self()->guiPopulateBookmarksMenu(m_actions->m_connectAction, QDomNode(), false, m_session);
+}
+
+void View::slotHistoryActivated(const QString &text)
+{
+ KURL dest = url();
+ dest.setPath(text);
+
+ openUrl(dest);
+}
+
+void View::slotHistoryEraseClicked()
+{
+ m_historyCombo->setCurrentText(QString::null);
+ m_historyCombo->setFocus();
+}
+
+void View::slotHistoryChanged()
+{
+ /*m_historyCombo->insertItem(loadSmallPixmap("folder"), url().path(-1), 0);
+ m_historyCombo->setCurrentItem(0);*/
+ m_historyCombo->addToHistory(url().path(-1));
+}
+
+void View::slotDisplayCertInfo()
+{
+ if (m_ftpClient->socket()->protocolName() == "ftp" && m_ftpClient->socket()->isEncrypted()) {
+ KSSLInfoDlg *sslInfo = new KSSLInfoDlg(true, this);
+ sslInfo->exec();
+ } else if (m_ftpClient->socket()->protocolName() == "sftp") {
+ KMessageBox::information(this, i18n("This is a SSH encrypted connection. No certificate info is currently available."));
+ } else {
+ KSSLInfoDlg *sslInfo = new KSSLInfoDlg(false, this);
+ sslInfo->exec();
+ }
+}
+
+void View::slotDurationUpdate()
+{
+ m_connDuration = m_connDuration.addSecs(1);
+ m_connDurationMsg->setText(m_connDuration.toString("hh:mm:ss"));
+}
+
+void View::slotEngineEvent(KFTPEngine::Event *event)
+{
+ switch (event->type()) {
+ case Event::EventState: {
+ // Set new state
+ m_statusMsg->setText(event->getParameter(0).asString());
+ break;
+ }
+ case Event::EventConnect:
+ case Event::EventDisconnect: {
+ // Change encryption icon
+ m_sslIcon->setIconSet(SmallIconSet(m_ftpClient->socket()->isEncrypted() ? "encrypted" : "decrypted"));
+ m_sslIcon->setEnabled(m_ftpClient->socket()->isConnected());
+
+ // Start or stop the duration timer
+ if (m_ftpClient->socket()->isConnected()) {
+ m_connTimer->start(1000);
+ m_connDuration.setHMS(0, 0, 0);
+ } else {
+ m_connTimer->stop();
+ m_connDurationMsg->setText("");
+ }
+
+ // Reset selected charset to default
+ KPopupMenu *menu = m_actions->m_changeEncodingAction->popupMenu();
+ menu->setItemChecked(m_actions->m_defaultCharsetOption, true);
+ menu->setItemChecked(m_actions->m_curCharsetOption, false);
+ m_actions->m_curCharsetOption = m_actions->m_defaultCharsetOption;
+ break;
+ }
+ default: break;
+ }
+
+ if (m_ftpClient->socket()->isBusy()) {
+ m_tree->setEnabled(false);
+ m_detailsView->setEnabled(false);
+ m_toolBarSecond->setEnabled(false);
+ } else if (KFTPQueue::Manager::self()->getNumRunning(m_ftpClient->socket()->getCurrentUrl()) == 0) {
+ m_tree->setEnabled(true);
+ m_detailsView->setEnabled(true);
+ m_toolBarSecond->setEnabled(true);
+ }
+
+ // Update actions
+ updateActions();
+}
+
+}
+
+}
+
+#include "view.moc"
diff --git a/kftpgrabber/src/widgets/browser/view.h b/kftpgrabber/src/widgets/browser/view.h
new file mode 100644
index 0000000..67dffa6
--- /dev/null
+++ b/kftpgrabber/src/widgets/browser/view.h
@@ -0,0 +1,258 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPFILEDIRVIEW_H
+#define KFTPFILEDIRVIEW_H
+
+#include "browser/locationnavigator.h"
+
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qsplitter.h>
+
+#include <kurl.h>
+#include <kfileitem.h>
+
+namespace KFTPSession {
+ class Session;
+ class Manager;
+}
+
+class KToolBar;
+class KHistoryCombo;
+class KAction;
+class KLineEdit;
+
+namespace KFTPEngine {
+ class Thread;
+ class Event;
+}
+
+namespace KFTPWidgets {
+
+namespace Browser {
+
+class DetailsView;
+class ListView;
+class TreeView;
+class Actions;
+class FilterWidget;
+
+/**
+ * @author Jernej Kos
+ */
+class View : public QWidget
+{
+Q_OBJECT
+friend class Actions;
+friend class DetailsView;
+friend class ListView;
+friend class TreeView;
+friend class KFTPSession::Manager;
+friend class KFTPSession::Session;
+public:
+ /**
+ * Class constructor.
+ */
+ View(QWidget *parent, const char *name, KFTPEngine::Thread *client, KFTPSession::Session *session);
+
+ /**
+ * Class destructor.
+ */
+ ~View();
+
+ /**
+ * Returns the current URL.
+ */
+ const KURL &url() const;
+
+ /**
+ * Returns the current history elements.
+ *
+ * @param index Variable to save the current history position to
+ * @return Current history element list
+ */
+ const QValueList<LocationNavigator::Element> history(int &index) const;
+
+ /**
+ * Returns all currenty selected items.
+ */
+ const KFileItemList *selectedItems() const;
+
+ /**
+ * Returns all currently selected URLs.
+ */
+ KURL::List selectedURLs() const;
+
+ /**
+ * Returns all items in the current sort order.
+ */
+ const KFileItemList *items() const;
+
+ /**
+ * Open an URL. Note that if a remote URL is specified the session needs to
+ * be connected to the specified host!
+ *
+ * @param url URL to open
+ */
+ void openUrl(const KURL &url);
+
+ /**
+ * Changes the visibility of tree widget.
+ *
+ * @param visible True to display the tree widget, false to hide it
+ */
+ void setTreeVisible(bool visible);
+
+ /**
+ * Changes the "show hidden files" setting.
+ *
+ * @param value True to enable showing hidden files, false otherwise
+ */
+ void setShowHidden(bool value);
+
+ /**
+ * Set the home URL.
+ *
+ * @param url URL to use as home URL
+ */
+ void setHomeUrl(const KURL &url);
+
+ /**
+ * Go one history hop back.
+ */
+ void goBack();
+
+ /**
+ * Go one history hop forward.
+ */
+ void goForward();
+
+ /**
+ * Go up in the directory structure.
+ */
+ void goUp();
+
+ /**
+ * Go the the predefined home URL.
+ */
+ void goHome();
+
+ /**
+ * Reload the current directory listing.
+ */
+ void reload();
+
+ /**
+ * Renames the provided source file to a new name.
+ */
+ void rename(const KURL &source, const QString &name);
+
+ /**
+ * Returns the details view widget.
+ */
+ DetailsView *getDetailsView() const { return m_detailsView; }
+
+ /**
+ * Returns the tree view widget.
+ */
+ TreeView *getTreeView() const { return m_tree; }
+
+ /**
+ * Returns the status label widget.
+ */
+ QLabel *getStatusLabel() const { return m_statusMsg; }
+
+ /**
+ * Returns the associated session.
+ */
+ KFTPSession::Session *getSession() const { return m_session; }
+protected:
+ /**
+ * Initialize the widget.
+ */
+ void init();
+
+ /**
+ * Populate the toolbar.
+ */
+ void populateToolbar();
+private:
+ KFTPSession::Session *m_session;
+ KFTPEngine::Thread *m_ftpClient;
+
+ DetailsView *m_detailsView;
+ TreeView *m_tree;
+
+ Actions *m_actions;
+
+ KToolBar *m_toolBarFirst; /* Upper toolbar */
+ KToolBar *m_toolBarSecond; /* Lower toolbar */
+ KToolBar *m_searchToolBar;
+
+ QLabel *m_statusMsg;
+ QLabel *m_connDurationMsg;
+ QPushButton *m_sslIcon;
+ QSplitter *m_splitter;
+
+ QTimer *m_connTimer;
+ QTime m_connDuration;
+
+ KHistoryCombo *m_historyCombo;
+ FilterWidget *m_searchFilter;
+public slots:
+ void updateActions();
+ void updateBookmarks();
+private slots:
+ void slotHistoryEraseClicked();
+ void slotHistoryActivated(const QString &text);
+ void slotHistoryChanged();
+
+ void slotDisplayCertInfo();
+ void slotDurationUpdate();
+
+ void slotEngineEvent(KFTPEngine::Event *event);
+
+ void slotConfigUpdate();
+};
+
+}
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/configdialog.cpp b/kftpgrabber/src/widgets/configdialog.cpp
new file mode 100644
index 0000000..49d0d16
--- /dev/null
+++ b/kftpgrabber/src/widgets/configdialog.cpp
@@ -0,0 +1,187 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "configdialog.h"
+#include "misc.h"
+#include "misc/config.h"
+#include "widgets/systemtray.h"
+
+#include <klocale.h>
+#include <kfontdialog.h>
+#include <kcolorbutton.h>
+#include <kurlrequester.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kcombobox.h>
+
+#include <qframe.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+
+// Config layouts
+#include "ui/config_general.h"
+#include "ui/config_transfers.h"
+#include "ui/config_log.h"
+#include "ui/config_display.h"
+
+#include "configfilter.h"
+
+namespace KFTPWidgets {
+
+ConfigDialog::ConfigDialog(QWidget *parent, const char *name)
+ : KConfigDialog(parent, name, KFTPCore::Config::self(), KDialogBase::TreeList, Ok|Apply|Cancel, Ok, true)
+{
+ // Add all standard pages
+ addPage(new GeneralSettings(0, "General"), i18n("General"), "kfm");
+ addPage(new TransferSettings(0, "Transfers"), i18n("Transfers"), "queue");
+ addPage(new LogSettings(0, "Log"), i18n("Log"), "log");
+ addPage(new DisplaySettings(0, "Display"), i18n("Display"), "display");
+
+ // Add the actions page
+ QFrame *aFrame = new QFrame();
+ QVBoxLayout *aLayout = new QVBoxLayout(aFrame, 0, 0);
+ aLayout->addWidget(KFTPCore::Config::self()->dActions()->getConfigWidget(aFrame));
+ aLayout->addSpacing(KDialog::spacingHint());
+ aLayout->addWidget(KFTPCore::Config::self()->uActions()->getConfigWidget(aFrame));
+ aLayout->addSpacing(KDialog::spacingHint());
+ aLayout->addWidget(KFTPCore::Config::self()->fActions()->getConfigWidget(aFrame));
+ aLayout->addStretch(1);
+ addPage(aFrame, i18n("Actions"), "kfm");
+
+ // Add the filter page
+ aFrame = new QFrame();
+ aLayout = new QVBoxLayout(aFrame, 0, 0);
+ m_configFilter = new ConfigFilter(aFrame);
+ aLayout->addWidget(m_configFilter);
+ addPage(aFrame, i18n("Filters"), "kfm");
+
+ // Setup some stuff
+ static_cast<KURLRequester*>(child("kcfg_defLocalDir"))->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+
+ // Setup signals
+ connect(child("kcfg_globalMail"), SIGNAL(toggled(bool)), this, SLOT(slotGeneralEmailChanged(bool)));
+ connect(child("kcfg_portForceIp"), SIGNAL(toggled(bool)), this, SLOT(slotTransfersForceIpChanged(bool)));
+ connect(child("kcfg_activeForcePort"), SIGNAL(toggled(bool)), this, SLOT(slotTransfersForcePortChanged(bool)));
+ connect(child("kcfg_diskCheckSpace"), SIGNAL(toggled(bool)), this, SLOT(slotTransfersDiskChanged(bool)));
+ connect(child("kcfg_failedAutoRetry"), SIGNAL(toggled(bool)), this, SLOT(slotTransfersFailedRetryChanged(bool)));
+ connect(child("kcfg_saveToFile"), SIGNAL(toggled(bool)), this, SLOT(slotLogOutputFileChanged(bool)));
+
+ // Refresh states
+ slotGeneralEmailChanged(static_cast<QCheckBox*>(child("kcfg_globalMail"))->isChecked());
+ slotTransfersForceIpChanged(static_cast<QCheckBox*>(child("kcfg_portForceIp"))->isChecked());
+ slotTransfersDiskChanged(static_cast<QCheckBox*>(child("kcfg_diskCheckSpace"))->isChecked());
+ slotTransfersFailedRetryChanged(static_cast<QCheckBox*>(child("kcfg_failedAutoRetry"))->isChecked());
+ slotLogOutputFileChanged(static_cast<QCheckBox*>(child("kcfg_saveToFile"))->isChecked());
+ slotTransfersForcePortChanged(static_cast<QCheckBox*>(child("kcfg_activeForcePort"))->isChecked());
+
+ // Let the config be up-to-date
+ connect(this, SIGNAL(settingsChanged()), KFTPCore::Config::self(), SLOT(emitChange()));
+ connect(this, SIGNAL(okClicked()), this, SLOT(slotSettingsChanged()));
+}
+
+void ConfigDialog::prepareDialog()
+{
+ // Update the actions
+ KFTPCore::Config::self()->dActions()->updateWidget();
+ KFTPCore::Config::self()->uActions()->updateWidget();
+ KFTPCore::Config::self()->fActions()->updateWidget();
+
+ // Populate charsets
+ QStringList charsets = KGlobal::charsets()->descriptiveEncodingNames();
+ static_cast<KComboBox*>(child("cfg_defEncoding"))->insertStringList(charsets);
+
+ QString defEncoding = KFTPCore::Config::defEncoding();
+ defEncoding = QString("%1 ( %2 )").arg(KGlobal::charsets()->languageForEncoding(defEncoding)).arg(defEncoding);
+ static_cast<KComboBox*>(child("cfg_defEncoding"))->setCurrentText(defEncoding);
+
+ m_configFilter->loadSettings();
+}
+
+void ConfigDialog::slotSettingsChanged()
+{
+ // Update the actions
+ KFTPCore::Config::self()->dActions()->updateConfig();
+ KFTPCore::Config::self()->uActions()->updateConfig();
+ KFTPCore::Config::self()->fActions()->updateConfig();
+
+ m_configFilter->saveSettings();
+
+ // Save encoding
+ KFTPCore::Config::setDefEncoding(KGlobal::charsets()->encodingForName(static_cast<KComboBox*>(child("cfg_defEncoding"))->currentText()));
+
+ // Show/hide the systray icon
+ if (KFTPCore::Config::showSystrayIcon())
+ SystemTray::self()->show();
+ else
+ SystemTray::self()->hide();
+}
+
+void ConfigDialog::slotGeneralEmailChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_anonMail"))->setEnabled(!on);
+}
+
+void ConfigDialog::slotTransfersForceIpChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_portIp"))->setEnabled(on);
+}
+
+void ConfigDialog::slotTransfersForcePortChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_activeMinPort"))->setEnabled(on);
+ static_cast<QWidget*>(child("kcfg_activeMaxPort"))->setEnabled(on);
+}
+
+void ConfigDialog::slotTransfersDiskChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_diskCheckInterval"))->setEnabled(on);
+ static_cast<QWidget*>(child("kcfg_diskMinFreeSpace"))->setEnabled(on);
+}
+
+void ConfigDialog::slotTransfersFailedRetryChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_failedAutoRetryCount"))->setEnabled(on);
+}
+
+void ConfigDialog::slotLogOutputFileChanged(bool on)
+{
+ static_cast<QWidget*>(child("kcfg_outputFilename"))->setEnabled(on);
+}
+
+}
+
+#include "configdialog.moc"
diff --git a/kftpgrabber/src/widgets/configdialog.h b/kftpgrabber/src/widgets/configdialog.h
new file mode 100644
index 0000000..6234c14
--- /dev/null
+++ b/kftpgrabber/src/widgets/configdialog.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPCONFIGDIALOG_H
+#define KFTPCONFIGDIALOG_H
+
+#include <kconfigdialog.h>
+
+namespace KFTPWidgets {
+
+class ConfigFilter;
+
+/**
+ * @author Jernej Kos
+ */
+class ConfigDialog : public KConfigDialog
+{
+Q_OBJECT
+public:
+ ConfigDialog(QWidget *parent, const char *name = 0);
+
+ void prepareDialog();
+private:
+ ConfigFilter *m_configFilter;
+private slots:
+ void slotGeneralEmailChanged(bool on);
+
+ void slotTransfersForceIpChanged(bool on);
+ void slotTransfersDiskChanged(bool on);
+ void slotTransfersForcePortChanged(bool on);
+ void slotTransfersFailedRetryChanged(bool on);
+
+ void slotLogOutputFileChanged(bool on);
+
+ void slotSettingsChanged();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/configfilter.cpp b/kftpgrabber/src/widgets/configfilter.cpp
new file mode 100644
index 0000000..c2dc3b9
--- /dev/null
+++ b/kftpgrabber/src/widgets/configfilter.cpp
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "configfilter.h"
+#include "filtereditor.h"
+
+#include "misc/config.h"
+#include "misc.h"
+
+#include <klistview.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+#include <klineeditdlg.h>
+
+#include <klineedit.h>
+#include <kcolorbutton.h>
+#include <kdialogbase.h>
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+ConfigFilter::ConfigFilter(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ // Create the main widget
+ QVBoxLayout *t = new QVBoxLayout(this, 0, 0);
+
+ m_editorLayout = new ConfigFilterLayout(this);
+ t->addWidget(m_editorLayout);
+
+ m_filterEditor = new FilterEditor(this);
+ m_editorLayout->tabWidget->insertTab(m_filterEditor, i18n("Filters"), 0);
+ m_editorLayout->tabWidget->setCurrentPage(0);
+ m_editorLayout->tabWidget->setMargin(KDialog::spacingHint());
+
+ loadSettings();
+
+ // Connect the slots
+ connect(m_editorLayout->addExtButton, SIGNAL(clicked()), this, SLOT(slotAddAscii()));
+ connect(m_editorLayout->removeExtButton, SIGNAL(clicked()), this, SLOT(slotRemoveAscii()));
+}
+
+void ConfigFilter::loadSettings()
+{
+ m_filterEditor->reset();
+ asciiLoadExtensions();
+}
+
+void ConfigFilter::saveSettings()
+{
+ // Save the settings
+ KFTPCore::Config::setAsciiList(asciiToStringList());
+}
+
+void ConfigFilter::slotAddAscii()
+{
+ if (!m_editorLayout->newExtension->text().stripWhiteSpace().isEmpty()) {
+ new QListViewItem(m_editorLayout->extensionList, m_editorLayout->newExtension->text().stripWhiteSpace());
+ m_editorLayout->newExtension->clear();
+ }
+}
+
+void ConfigFilter::slotRemoveAscii()
+{
+ if (!m_editorLayout->extensionList->selectedItem())
+ return;
+
+ delete m_editorLayout->extensionList->selectedItem();
+}
+
+void ConfigFilter::asciiLoadExtensions()
+{
+ // Load the ascii extensions
+ m_editorLayout->extensionList->clear();
+ QStringList p_extensions = KFTPCore::Config::asciiList();
+
+ QStringList::iterator end( p_extensions.end() );
+ for (QStringList::iterator i( p_extensions.begin() ); i != end; ++i) {
+ new QListViewItem(m_editorLayout->extensionList, (*i));
+ }
+
+}
+
+QStringList ConfigFilter::asciiToStringList()
+{
+ QStringList p_extensions;
+ QListViewItem *i = m_editorLayout->extensionList->firstChild();
+ while (i) {
+ p_extensions.append(i->text(0));
+
+ i = i->itemBelow();
+ }
+
+ return p_extensions;
+}
+
+}
+#include "configfilter.moc"
diff --git a/kftpgrabber/src/widgets/configfilter.h b/kftpgrabber/src/widgets/configfilter.h
new file mode 100644
index 0000000..3d9ee22
--- /dev/null
+++ b/kftpgrabber/src/widgets/configfilter.h
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSCONFIGFILTER_H
+#define KFTPWIDGETSCONFIGFILTER_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include <knuminput.h>
+#include <qradiobutton.h>
+#include <qvaluelist.h>
+
+// Layouts
+#include "ui/config_filters.h"
+
+namespace KFTPWidgets {
+
+class FilterEditor;
+
+/**
+ * Filter related configuration dialog.
+ *
+ * @author Jernej Kos
+ */
+class ConfigFilter : public QWidget
+{
+Q_OBJECT
+public:
+ ConfigFilter(QWidget *parent = 0, const char *name = 0);
+
+ void saveSettings();
+ void loadSettings();
+private:
+ void asciiLoadExtensions();
+ QStringList asciiToStringList();
+
+ FilterEditor *m_filterEditor;
+ ConfigFilterLayout *m_editorLayout;
+private slots:
+ void slotAddAscii();
+ void slotRemoveAscii();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/failedtransfers.cpp b/kftpgrabber/src/widgets/failedtransfers.cpp
new file mode 100644
index 0000000..30fc0a6
--- /dev/null
+++ b/kftpgrabber/src/widgets/failedtransfers.cpp
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "failedtransfers.h"
+#include "kftpqueue.h"
+#include "listview.h"
+#include "misc.h"
+#include "kftpapi.h"
+
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kio/job.h>
+#include <kmessagebox.h>
+
+#include <qlayout.h>
+#include <qheader.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+FailedTransferItem::FailedTransferItem(KFTPQueue::FailedTransfer *transfer, QListView *parent)
+ : KFTPWidgets::ListViewItem(parent),
+ m_failedTransfer(transfer)
+{
+ QString desc = "<nobr><b>";
+ desc += i18n("Transfer");
+ desc += ":</b> ";
+ desc += transfer->getTransfer()->getSourceUrl().prettyURL();
+ desc += " <b>&gt;&gt;&gt;</b> ";
+ desc += transfer->getTransfer()->getDestUrl().prettyURL();
+ desc += "<br><b>";
+ desc += i18n("Error");
+ desc += ":</b> <i>";
+ desc += transfer->getError();
+ desc += "</i></nobr>";
+
+ // Setup columns
+ setRichText(0, desc);
+ setText(1, KIO::convertSize(transfer->getTransfer()->getActualSize()));
+ setText(2, QString::number(transfer->getTransfer()->getId()));
+
+ setPixmap(0, loadSmallPixmap("info"));
+}
+
+KActionCollection *FailedTransfers::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+FailedTransfers::FailedTransfers(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ // Create the list view
+ m_list = new KFTPWidgets::ListView(this);
+
+ // Create the columns
+ m_list->addColumn(i18n("Description"), 500);
+ m_list->addColumn(i18n("Size"), 75);
+ m_list->header()->setStretchEnabled(true, 0);
+
+ // Text when there are no failed transfers
+ m_list->setEmptyListText(i18n("There are no failed transfers."));
+ m_list->setAllColumnsShowFocus(true);
+
+ layout->addWidget(m_list);
+
+ connect(KFTPQueue::Manager::self(), SIGNAL(failedTransferNew(KFTPQueue::FailedTransfer*)), this, SLOT(slotFailedTransferNew(KFTPQueue::FailedTransfer*)));
+ connect(KFTPQueue::Manager::self(), SIGNAL(failedTransferRemoved(long)), this, SLOT(slotFailedTransferRemoved(long)));
+
+ connect(m_list, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), this, SLOT(contextMenuRequested(KListView*, QListViewItem*, const QPoint&)));
+
+ // Initialize the actions
+ initActions();
+}
+
+FailedTransfers::~FailedTransfers()
+{
+}
+
+void FailedTransfers::initActions()
+{
+ m_restartAction = new KAction(i18n("&Restart Transfer"), "launch", KShortcut(), this, SLOT(slotRestart()), actionCollection(), "launch");
+ m_addToQueueAction = new KAction(i18n("&Add To Queue"), "queue", KShortcut(), this, SLOT(slotAddToQueue()), actionCollection(), "addtoqueue");
+ m_addAllToQueueAction = new KAction(i18n("Add All To Queue"), KShortcut(), this, SLOT(slotAddAllToQueue()), actionCollection(), "addalltoqueue");
+ m_removeAction = new KAction(i18n("R&emove"), "editdelete", KShortcut(), this, SLOT(slotRemove()), actionCollection(), "remove");
+ m_removeAllAction = new KAction(i18n("Remove All"), KShortcut(), this, SLOT(slotRemoveAll()), actionCollection(), "removeall");
+}
+
+void FailedTransfers::updateActions()
+{
+ m_restartAction->setEnabled(m_list->selectedItem());
+ m_addToQueueAction->setEnabled(m_list->selectedItem());
+ m_removeAction->setEnabled(m_list->selectedItem());
+
+ m_addAllToQueueAction->setEnabled(m_list->childCount());
+ m_removeAllAction->setEnabled(m_list->childCount());
+}
+
+void FailedTransfers::slotFailedTransferNew(KFTPQueue::FailedTransfer *transfer)
+{
+ // Create a new failed transfer list item
+ new FailedTransferItem(transfer, m_list);
+}
+
+void FailedTransfers::slotFailedTransferRemoved(long id)
+{
+ // Delete the failed transfer item
+ QListViewItem *item = m_list->findItem(QString::number(id), 2);
+
+ if (item)
+ delete item;
+}
+
+void FailedTransfers::slotRestart()
+{
+ // First restore the transfer
+ KFTPQueue::TransferFile *transfer = static_cast<FailedTransferItem*>(m_list->selectedItem())->getFailedTransfer()->restore();
+
+ // Then execute it
+ transfer->delayedExecute();
+}
+
+void FailedTransfers::slotAddToQueue()
+{
+ // Restore the transfer
+ static_cast<FailedTransferItem*>(m_list->selectedItem())->getFailedTransfer()->restore();
+}
+
+void FailedTransfers::slotAddAllToQueue()
+{
+ KFTPQueue::FailedTransfer *transfer;
+ QPtrList<KFTPQueue::FailedTransfer> *list = KFTPQueue::Manager::self()->getFailedTransferList();
+ QPtrListIterator<KFTPQueue::FailedTransfer> i(*list);
+
+ while ((transfer = i.current()) != 0) {
+ ++i;
+
+ // Restore the transfer
+ transfer->restore();
+ }
+}
+
+void FailedTransfers::slotRemove()
+{
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove this failed transfer?")) == KMessageBox::Yes) {
+ // Remove the failed transfer
+ KFTPQueue::Manager::self()->removeFailedTransfer(static_cast<FailedTransferItem*>(m_list->selectedItem())->getFailedTransfer());
+ }
+}
+
+void FailedTransfers::slotRemoveAll()
+{
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove ALL failed transfers?")) == KMessageBox::Yes) {
+ KFTPQueue::Manager::self()->clearFailedTransferList();
+ }
+}
+
+void FailedTransfers::contextMenuRequested(KListView*, QListViewItem*, const QPoint &p)
+{
+ KPopupMenu *contextMenu = new KPopupMenu(m_list);
+
+ m_restartAction->plug(contextMenu);
+ m_addToQueueAction->plug(contextMenu);
+ m_addAllToQueueAction->plug(contextMenu);
+ contextMenu->insertSeparator();
+ m_removeAction->plug(contextMenu);
+ m_removeAllAction->plug(contextMenu);
+
+ // Update the actions
+ updateActions();
+
+ contextMenu->exec(p);
+}
+
+}
+
+#include "failedtransfers.moc"
+
diff --git a/kftpgrabber/src/widgets/failedtransfers.h b/kftpgrabber/src/widgets/failedtransfers.h
new file mode 100644
index 0000000..2b17a5a
--- /dev/null
+++ b/kftpgrabber/src/widgets/failedtransfers.h
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSFAILEDTRANSFERS_H
+#define KFTPWIDGETSFAILEDTRANSFERS_H
+
+#include <klistview.h>
+#include <kaction.h>
+
+#include <qwidget.h>
+#include <qguardedptr.h>
+
+#include "listviewitem.h"
+
+namespace KFTPQueue {
+ class FailedTransfer;
+}
+
+namespace KFTPWidgets {
+
+class ListView;
+
+/**
+ * This class represents a QListViewItem item that contains information
+ * about a specific failed transfer.
+ *
+ * @author Jernej Kos
+ */
+class FailedTransferItem : public KFTPWidgets::ListViewItem
+{
+public:
+ FailedTransferItem(KFTPQueue::FailedTransfer *transfer, QListView *parent);
+
+ KFTPQueue::FailedTransfer *getFailedTransfer() { return m_failedTransfer; }
+private:
+ QGuardedPtr<KFTPQueue::FailedTransfer> m_failedTransfer;
+};
+
+/**
+ * This class provides a list of failed transfers. The transfers
+ * can be restarted.
+ *
+ * @author Jernej Kos
+ */
+class FailedTransfers : public QWidget
+{
+Q_OBJECT
+public:
+ FailedTransfers(QWidget *parent = 0, const char *name = 0);
+ ~FailedTransfers();
+
+ void updateActions();
+ void initActions();
+private:
+ KActionCollection *actionCollection();
+
+ KFTPWidgets::ListView *m_list;
+
+ KAction *m_restartAction;
+ KAction *m_addToQueueAction;
+ KAction *m_addAllToQueueAction;
+ KAction *m_removeAction;
+ KAction *m_removeAllAction;
+private slots:
+ void slotRestart();
+ void slotAddToQueue();
+ void slotAddAllToQueue();
+ void slotRemove();
+ void slotRemoveAll();
+
+ void contextMenuRequested(KListView*, QListViewItem*, const QPoint&);
+
+ void slotFailedTransferNew(KFTPQueue::FailedTransfer*);
+ void slotFailedTransferRemoved(long);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/filtereditor.cpp b/kftpgrabber/src/widgets/filtereditor.cpp
new file mode 100644
index 0000000..9be3b6e
--- /dev/null
+++ b/kftpgrabber/src/widgets/filtereditor.cpp
@@ -0,0 +1,625 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "filtereditor.h"
+#include "listview.h"
+#include "misc/filterwidgethandler.h"
+
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qtooltip.h>
+#include <qheader.h>
+#include <qbuttongroup.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kdialog.h>
+#include <kinputdialog.h>
+
+using namespace KFTPCore::Filter;
+
+namespace KFTPWidgets {
+
+FilterEditor::FilterEditor(QWidget *parent)
+ : QWidget(parent),
+ m_rule(0)
+{
+ QHBoxLayout *mainLayout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ m_listView = new FilterListView(this);
+ mainLayout->addWidget(m_listView, 1);
+
+ QVBoxLayout *rightLayout = new QVBoxLayout(mainLayout);
+ mainLayout->setStretchFactor(rightLayout, KDialog::spacingHint());
+
+ m_enabledCheck = new QCheckBox(i18n("Filter &enabled"), this);
+ rightLayout->addWidget(m_enabledCheck);
+
+ m_conditionsList = new FilterConditionsList(this);
+ rightLayout->addWidget(m_conditionsList, 0, Qt::AlignTop);
+
+ m_actionsList = new FilterActionsList(this);
+ rightLayout->addWidget(m_actionsList, 0, Qt::AlignTop);
+
+ rightLayout->addStretch(1);
+
+ // Connect some signals
+ connect(m_enabledCheck, SIGNAL(clicked()), this, SLOT(slotEnabledChanged()));
+
+ connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), this, SLOT(slotRuleChanged(KFTPCore::Filter::Rule*)));
+ connect(m_listView, SIGNAL(ruleRemoved()), this, SLOT(slotRuleRemoved()));
+
+ connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), m_conditionsList, SLOT(loadRule(KFTPCore::Filter::Rule*)));
+ connect(m_listView, SIGNAL(ruleRemoved()), m_conditionsList, SLOT(reset()));
+
+ connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), m_actionsList, SLOT(loadRule(KFTPCore::Filter::Rule*)));
+ connect(m_listView, SIGNAL(ruleRemoved()), m_actionsList, SLOT(reset()));
+}
+
+void FilterEditor::slotRuleChanged(KFTPCore::Filter::Rule *rule)
+{
+ m_enabledCheck->setEnabled(true);
+ m_enabledCheck->setChecked(rule->isEnabled());
+
+ m_rule = rule;
+}
+
+void FilterEditor::slotRuleRemoved()
+{
+ m_enabledCheck->setChecked(false);
+ m_enabledCheck->setEnabled(false);
+}
+
+void FilterEditor::slotEnabledChanged()
+{
+ if (m_rule)
+ m_rule->setEnabled(m_enabledCheck->isChecked());
+}
+
+void FilterEditor::reset()
+{
+ m_enabledCheck->setChecked(false);
+ m_enabledCheck->setEnabled(false);
+
+ m_conditionsList->reset();
+ m_actionsList->reset();
+ m_listView->reset();
+}
+
+FilterListItem::FilterListItem(ListView *parent, KFTPCore::Filter::Rule *rule)
+ : QListViewItem(parent),
+ m_rule(rule)
+{
+ setText(0, rule->name());
+}
+
+FilterListView::FilterListView(QWidget *parent)
+ : QGroupBox(1, Horizontal, i18n("Filters"), parent)
+{
+ m_listView = new ListView(this);
+ m_listView->setSelectionMode(QListView::Single);
+ m_listView->setSorting(-1);
+ m_listView->header()->hide();
+ m_listView->setMinimumWidth(150);
+ m_listView->setEmptyListText(i18n("No filters."));
+ m_listView->addColumn("");
+ m_listView->setFullWidth(true);
+
+ QHBox *hb = new QHBox(this);
+ hb->setSpacing(4);
+
+ // Up/down buttons
+ m_buttonUp = new KPushButton(QString::null, hb);
+ m_buttonUp->setAutoRepeat(true);
+ m_buttonUp->setIconSet(BarIconSet("up", KIcon::SizeSmall));
+ m_buttonUp->setMinimumSize(m_buttonUp->sizeHint() * 1.2);
+
+ m_buttonDown = new KPushButton(QString::null, hb);
+ m_buttonDown->setAutoRepeat(true);
+ m_buttonDown->setIconSet(BarIconSet("down", KIcon::SizeSmall));
+ m_buttonDown->setMinimumSize(m_buttonDown->sizeHint() * 1.2);
+
+ QToolTip::add(m_buttonUp, i18n("Up"));
+ QToolTip::add(m_buttonDown, i18n("Down"));
+
+ // New, copy, delete buttons
+ hb = new QHBox(this);
+ hb->setSpacing(4);
+
+ m_buttonNew = new QPushButton(QString::null, hb);
+ m_buttonNew->setPixmap(BarIcon("filenew", KIcon::SizeSmall));
+ m_buttonNew->setMinimumSize(m_buttonNew->sizeHint() * 1.2);
+
+ m_buttonCopy = new QPushButton(QString::null, hb);
+ m_buttonCopy->setPixmap(BarIcon("editcopy", KIcon::SizeSmall));
+ m_buttonCopy->setMinimumSize(m_buttonCopy->sizeHint() * 1.2);
+
+ m_buttonDelete = new QPushButton(QString::null, hb);
+ m_buttonDelete->setPixmap(BarIcon("editdelete", KIcon::SizeSmall));
+ m_buttonDelete->setMinimumSize(m_buttonDelete->sizeHint() * 1.2);
+
+ m_buttonRename = new QPushButton(i18n("Rename..."), hb);
+
+ QToolTip::add(m_buttonNew, i18n("New"));
+ QToolTip::add(m_buttonCopy, i18n("Copy"));
+ QToolTip::add(m_buttonDelete, i18n("Delete"));
+
+ // Connect the signals
+ connect(m_buttonNew, SIGNAL(clicked()), this, SLOT(slotNewRule()));
+ connect(m_buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteRule()));
+ connect(m_buttonRename, SIGNAL(clicked()), this, SLOT(slotRenameRule()));
+ connect(m_buttonCopy, SIGNAL(clicked()), this, SLOT(slotCopyRule()));
+
+ connect(m_buttonUp, SIGNAL(clicked()), this, SLOT(slotUp()));
+ connect(m_buttonDown, SIGNAL(clicked()), this, SLOT(slotDown()));
+
+ connect(m_listView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotSelectionChanged(QListViewItem*)));
+
+ m_buttonUp->setEnabled(false);
+ m_buttonDown->setEnabled(false);
+ m_buttonRename->setEnabled(false);
+
+ // Reset the view, loading all the existing rules
+ reset();
+}
+
+void FilterListView::reset()
+{
+ m_listView->clear();
+
+ // Load all existing rules
+ Filters *filters = Filters::self();
+ Filters::ConstIterator le = filters->end();
+
+ for (Filters::ConstIterator i = filters->begin(); i != le; ++i) {
+ FilterListItem *item = new FilterListItem(m_listView, (*i));
+ item->moveItem(m_listView->lastItem());
+ }
+
+ // Select the first rule
+ m_listView->setSelected(m_listView->firstChild(), true);
+}
+
+void FilterListView::slotSelectionChanged(QListViewItem *item)
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(item);
+
+ m_buttonUp->setEnabled(item->itemAbove());
+ m_buttonDown->setEnabled(item->nextSibling());
+ m_buttonRename->setEnabled(true);
+
+ // Signal the rule change
+ emit ruleChanged(selected->rule());
+}
+
+void FilterListView::slotNewRule()
+{
+ Rule *rule = new Rule(i18n("Unnamed Rule"));
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+ FilterListItem *item = new FilterListItem(m_listView, rule);
+
+ if (selected) {
+ Filters::self()->insert(Filters::self()->findRef(selected->rule()) + 1, rule);
+ item->moveItem(selected);
+ } else {
+ Filters::self()->append(rule);
+ }
+
+ m_listView->setSelected(item, true);
+}
+
+void FilterListView::slotDeleteRule()
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+
+ if (selected) {
+ Rule *rule = selected->rule();
+ delete selected;
+
+ emit ruleRemoved();
+
+ Filters::self()->removeRef(rule);
+ m_listView->setSelected(m_listView->lastItem(), true);
+ }
+
+ if (!m_listView->selectedItem())
+ m_buttonRename->setEnabled(false);
+}
+
+void FilterListView::slotRenameRule()
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+
+ if (selected) {
+ QString name = KInputDialog::getText(i18n("Rename Rule"), i18n("Rename rule '%1' to:").arg(selected->rule()->name()), selected->rule()->name());
+
+ if (name.stripWhiteSpace().isEmpty())
+ name = i18n("Unnamed Rule");
+
+ selected->rule()->setName(name);
+ selected->setText(0, name);
+ }
+}
+
+void FilterListView::slotCopyRule()
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+
+ if (selected) {
+ Rule *rule = new Rule(selected->rule());
+ FilterListItem *item = new FilterListItem(m_listView, rule);
+
+ Filters::self()->insert(Filters::self()->findRef(selected->rule()) + 1, rule);
+ item->moveItem(selected);
+ m_listView->setSelected(item, true);
+ }
+}
+
+void FilterListView::slotUp()
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+ QListViewItem *tmp = selected->itemAbove();
+ if (!tmp)
+ return;
+
+ FilterListItem *previous = static_cast<FilterListItem*>(tmp->itemAbove());
+
+ if (selected) {
+ Rule *rule = Filters::self()->take(Filters::self()->findRef(selected->rule()));
+
+ if (previous) {
+ Filters::self()->insert(Filters::self()->findRef(previous->rule()) + 1, rule);
+ selected->moveItem(previous);
+ } else {
+ Filters::self()->insert(0, rule);
+ m_listView->takeItem(selected);
+ m_listView->insertItem(selected);
+ m_listView->setSelected(selected, true);
+ }
+
+ m_buttonUp->setEnabled(selected->itemAbove());
+ m_buttonDown->setEnabled(selected->nextSibling());
+ }
+}
+
+void FilterListView::slotDown()
+{
+ FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
+ FilterListItem *next = static_cast<FilterListItem*>(selected->nextSibling());
+
+ if (selected && next) {
+ Rule *rule = Filters::self()->take(Filters::self()->findRef(selected->rule()));
+ Filters::self()->insert(Filters::self()->findRef(next->rule()) + 1, rule);
+ selected->moveItem(next);
+
+ m_buttonUp->setEnabled(selected->itemAbove());
+ m_buttonDown->setEnabled(selected->nextSibling());
+ }
+}
+
+FilterConditionsList::FilterConditionsList(QWidget *parent)
+ : QGroupBox(1, Horizontal, i18n("Conditions"), parent)
+{
+ setEnabled(false);
+
+ m_buttonAll = new QRadioButton(i18n("Match a&ll of the following"), this);
+ m_buttonAny = new QRadioButton(i18n("Match an&y of the following"), this);
+
+ m_buttonAll->setChecked(true);
+ m_buttonAny->setChecked(false);
+
+ QButtonGroup *bg = new QButtonGroup(this);
+ bg->hide();
+ bg->insert(m_buttonAll, (int) ConditionChain::All);
+ bg->insert(m_buttonAny, (int) ConditionChain::Any);
+
+ // Connect some signals
+ connect(bg, SIGNAL(clicked(int)), this, SLOT(slotMatchTypeChanged(int)));
+
+ m_lister = new FilterConditionWidgetLister(this);
+}
+
+void FilterConditionsList::reset()
+{
+ m_lister->clear();
+ setEnabled(false);
+}
+
+void FilterConditionsList::loadRule(Rule *rule)
+{
+ m_rule = rule;
+
+ switch (rule->conditions()->type()) {
+ case ConditionChain::All: m_buttonAll->setChecked(true); break;
+ case ConditionChain::Any: m_buttonAny->setChecked(true); break;
+ }
+
+ m_lister->loadConditions(rule);
+ setEnabled(true);
+}
+
+void FilterConditionsList::slotMatchTypeChanged(int type)
+{
+ if (m_rule)
+ const_cast<ConditionChain*>(m_rule->conditions())->setType((ConditionChain::Type) type);
+}
+
+FilterConditionWidgetLister::FilterConditionWidgetLister(QWidget *parent)
+ : WidgetLister(parent, 0, 7),
+ m_rule(0)
+{
+ setMinimumWidth(400);
+}
+
+void FilterConditionWidgetLister::loadConditions(KFTPCore::Filter::Rule *rule)
+{
+ const ConditionChain *conditions = rule->conditions();
+
+ // Clear the current list
+ setNumberShown(QMAX(conditions->count(), 0));
+
+ ConditionChain::ConstIterator le = conditions->end();
+ QPtrList<QWidget>::Iterator wi = m_widgetList.begin();
+ for (ConditionChain::ConstIterator i = conditions->begin(); i != le; ++i, ++wi)
+ static_cast<FilterConditionWidget*>((*wi))->setCondition((*i));
+
+ m_rule = rule;
+}
+
+void FilterConditionWidgetLister::slotMore()
+{
+ WidgetLister::slotMore();
+
+ // Actually add the condition and update the latest widget
+ Condition *condition = new Condition(Filename, Condition::Contains, QVariant(""));
+
+ const_cast<ConditionChain*>(m_rule->conditions())->append(condition);
+ static_cast<FilterConditionWidget*>(m_widgetList.last())->setCondition(condition);
+}
+
+void FilterConditionWidgetLister::slotFewer()
+{
+ // Actually remove the condition
+ Condition *condition = static_cast<FilterConditionWidget*>(m_widgetList.last())->condition();
+ const_cast<ConditionChain*>(m_rule->conditions())->remove(condition);
+
+ WidgetLister::slotFewer();
+}
+
+void FilterConditionWidgetLister::slotClear()
+{
+ if (m_rule)
+ const_cast<ConditionChain*>(m_rule->conditions())->clear();
+
+ WidgetLister::slotClear();
+}
+
+QWidget *FilterConditionWidgetLister::createWidget(QWidget *parent)
+{
+ return new FilterConditionWidget(parent);
+}
+
+FilterConditionWidget::FilterConditionWidget(QWidget *parent)
+ : QWidget(parent),
+ m_condition(0)
+{
+ QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ m_fieldCombo = new QComboBox(this);
+ m_fieldCombo->insertStringList(Filters::self()->getFieldNames());
+ layout->addWidget(m_fieldCombo);
+
+ m_typeStack = new QWidgetStack(this);
+ m_typeStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ layout->addWidget(m_typeStack);
+
+ m_valueStack = new QWidgetStack(this);
+ m_valueStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ layout->addWidget(m_valueStack);
+ layout->setStretchFactor(m_valueStack, 10);
+
+ // Initialize widgets
+ WidgetHandlerManager::self()->createConditionWidgets(m_typeStack, m_valueStack, this);
+
+ // Connect signals
+ connect(m_fieldCombo, SIGNAL(activated(int)), this, SLOT(slotFieldChanged(int)));
+
+ setFocusProxy(m_fieldCombo);
+}
+
+void FilterConditionWidget::setCondition(const Condition *condition)
+{
+ m_condition = const_cast<Condition*>(condition);
+
+ m_fieldCombo->setCurrentItem((int) condition->field());
+ WidgetHandlerManager::self()->setCondition(m_typeStack, m_valueStack, condition);
+}
+
+void FilterConditionWidget::slotFieldChanged(int field)
+{
+ WidgetHandlerManager::self()->update((Field) field, m_typeStack, m_valueStack);
+
+ if (m_condition) {
+ // Update the current condition
+ m_condition->setField((Field) field);
+ slotTypeChanged();
+ }
+}
+
+void FilterConditionWidget::slotTypeChanged()
+{
+ if (m_condition) {
+ // Update the current condition
+ m_condition->setType(WidgetHandlerManager::self()->getConditionType(m_condition->field(), m_typeStack));
+ slotValueChanged();
+ }
+}
+
+void FilterConditionWidget::slotValueChanged()
+{
+ if (m_condition) {
+ // Update the current condition
+ m_condition->setValue(WidgetHandlerManager::self()->getConditionValue(m_condition->field(), m_valueStack));
+ }
+}
+
+FilterActionsList::FilterActionsList(QWidget *parent)
+ : QGroupBox(1, Horizontal, i18n("Actions"), parent)
+{
+ setEnabled(false);
+
+ m_lister = new FilterActionWidgetLister(this);
+}
+
+void FilterActionsList::reset()
+{
+ m_lister->clear();
+ setEnabled(false);
+}
+
+void FilterActionsList::loadRule(Rule *rule)
+{
+ m_rule = rule;
+
+ m_lister->loadActions(rule);
+ setEnabled(true);
+}
+
+FilterActionWidgetLister::FilterActionWidgetLister(QWidget *parent)
+ : WidgetLister(parent, 0, 7),
+ m_rule(0)
+{
+ setMinimumWidth(400);
+}
+
+void FilterActionWidgetLister::loadActions(KFTPCore::Filter::Rule *rule)
+{
+ const ActionChain *actions = rule->actions();
+
+ // Clear the current list
+ setNumberShown(QMAX(actions->count(), 0));
+
+ ActionChain::ConstIterator le = actions->end();
+ QPtrList<QWidget>::Iterator wi = m_widgetList.begin();
+ for (ActionChain::ConstIterator i = actions->begin(); i != le; ++i, ++wi)
+ static_cast<FilterActionWidget*>((*wi))->setAction((*i));
+
+ m_rule = rule;
+}
+
+void FilterActionWidgetLister::slotMore()
+{
+ WidgetLister::slotMore();
+
+ // Actually add the action and update the latest widget
+ Action *action = new Action(Action::None, QVariant());
+
+ const_cast<ActionChain*>(m_rule->actions())->append(action);
+ static_cast<FilterActionWidget*>(m_widgetList.last())->setAction(action);
+}
+
+void FilterActionWidgetLister::slotFewer()
+{
+ // Actually remove the action
+ Action *action = static_cast<FilterActionWidget*>(m_widgetList.last())->action();
+ const_cast<ActionChain*>(m_rule->actions())->remove(action);
+
+ WidgetLister::slotFewer();
+}
+
+void FilterActionWidgetLister::slotClear()
+{
+ if (m_rule)
+ const_cast<ActionChain*>(m_rule->actions())->clear();
+
+ WidgetLister::slotClear();
+}
+
+QWidget *FilterActionWidgetLister::createWidget(QWidget *parent)
+{
+ return new FilterActionWidget(parent);
+}
+
+FilterActionWidget::FilterActionWidget(QWidget *parent)
+ : QWidget(parent),
+ m_action(0)
+{
+ QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ m_actionCombo = new QComboBox(this);
+ m_actionCombo->insertStringList(Filters::self()->getActionNames());
+ layout->addWidget(m_actionCombo);
+
+ m_valueStack = new QWidgetStack(this);
+ m_valueStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ layout->addWidget(m_valueStack);
+ layout->setStretchFactor(m_valueStack, 10);
+
+ // Initialize widgets
+ WidgetHandlerManager::self()->createActionWidgets(m_valueStack, this);
+
+ // Connect signals
+ connect(m_actionCombo, SIGNAL(activated(int)), this, SLOT(slotActionChanged(int)));
+ connect(m_actionCombo, SIGNAL(activated(int)), m_valueStack, SLOT(raiseWidget(int)));
+
+ setFocusProxy(m_actionCombo);
+}
+
+void FilterActionWidget::setAction(const Action *action)
+{
+ m_action = const_cast<Action*>(action);
+
+ m_actionCombo->setCurrentItem((int) action->type());
+ WidgetHandlerManager::self()->setAction(m_valueStack, action);
+}
+
+void FilterActionWidget::slotActionChanged(int field)
+{
+ if (m_action) {
+ m_action->setType((Action::Type) field);
+ slotValueChanged();
+ }
+}
+
+void FilterActionWidget::slotValueChanged()
+{
+ if (m_action)
+ m_action->setValue(WidgetHandlerManager::self()->getActionValue(m_valueStack));
+}
+
+}
+
+#include "filtereditor.moc"
+
diff --git a/kftpgrabber/src/widgets/filtereditor.h b/kftpgrabber/src/widgets/filtereditor.h
new file mode 100644
index 0000000..95fd664
--- /dev/null
+++ b/kftpgrabber/src/widgets/filtereditor.h
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETSFILTEREDITOR_H
+#define KFTPWIDGETSFILTEREDITOR_H
+
+#include "widgetlister.h"
+#include "misc/filter.h"
+
+#include <qwidget.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qlistview.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qwidgetstack.h>
+#include <qcheckbox.h>
+
+#include <kpushbutton.h>
+
+namespace KFTPWidgets {
+
+class ListView;
+
+/**
+ * A visual representation of a condition.
+ *
+ * @author Jernej Kos
+ */
+class FilterConditionWidget : public QWidget {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterConditionWidget(QWidget *parent);
+
+ /**
+ * Associate a condition with this widget.
+ *
+ * @param condition The internal condition representation
+ */
+ void setCondition(const KFTPCore::Filter::Condition *condition);
+
+ /**
+ * Return the condition associated with this widget.
+ *
+ * @return A valid condition or 0 if no condition has been associated
+ */
+ KFTPCore::Filter::Condition *condition() const { return m_condition; }
+private slots:
+ void slotFieldChanged(int field);
+ void slotTypeChanged();
+ void slotValueChanged();
+private:
+ QComboBox *m_fieldCombo;
+ QWidgetStack *m_typeStack;
+ QWidgetStack *m_valueStack;
+
+ KFTPCore::Filter::Condition *m_condition;
+};
+
+/**
+ * A container for condition representation widgets.
+ *
+ * @author Jernej Kos
+ */
+class FilterConditionWidgetLister : public WidgetLister {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterConditionWidgetLister(QWidget *parent);
+
+ /**
+ * Load the conditions from the specified rule.
+ *
+ * @param rule The rule instance
+ */
+ void loadConditions(KFTPCore::Filter::Rule *rule);
+protected:
+ QWidget *createWidget(QWidget *parent);
+protected slots:
+ void slotMore();
+ void slotFewer();
+ void slotClear();
+private:
+ KFTPCore::Filter::Rule *m_rule;
+};
+
+/**
+ * A list of conditions together with all/any configuration.
+ *
+ * @author Jernej Kos
+ */
+class FilterConditionsList : public QGroupBox {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterConditionsList(QWidget *parent);
+public slots:
+ /**
+ * Reset the condition list and disable it.
+ */
+ void reset();
+
+ /**
+ * Load the conditions from the specified rule.
+ *
+ * @param rule The rule instance
+ */
+ void loadRule(KFTPCore::Filter::Rule *rule);
+private slots:
+ void slotMatchTypeChanged(int type);
+private:
+ QRadioButton *m_buttonAll;
+ QRadioButton *m_buttonAny;
+
+ FilterConditionWidgetLister *m_lister;
+ KFTPCore::Filter::Rule *m_rule;
+};
+
+/**
+ * A visual representation of an action.
+ *
+ * @author Jernej Kos
+ */
+class FilterActionWidget : public QWidget {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterActionWidget(QWidget *parent);
+
+ /**
+ * Associate an action with this widget.
+ *
+ * @param action The internal action representation
+ */
+ void setAction(const KFTPCore::Filter::Action *action);
+
+ /**
+ * Return the action associated with this widget.
+ *
+ * @return A valid action or 0 if no action has been associated
+ */
+ KFTPCore::Filter::Action *action() const { return m_action; }
+private slots:
+ void slotActionChanged(int field);
+ void slotValueChanged();
+private:
+ QComboBox *m_actionCombo;
+ QWidgetStack *m_valueStack;
+
+ KFTPCore::Filter::Action *m_action;
+};
+
+/**
+ * A container for action representation widgets.
+ *
+ * @author Jernej Kos
+ */
+class FilterActionWidgetLister : public WidgetLister {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterActionWidgetLister(QWidget *parent);
+
+ /**
+ * Load the actions from the specified rule.
+ *
+ * @param rule The rule instance
+ */
+ void loadActions(KFTPCore::Filter::Rule *rule);
+protected:
+ QWidget *createWidget(QWidget *parent);
+protected slots:
+ void slotMore();
+ void slotFewer();
+ void slotClear();
+private:
+ KFTPCore::Filter::Rule *m_rule;
+};
+
+/**
+ * A list of actions.
+ *
+ * @author Jernej Kos
+ */
+class FilterActionsList : public QGroupBox {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterActionsList(QWidget *parent);
+public slots:
+ /**
+ * Reset the action list and disable it.
+ */
+ void reset();
+
+ /**
+ * Load the actions from the specified rule.
+ *
+ * @param rule The rule instance
+ */
+ void loadRule(KFTPCore::Filter::Rule *rule);
+private:
+ FilterActionWidgetLister *m_lister;
+ KFTPCore::Filter::Rule *m_rule;
+};
+
+/**
+ * A widget that displays the list of currently loaded filter rules.
+ *
+ * @author Jernej Kos
+ */
+class FilterListView : public QGroupBox {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ FilterListView(QWidget *parent);
+
+ /**
+ * Reset the filter editor and reload all the rules.
+ */
+ void reset();
+private slots:
+ void slotSelectionChanged(QListViewItem *item);
+
+ void slotNewRule();
+ void slotDeleteRule();
+ void slotRenameRule();
+ void slotCopyRule();
+
+ void slotUp();
+ void slotDown();
+private:
+ ListView *m_listView;
+
+ KPushButton *m_buttonUp;
+ KPushButton *m_buttonDown;
+
+ QPushButton *m_buttonNew;
+ QPushButton *m_buttonCopy;
+ QPushButton *m_buttonDelete;
+ QPushButton *m_buttonRename;
+signals:
+ /**
+ * This signal gets emitted when a new rule should be displayed by
+ * other widgets.
+ *
+ * @param rule The rule to display
+ */
+ void ruleChanged(KFTPCore::Filter::Rule *rule);
+
+ /**
+ * This signal gets emitted when a rule is removed.
+ */
+ void ruleRemoved();
+};
+
+/**
+ * An item visually representing a filter rule.
+ *
+ * @author Jernej Kos
+ */
+class FilterListItem : public QListViewItem {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent The parent list view
+ * @param rule Rule this item represents
+ */
+ FilterListItem(ListView *parent, KFTPCore::Filter::Rule *rule);
+
+ /**
+ * Get the rule this item represents.
+ */
+ KFTPCore::Filter::Rule *rule() { return m_rule; }
+private:
+ KFTPCore::Filter::Rule *m_rule;
+};
+
+/**
+ * This widget is a global filter editor and enables the user to add,
+ * remove or modify existing filters.
+ *
+ * @author Jernej Kos
+ */
+class FilterEditor : public QWidget {
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ */
+ FilterEditor(QWidget *parent);
+
+ /**
+ * Reset the filter editor and reload all the rules.
+ */
+ void reset();
+private slots:
+ void slotRuleChanged(KFTPCore::Filter::Rule *rule);
+ void slotRuleRemoved();
+
+ void slotEnabledChanged();
+private:
+ KFTPCore::Filter::Rule *m_rule;
+
+ QCheckBox *m_enabledCheck;
+ FilterListView *m_listView;
+ FilterConditionsList *m_conditionsList;
+ FilterActionsList *m_actionsList;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/kftpfilteraddpatternlayout.cpp b/kftpgrabber/src/widgets/kftpfilteraddpatternlayout.cpp
new file mode 100644
index 0000000..50c403b
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpfilteraddpatternlayout.cpp
@@ -0,0 +1,92 @@
+#include <klocale.h>
+/****************************************************************************
+** Form implementation generated from reading ui file '/home/kostko/development/kftpgrabber/src/widgets/kftpfilteraddpatternlayout.ui'
+**
+** Created: Mon Oct 20 18:26:31 2003
+** by: The User Interface Compiler ($Id: kftpfilteraddpatternlayout.cpp,v 1.1.1.1 2004/02/13 13:33:43 kostko Exp $)
+**
+** WARNING! All changes made in this file will be lost!
+****************************************************************************/
+
+#include "kftpfilteraddpatternlayout.h"
+
+#include <qvariant.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <klineedit.h>
+#include <kcolorbutton.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+/*
+ * Constructs a KFTPFilterAddPatternLayout as a child of 'parent', with the
+ * name 'name' and widget flags set to 'f'.
+ */
+KFTPFilterAddPatternLayout::KFTPFilterAddPatternLayout( QWidget* parent, const char* name, WFlags fl )
+ : QWidget( parent, name, fl )
+{
+ if ( !name )
+ setName( "KFTPFilterAddPatternLayout" );
+ KFTPFilterAddPatternLayoutLayout = new QGridLayout( this, 1, 1, 11, 6, "KFTPFilterAddPatternLayoutLayout");
+
+ groupBox1 = new QGroupBox( this, "groupBox1" );
+ groupBox1->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)2, 0, 0, groupBox1->sizePolicy().hasHeightForWidth() ) );
+ groupBox1->setColumnLayout(0, Qt::Vertical );
+ groupBox1->layout()->setSpacing( 6 );
+ groupBox1->layout()->setMargin( 11 );
+ groupBox1Layout = new QGridLayout( groupBox1->layout() );
+ groupBox1Layout->setAlignment( Qt::AlignTop );
+
+ layout9 = new QHBoxLayout( 0, 0, 6, "layout9");
+
+ layout8 = new QVBoxLayout( 0, 0, 6, "layout8");
+
+ textLabel1 = new QLabel( groupBox1, "textLabel1" );
+ layout8->addWidget( textLabel1 );
+
+ textLabel2 = new QLabel( groupBox1, "textLabel2" );
+ layout8->addWidget( textLabel2 );
+ layout9->addLayout( layout8 );
+
+ layout7 = new QVBoxLayout( 0, 0, 6, "layout7");
+
+ kLineEdit1 = new KLineEdit( groupBox1, "kLineEdit1" );
+ layout7->addWidget( kLineEdit1 );
+
+ kColorButton1 = new KColorButton( groupBox1, "kColorButton1" );
+ kColorButton1->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0, (QSizePolicy::SizeType)0, 0, 0, kColorButton1->sizePolicy().hasHeightForWidth() ) );
+ layout7->addWidget( kColorButton1 );
+ layout9->addLayout( layout7 );
+
+ groupBox1Layout->addLayout( layout9, 0, 0 );
+
+ KFTPFilterAddPatternLayoutLayout->addWidget( groupBox1, 0, 0 );
+ languageChange();
+ resize( QSize(380, 110).expandedTo(minimumSizeHint()) );
+ clearWState( WState_Polished );
+}
+
+/*
+ * Destroys the object and frees any allocated resources
+ */
+KFTPFilterAddPatternLayout::~KFTPFilterAddPatternLayout()
+{
+ // no need to delete child widgets, Qt does it all for us
+}
+
+/*
+ * Sets the strings of the subwidgets using the current
+ * language.
+ */
+void KFTPFilterAddPatternLayout::languageChange()
+{
+ setCaption( tr2i18n( "Form1" ) );
+ groupBox1->setTitle( tr2i18n( "New Pattern" ) );
+ textLabel1->setText( tr2i18n( "Filename pattern:" ) );
+ textLabel2->setText( tr2i18n( "Color:" ) );
+ kColorButton1->setText( QString::null );
+}
+
+#include "kftpfilteraddpatternlayout.moc"
diff --git a/kftpgrabber/src/widgets/kftpfiltereditorlayout.cpp b/kftpgrabber/src/widgets/kftpfiltereditorlayout.cpp
new file mode 100644
index 0000000..b52913c
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpfiltereditorlayout.cpp
@@ -0,0 +1,115 @@
+#include <klocale.h>
+/****************************************************************************
+** Form implementation generated from reading ui file '/home/kostko/development/kftpgrabber/src/widgets/kftpfiltereditorlayout.ui'
+**
+** Created: Mon Oct 20 16:14:00 2003
+** by: The User Interface Compiler ($Id: kftpfiltereditorlayout.cpp,v 1.1.1.1 2004/02/13 13:33:43 kostko Exp $)
+**
+** WARNING! All changes made in this file will be lost!
+****************************************************************************/
+
+#include "kftpfiltereditorlayout.h"
+
+#include <qvariant.h>
+#include <qpushbutton.h>
+#include <qtabwidget.h>
+#include <kpushbutton.h>
+#include <qheader.h>
+#include <klistview.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+/*
+ * Constructs a KFTPFilterEditorLayout as a child of 'parent', with the
+ * name 'name' and widget flags set to 'f'.
+ */
+KFTPFilterEditorLayout::KFTPFilterEditorLayout( QWidget* parent, const char* name, WFlags fl )
+ : QWidget( parent, name, fl )
+{
+ if ( !name )
+ setName( "KFTPFilterEditorLayout" );
+ KFTPFilterEditorLayoutLayout = new QGridLayout( this, 1, 1, 11, 6, "KFTPFilterEditorLayoutLayout");
+
+ tabWidget2 = new QTabWidget( this, "tabWidget2" );
+
+ tab = new QWidget( tabWidget2, "tab" );
+ tabLayout = new QGridLayout( tab, 1, 1, 11, 6, "tabLayout");
+
+ layout1 = new QVBoxLayout( 0, 0, 6, "layout1");
+
+ addPatternButton = new KPushButton( tab, "addPatternButton" );
+ layout1->addWidget( addPatternButton );
+
+ editPatternButton = new KPushButton( tab, "editPatternButton" );
+ layout1->addWidget( editPatternButton );
+
+ removePatternButton = new KPushButton( tab, "removePatternButton" );
+ layout1->addWidget( removePatternButton );
+
+ tabLayout->addLayout( layout1, 0, 1 );
+ QSpacerItem* spacer = new QSpacerItem( 31, 111, QSizePolicy::Minimum, QSizePolicy::Expanding );
+ tabLayout->addItem( spacer, 1, 1 );
+
+ layout2 = new QVBoxLayout( 0, 0, 6, "layout2");
+
+ patternList = new KListView( tab, "patternList" );
+ patternList->addColumn( tr2i18n( "Pattern" ) );
+ patternList->addColumn( tr2i18n( "Color" ) );
+ layout2->addWidget( patternList );
+
+ enabledCheck = new QCheckBox( tab, "enabledCheck" );
+ layout2->addWidget( enabledCheck );
+
+ tabLayout->addMultiCellLayout( layout2, 0, 1, 0, 0 );
+ tabWidget2->insertTab( tab, QString("") );
+
+ tab_2 = new QWidget( tabWidget2, "tab_2" );
+
+ textLabel1 = new QLabel( tab_2, "textLabel1" );
+ textLabel1->setGeometry( QRect( 10, 10, 130, 20 ) );
+ tabWidget2->insertTab( tab_2, QString("") );
+
+ tab_3 = new QWidget( tabWidget2, "tab_3" );
+
+ textLabel1_2 = new QLabel( tab_3, "textLabel1_2" );
+ textLabel1_2->setGeometry( QRect( 10, 10, 130, 20 ) );
+ tabWidget2->insertTab( tab_3, QString("") );
+
+ KFTPFilterEditorLayoutLayout->addWidget( tabWidget2, 0, 0 );
+ languageChange();
+ resize( QSize(456, 299).expandedTo(minimumSizeHint()) );
+ clearWState( WState_Polished );
+}
+
+/*
+ * Destroys the object and frees any allocated resources
+ */
+KFTPFilterEditorLayout::~KFTPFilterEditorLayout()
+{
+ // no need to delete child widgets, Qt does it all for us
+}
+
+/*
+ * Sets the strings of the subwidgets using the current
+ * language.
+ */
+void KFTPFilterEditorLayout::languageChange()
+{
+ setCaption( tr2i18n( "Form1" ) );
+ addPatternButton->setText( tr2i18n( "Add pattern" ) );
+ editPatternButton->setText( tr2i18n( "Edit" ) );
+ removePatternButton->setText( tr2i18n( "Remove" ) );
+ patternList->header()->setLabel( 0, tr2i18n( "Pattern" ) );
+ patternList->header()->setLabel( 1, tr2i18n( "Color" ) );
+ enabledCheck->setText( tr2i18n( "Enabled" ) );
+ tabWidget2->changeTab( tab, tr2i18n( "Highlighting" ) );
+ textLabel1->setText( tr2i18n( "<b>Not yet implemented.</b>" ) );
+ tabWidget2->changeTab( tab_2, tr2i18n( "Skip List" ) );
+ textLabel1_2->setText( tr2i18n( "<b>Not yet implemented.</b>" ) );
+ tabWidget2->changeTab( tab_3, tr2i18n( "ASCII xtensions" ) );
+}
+
+#include "kftpfiltereditorlayout.moc"
diff --git a/kftpgrabber/src/widgets/kftpselectserverdialog.cpp b/kftpgrabber/src/widgets/kftpselectserverdialog.cpp
new file mode 100644
index 0000000..f3b6b3a
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpselectserverdialog.cpp
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpbookmarks.h"
+#include "kftpselectserverdialog.h"
+#include "kftpbookmarks.h"
+#include "bookmarks/listview.h"
+#include "misc.h"
+
+#include <klistview.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+using namespace KFTPGrabberBase;
+
+KFTPSelectServerDialog::KFTPSelectServerDialog(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, "Select a server", KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok), m_selectedSite(0)
+{
+ m_tree = new KFTPWidgets::Bookmarks::ListView(KFTPBookmarks::Manager::self(), this);
+ m_tree->setMinimumWidth(270);
+ m_tree->fillBookmarkData();
+
+ connect(m_tree, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotTreeClicked()));
+
+ // Set some stuff
+ setMainWidget(m_tree);
+ enableButtonOK(false);
+}
+
+void KFTPSelectServerDialog::slotTreeClicked()
+{
+ enableButtonOK(false);
+
+ if (m_tree->selectedItem()) {
+ if (static_cast<KFTPWidgets::Bookmarks::ListViewItem*>(m_tree->selectedItem())->m_type == 1) {
+ // Set the active server
+ m_selectedSite = static_cast<KFTPWidgets::Bookmarks::ListViewItem*>(m_tree->selectedItem())->m_site;
+
+ enableButtonOK(true);
+ return;
+ }
+ }
+}
+
+
+#include "kftpselectserverdialog.moc"
diff --git a/kftpgrabber/src/widgets/kftpselectserverdialog.h b/kftpgrabber/src/widgets/kftpselectserverdialog.h
new file mode 100644
index 0000000..8c4afd3
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpselectserverdialog.h
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPSELECTSERVERDIALOG_H
+#define KFTPSELECTSERVERDIALOG_H
+
+#include <kdialogbase.h>
+#include <qdom.h>
+#include <qlistview.h>
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+namespace KFTPWidgets {
+namespace Bookmarks {
+ class ListView;
+}
+}
+
+/**
+@author Jernej Kos
+*/
+class KFTPSelectServerDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ KFTPSelectServerDialog(QWidget *parent = 0, const char *name = 0);
+
+ KFTPBookmarks::Site *getSelectedSite() { return m_selectedSite; }
+private:
+ KFTPWidgets::Bookmarks::ListView *m_tree;
+ KFTPBookmarks::Site *m_selectedSite;
+private slots:
+ void slotTreeClicked();
+};
+
+#endif
diff --git a/kftpgrabber/src/widgets/kftpserverlineedit.cpp b/kftpgrabber/src/widgets/kftpserverlineedit.cpp
new file mode 100644
index 0000000..87e94e3
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpserverlineedit.cpp
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpbookmarks.h"
+#include "kftpserverlineedit.h"
+#include "kftpselectserverdialog.h"
+
+#include <kdialog.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+
+#include <qlayout.h>
+
+KFTPServerLineEdit::KFTPServerLineEdit(QWidget *parent, const char *name, WFlags f)
+ : QWidget(parent, name, f), m_currentSite(0)
+{
+ QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
+
+ m_lineEdit = new KLineEdit(this);
+ m_lineEdit->setReadOnly(true);
+
+ KPushButton *selectButton = new KPushButton(i18n("Select..."), this);
+ selectButton->setFlat(true);
+ connect(selectButton, SIGNAL(clicked()), this, SLOT(slotSelectButtonClicked()));
+
+ layout->addWidget(m_lineEdit);
+ layout->addWidget(selectButton);
+}
+
+KFTPServerLineEdit::~KFTPServerLineEdit()
+{
+}
+
+void KFTPServerLineEdit::setCurrentSite(KFTPBookmarks::Site *site)
+{
+ if (site) {
+ m_currentSite = site;
+ m_lineEdit->setText(m_currentSite->getAttribute("name"));
+
+ emit siteChanged(m_currentSite);
+ } else {
+ m_currentSite = 0L;
+
+ clear();
+ }
+}
+
+void KFTPServerLineEdit::clear()
+{
+ m_lineEdit->clear();
+}
+
+void KFTPServerLineEdit::slotSelectButtonClicked()
+{
+ KFTPSelectServerDialog *dialog = new KFTPSelectServerDialog(this);
+
+ if (dialog->exec() == QDialog::Accepted) {
+ m_currentSite = dialog->getSelectedSite();
+
+ if (m_currentSite)
+ m_lineEdit->setText(m_currentSite->getAttribute("name"));
+ else
+ m_lineEdit->setText(i18n("No name"));
+
+ emit siteChanged(m_currentSite);
+ }
+
+ delete dialog;
+}
+
+
+
+#include "kftpserverlineedit.moc"
diff --git a/kftpgrabber/src/widgets/kftpserverlineedit.h b/kftpgrabber/src/widgets/kftpserverlineedit.h
new file mode 100644
index 0000000..dd471ae
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpserverlineedit.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPSERVERLINEEDIT_H
+#define KFTPSERVERLINEEDIT_H
+
+#include <qwidget.h>
+#include <qdom.h>
+
+#include <klineedit.h>
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+/**
+@author Jernej Kos
+*/
+class KFTPServerLineEdit : public QWidget
+{
+Q_OBJECT
+public:
+ KFTPServerLineEdit(QWidget *parent = 0, const char *name = 0, WFlags f = 0);
+ ~KFTPServerLineEdit();
+
+ KFTPBookmarks::Site *getCurrentSite() { return m_currentSite; }
+ void setCurrentSite(KFTPBookmarks::Site *site);
+
+ void clear();
+private:
+ KFTPBookmarks::Site *m_currentSite;
+ KLineEdit *m_lineEdit;
+private slots:
+ void slotSelectButtonClicked();
+signals:
+ void siteChanged(KFTPBookmarks::Site *site);
+};
+
+#endif
diff --git a/kftpgrabber/src/widgets/kftptabwidget.cpp b/kftpgrabber/src/widgets/kftptabwidget.cpp
new file mode 100644
index 0000000..317f887
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftptabwidget.cpp
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftptabwidget.h"
+#include "kftptabwidget.moc"
+
+KFTPTabWidget::KFTPTabWidget( QWidget* parent, const char* name )
+ : KTabWidget ( parent, name )
+{
+}
+
+
+KFTPTabWidget::~KFTPTabWidget()
+{
+}
+
+void KFTPTabWidget::mousePressEvent( QMouseEvent* )
+{
+ emit mouseEvent( this->name() );
+}
diff --git a/kftpgrabber/src/widgets/kftptabwidget.h b/kftpgrabber/src/widgets/kftptabwidget.h
new file mode 100644
index 0000000..d17efe3
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftptabwidget.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPTABWIDGET_H
+#define KFTPTABWIDGET_H
+
+#include <ktabwidget.h>
+
+/**
+@author Blacknator
+*/
+class KFTPTabWidget : public KTabWidget
+{
+ private:
+ Q_OBJECT
+
+ public:
+ KFTPTabWidget( QWidget* parent = 0, const char* name = 0 );
+ ~KFTPTabWidget();
+
+ virtual void mousePressEvent( QMouseEvent* mouseEvent );
+
+ signals:
+ void mouseEvent( QString widgetName );
+
+};
+
+#endif
diff --git a/kftpgrabber/src/widgets/kftpzeroconflistview.cpp b/kftpgrabber/src/widgets/kftpzeroconflistview.cpp
new file mode 100644
index 0000000..eb60204
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpzeroconflistview.cpp
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "kftpzeroconflistview.h"
+#include "kftpapi.h"
+#include "kftpsession.h"
+
+#include <kdeversion.h>
+
+#if KDE_IS_VERSION(3,4,0)
+#include "kftpzeroconf.h"
+#endif
+
+#include <klocale.h>
+#include <kiconloader.h>
+
+KFTPZeroConfListView::KFTPZeroConfListView(QWidget *parent, const char *name)
+ : KFTPWidgets::ListView(parent, name)
+{
+ // Set some stuff
+ setMinimumWidth(150);
+ setFullWidth(true);
+ addColumn(i18n("Sites Near You"));
+ setRootIsDecorated(true);
+ setEmptyListText(i18n("No sites published."));
+ setItemsRenameable(false);
+
+#if KDE_IS_VERSION(3,4,0)
+ connect(KFTPAPI::getInstance()->zeroConfInterface(), SIGNAL(servicesUpdated()), this, SLOT(slotSitesChanged()));
+ connect(this, SIGNAL(executed(QListViewItem*)), this, SLOT(slotSiteExecuted(QListViewItem*)));
+
+ slotSitesChanged();
+#endif
+}
+
+void KFTPZeroConfListView::slotSitesChanged()
+{
+#if KDE_IS_VERSION(3,4,0)
+ // Update the site list
+ QValueList<DNSSD::RemoteService::Ptr> list = KFTPAPI::getInstance()->zeroConfInterface()->getServiceList();
+
+ clear();
+ if (!list.isEmpty()) {
+ QValueList<DNSSD::RemoteService::Ptr>::iterator end(list.end());
+
+ for (QValueList<DNSSD::RemoteService::Ptr>::iterator i(list.begin()); i != end; ++i) {
+ QString name = (*i)->serviceName();
+ QString ip = (*i)->hostName();
+ QString port = QString::number((*i)->port());
+
+ KListViewItem *site = new KListViewItem(this, name, ip, port);
+ site->setPixmap(0, SmallIcon("lan"));
+ }
+ }
+#endif
+}
+
+void KFTPZeroConfListView::slotSiteExecuted(QListViewItem *item)
+{
+#if KDE_IS_VERSION(3,4,0)
+ // Connect to the site
+ KFTPAPI::getInstance()->mainWindow()->slotQuickConnect(item->text(0), item->text(1), item->text(2).toInt());
+#endif
+}
+
+#include "kftpzeroconflistview.moc"
diff --git a/kftpgrabber/src/widgets/kftpzeroconflistview.h b/kftpgrabber/src/widgets/kftpzeroconflistview.h
new file mode 100644
index 0000000..9aea9aa
--- /dev/null
+++ b/kftpgrabber/src/widgets/kftpzeroconflistview.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPZEROCONFLISTVIEW_H
+#define KFTPZEROCONFLISTVIEW_H
+
+#include "listview.h"
+
+/**
+ * This class provides a list view that displays all current FTP servers published via Zeroconf protocol.
+ *
+ * @author Jernej Kos
+ */
+class KFTPZeroConfListView : public KFTPWidgets::ListView
+{
+Q_OBJECT
+public:
+ KFTPZeroConfListView(QWidget *parent = 0, const char *name = 0);
+private slots:
+ void slotSitesChanged();
+ void slotSiteExecuted(QListViewItem*);
+};
+
+#endif
diff --git a/kftpgrabber/src/widgets/listview.cpp b/kftpgrabber/src/widgets/listview.cpp
new file mode 100644
index 0000000..635130b
--- /dev/null
+++ b/kftpgrabber/src/widgets/listview.cpp
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "listview.h"
+
+#include <qpainter.h>
+#include <qstringlist.h>
+
+namespace KFTPWidgets {
+
+ListView::ListView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+}
+
+
+ListView::~ListView()
+{
+}
+
+void ListView::resizeEvent(QResizeEvent *e)
+{
+ KListView::resizeEvent(e);
+ triggerUpdate();
+}
+
+void ListView::setEmptyListText(const QString &text)
+{
+ m_emptyListText = text;
+ triggerUpdate();
+}
+
+void ListView::drawContentsOffset(QPainter * p, int ox, int oy, int cx, int cy, int cw, int ch)
+{
+ KListView::drawContentsOffset(p, ox, oy, cx, cy, cw, ch);
+
+ if (childCount() == 0 && !m_emptyListText.isEmpty()) {
+ p->setPen(Qt::darkGray);
+
+ QStringList lines = QStringList::split("\n", m_emptyListText);
+ int ypos = 10 + p->fontMetrics().height();
+
+ QStringList::Iterator end(lines.end());
+ for (QStringList::Iterator str( lines.begin() ); str != end; ++str) {
+ p->drawText((viewport()->width()/2)-(p->fontMetrics().width(*str)/2), ypos, *str);
+ ypos += p->fontMetrics().lineSpacing();
+ }
+ }
+}
+
+}
+
+#include "listview.moc"
diff --git a/kftpgrabber/src/widgets/listview.h b/kftpgrabber/src/widgets/listview.h
new file mode 100644
index 0000000..8718f97
--- /dev/null
+++ b/kftpgrabber/src/widgets/listview.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPLISTVIEW_H
+#define KFTPLISTVIEW_H
+
+#include <klistview.h>
+
+namespace KFTPWidgets {
+
+/**
+ * @author Jernej Kos
+ */
+class ListView : public KListView
+{
+Q_OBJECT
+public:
+ ListView(QWidget *parent = 0, const char *name = 0);
+ ~ListView();
+
+ void setEmptyListText(const QString &text);
+ virtual void resizeEvent(QResizeEvent *e);
+protected:
+ virtual void drawContentsOffset(QPainter * p, int ox, int oy, int cx, int cy, int cw, int ch );
+private:
+ QString m_emptyListText;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/listviewitem.cpp b/kftpgrabber/src/widgets/listviewitem.cpp
new file mode 100644
index 0000000..6b91feb
--- /dev/null
+++ b/kftpgrabber/src/widgets/listviewitem.cpp
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "listviewitem.h"
+
+#include <qheader.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qsimplerichtext.h>
+
+namespace KFTPWidgets {
+
+ListViewItem::ListViewItem(QListView *parent)
+ : KListViewItem(parent)
+{
+}
+
+void ListViewItem::setRichText(int col, const QString &text)
+{
+ setText(col, QString::null);
+ m_richText[col] = text;
+}
+
+void ListViewItem::setup()
+{
+ QListViewItem::setup();
+
+ int maxHeight = height();
+
+ for (int i = 0; i < listView()->header()->count() - 1; i++) {
+ if (text(i).isNull()) {
+ QSimpleRichText rt(m_richText[i], QFont());
+ maxHeight = rt.height() > maxHeight ? rt.height() : maxHeight;
+ }
+ }
+
+ setHeight(maxHeight);
+}
+
+void ListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ if (text(column).isNull()) {
+ KListViewItem::paintCell(p, cg, column, width, alignment);
+ int pad = 0;
+
+ if (pixmap(column)) {
+ pad = pixmap(column)->width() + 5;
+ }
+
+ QSimpleRichText rt(m_richText[column], p->font());
+ rt.draw(p, pad, 0, QRect(pad, 0, width, height()), cg);
+ } else if (m_colors.contains(column)) {
+ QColorGroup _cg(cg);
+ QColor c = _cg.text();
+
+ _cg.setColor(QColorGroup::Text, m_colors[column]);
+ KListViewItem::paintCell(p, _cg, column, width, alignment);
+ _cg.setColor(QColorGroup::Text, c);
+ } else {
+ KListViewItem::paintCell(p, cg, column, width, alignment);
+ }
+}
+
+}
diff --git a/kftpgrabber/src/widgets/listviewitem.h b/kftpgrabber/src/widgets/listviewitem.h
new file mode 100644
index 0000000..b9edc23
--- /dev/null
+++ b/kftpgrabber/src/widgets/listviewitem.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSLISTVIEWITEM_H
+#define KFTPWIDGETSLISTVIEWITEM_H
+
+#include <klistview.h>
+
+#include <qmap.h>
+#include <qcolor.h>
+
+namespace KFTPWidgets {
+
+/**
+ * This class is a simple QListViewItem replacement that supports displaying
+ * richtext.
+ *
+ * @author Jernej Kos
+ */
+class ListViewItem : public KListViewItem
+{
+public:
+ ListViewItem(QListView *parent);
+
+ void setRichText(int col, const QString &text);
+ QString richText(int col) { return m_richText[col]; }
+
+ void setTextColor(int col, QColor color) { m_colors[col] = color; }
+
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+ void setup();
+private:
+ QMap<int, QString> m_richText;
+ QMap<int, QColor> m_colors;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/logview.cpp b/kftpgrabber/src/widgets/logview.cpp
new file mode 100644
index 0000000..bac9df8
--- /dev/null
+++ b/kftpgrabber/src/widgets/logview.cpp
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "logview.h"
+#include "misc/config.h"
+#include "kftpapi.h"
+
+#include <klocale.h>
+#include <kstdaction.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+namespace KFTPWidgets {
+
+LogView::LogView(QWidget *parent, const char *name)
+ : KTextEdit(parent, name)
+{
+ setTextFormat(Qt::LogText);
+ setMaxLogLines(200);
+ setFont(KFTPCore::Config::logFont());
+
+ // Override disabled background of KTextEdit
+ unsetPalette();
+
+ // Init actions
+ m_saveToFileAction = KStdAction::saveAs(this, SLOT(slotSaveToFile()), KFTPAPI::getInstance()->mainWindow()->actionCollection(), "log_safeAs");
+ m_clearLogAction = KStdAction::clear(this, SLOT(slotClearLog()), KFTPAPI::getInstance()->mainWindow()->actionCollection(), "log_clear");
+
+ append(i18n("<b>KFTPGrabber</b> logger initialized.<br>"));
+}
+
+
+LogView::~LogView()
+{
+}
+
+QPopupMenu *LogView::createPopupMenu(const QPoint &pos)
+{
+ QPopupMenu *menu = KTextEdit::createPopupMenu(pos);
+
+ m_saveToFileAction->plug(menu);
+ menu->insertSeparator();
+ m_clearLogAction->plug(menu);
+
+ return menu;
+}
+
+void LogView::slotSaveToFile()
+{
+ QString savePath = KFileDialog::getSaveFileName();
+
+ if (!savePath.isEmpty()) {
+ QFile file(savePath);
+ if (file.open(IO_WriteOnly)) {
+ QTextStream stream(&file);
+ stream << text();
+ file.close();
+ } else {
+ KMessageBox::error(0L, i18n("Unable to open file for writing."));
+ }
+ }
+}
+
+void LogView::slotClearLog()
+{
+ clear();
+}
+
+void LogView::append(const QString &text)
+{
+ QDateTime dt = QDateTime::currentDateTime();
+ QTextEdit::append("[" + dt.toString("hh:mm:ss") + "] " + text);
+ scrollToBottom();
+}
+
+void LogView::ftpLog(int type, const QString &text)
+{
+ // Set the font if changed
+ if (KFTPCore::Config::logFont() != font())
+ setFont(KFTPCore::Config::logFont());
+
+ QString line;
+ line = text.stripWhiteSpace();
+ line.replace("<", "&lt;");
+ line.replace(">", "&gt;");
+ switch (type) {
+ case 0: {
+ QString code = line.section(" ", 0, 0);
+ QString text = line.mid(line.find(' ')+1);
+ append("<font color=" + KFTPCore::Config::logResponsesColor().name() + "><b>" + code + "</b> " + text + "</font><br>");
+ break;
+ }
+ case 1: {
+ if (line.left(4) == "PASS")
+ line = "PASS (hidden)";
+
+ append("<font color=" + KFTPCore::Config::logCommandsColor().name() + "><b>" + line + "</b></font><br>");
+ break;
+ }
+ case 2: append("<font color=" + KFTPCore::Config::logMultilineColor().name() + ">" + line + "</font><br>"); break;
+ case 3: append("<font color=" + KFTPCore::Config::logStatusColor().name() + "><b>*** " + line + "</b></font><br>"); break;
+ case 4: append("<font color=" + KFTPCore::Config::logErrorColor().name() + "><b>*** " + line + "</b></font><br>"); break;
+ }
+}
+
+}
+
+#include "logview.moc"
diff --git a/kftpgrabber/src/widgets/logview.h b/kftpgrabber/src/widgets/logview.h
new file mode 100644
index 0000000..ac2f9e3
--- /dev/null
+++ b/kftpgrabber/src/widgets/logview.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPLOGVIEW_H
+#define KFTPLOGVIEW_H
+
+#include <ktextedit.h>
+#include <kaction.h>
+
+#include <qpopupmenu.h>
+
+namespace KFTPWidgets {
+
+/**
+ * This class provides a simple log widget.
+ *
+ * @author Jernej Kos
+ */
+class LogView : public KTextEdit
+{
+Q_OBJECT
+public:
+ LogView(QWidget *parent = 0, const char *name = 0);
+ ~LogView();
+
+ void append(const QString &text);
+protected:
+ KAction *m_saveToFileAction;
+ KAction *m_clearLogAction;
+
+ QPopupMenu *createPopupMenu(const QPoint &pos);
+public slots:
+ void ftpLog(int type, const QString &text);
+private slots:
+ void slotSaveToFile();
+ void slotClearLog();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/multitabbar.cpp b/kftpgrabber/src/widgets/multitabbar.cpp
new file mode 100644
index 0000000..2b7c583
--- /dev/null
+++ b/kftpgrabber/src/widgets/multitabbar.cpp
@@ -0,0 +1,1160 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2001-2003 by Joseph Wenninger <jowenn@kde.org>
+ * Copyright (C) 2005 by Mark Kretschmann <markey@web.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "multitabbar.h"
+#include "multitabbar.moc"
+#include "multitabbar_p.h"
+#include "multitabbar_p.moc"
+
+#include <math.h>
+
+#include <qapplication.h>
+#include <qbutton.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qfontmetrics.h>
+#include <qstyle.h>
+#include <qtimer.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kstringhandler.h>
+
+#define NEARBYINT(i) ((int(float(i) + 0.5)))
+
+namespace KFTPWidgets {
+
+class MultiTabBarTabPrivate
+{
+public:
+ QPixmap pix;
+};
+
+
+MultiTabBarInternal::MultiTabBarInternal( QWidget *parent, MultiTabBar::MultiTabBarMode bm ) : QScrollView( parent )
+{
+ m_expandedTabSize = -1;
+ m_showActiveTabTexts = false;
+ m_tabs.setAutoDelete( true );
+ m_barMode = bm;
+ setHScrollBarMode( AlwaysOff );
+ setVScrollBarMode( AlwaysOff );
+ if ( bm == MultiTabBar::Vertical ) {
+ box = new QWidget( viewport() );
+ mainLayout = new QVBoxLayout( box );
+ mainLayout->setAutoAdd( true );
+ box->setFixedWidth( 24 );
+ setFixedWidth( 24 );
+ } else {
+ box = new QWidget( viewport() );
+ mainLayout = new QHBoxLayout( box );
+ mainLayout->setAutoAdd( true );
+ box->setFixedHeight( 24 );
+ setFixedHeight( 24 );
+ }
+ addChild( box );
+ setFrameStyle( NoFrame );
+ viewport() ->setBackgroundMode( Qt::PaletteBackground );
+ /* box->setPaletteBackgroundColor(Qt::red);
+ setPaletteBackgroundColor(Qt::green);*/
+}
+
+void MultiTabBarInternal::setStyle( enum MultiTabBar::MultiTabBarStyle style )
+{
+ m_style = style;
+ for ( uint i = 0;i < m_tabs.count();i++ )
+ m_tabs.at( i ) ->setStyle( m_style );
+
+ if ( ( m_style == MultiTabBar::KDEV3 ) ||
+ ( m_style == MultiTabBar::KDEV3ICON ) ||
+ ( m_style == MultiTabBar::AMAROK ) ) {
+ delete mainLayout;
+ mainLayout = 0;
+ resizeEvent( 0 );
+ } else if ( mainLayout == 0 ) {
+ if ( m_barMode == MultiTabBar::Vertical ) {
+ box = new QWidget( viewport() );
+ mainLayout = new QVBoxLayout( box );
+ box->setFixedWidth( 24 );
+ setFixedWidth( 24 );
+ } else {
+ box = new QWidget( viewport() );
+ mainLayout = new QHBoxLayout( box );
+ box->setFixedHeight( 24 );
+ setFixedHeight( 24 );
+ }
+ addChild( box );
+ for ( uint i = 0;i < m_tabs.count();i++ )
+ mainLayout->add( m_tabs.at( i ) );
+ mainLayout->setAutoAdd( true );
+
+ }
+ viewport() ->repaint();
+}
+
+void MultiTabBarInternal::drawContents ( QPainter * paint, int clipx, int clipy, int clipw, int cliph )
+{
+ QScrollView::drawContents ( paint , clipx, clipy, clipw, cliph );
+
+ if ( m_position == MultiTabBar::Right ) {
+
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, 0, viewport() ->height() );
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 1, 0, 1, viewport() ->height() );
+
+
+ } else
+ if ( m_position == MultiTabBar::Left ) {
+ paint->setPen( colorGroup().light() );
+ paint->drawLine( 23, 0, 23, viewport() ->height() );
+ paint->drawLine( 22, 0, 22, viewport() ->height() );
+
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, 0, viewport() ->height() );
+ } else
+ if ( m_position == MultiTabBar::Bottom ) {
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, viewport() ->width(), 0 );
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 0, 1, viewport() ->width(), 1 );
+ } else {
+ paint->setPen( colorGroup().light() );
+ paint->drawLine( 0, 23, viewport() ->width(), 23 );
+ paint->drawLine( 0, 22, viewport() ->width(), 22 );
+
+ /* paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,viewport()->height());*/
+
+ }
+
+
+}
+
+void MultiTabBarInternal::contentsMousePressEvent( QMouseEvent *ev )
+{
+ ev->ignore();
+}
+
+void MultiTabBarInternal::mousePressEvent( QMouseEvent *ev )
+{
+ ev->ignore();
+}
+
+
+#define CALCDIFF(m_tabs,diff,i) if (m_lines>(int)lines) {\
+ /*kdDebug()<<"i="<<i<<" tabCount="<<tabCount<<" space="<<space<<endl;*/ \
+ uint ulen=0;\
+ diff=0; \
+ for (uint i2=i;i2<tabCount;i2++) {\
+ uint l1=m_tabs.at(i2)->neededSize();\
+ if ((ulen+l1)>space){\
+ if (ulen==0) diff=0;\
+ else diff=((float)(space-ulen))/(i2-i);\
+ break;\
+ }\
+ ulen+=l1;\
+ }\
+ } else {diff=0; }
+
+
+void MultiTabBarInternal::resizeEvent( QResizeEvent *ev )
+{
+ /* kdDebug()<<"MultiTabBarInternal::resizeEvent"<<endl;
+ kdDebug()<<"MultiTabBarInternal::resizeEvent - box geometry"<<box->geometry()<<endl;
+ kdDebug()<<"MultiTabBarInternal::resizeEvent - geometry"<<geometry()<<endl;*/
+ if ( ev ) QScrollView::resizeEvent( ev );
+
+ if ( ( m_style == MultiTabBar::KDEV3 ) ||
+ ( m_style == MultiTabBar::KDEV3ICON ) ||
+ ( m_style == MultiTabBar::AMAROK ) ) {
+ box->setGeometry( 0, 0, width(), height() );
+ int lines = 1;
+ uint space;
+ float tmp = 0;
+ if ( ( m_position == MultiTabBar::Bottom ) || ( m_position == MultiTabBar::Top ) )
+ space = width();
+ else
+ space = height();
+
+ int cnt = 0;
+ //CALCULATE LINES
+ const uint tabCount = m_tabs.count();
+ for ( uint i = 0;i < tabCount;i++ ) {
+ cnt++;
+ tmp += m_tabs.at( i ) ->neededSize();
+ if ( tmp > space ) {
+ if ( cnt > 1 ) i--;
+ else if ( i == ( tabCount - 1 ) ) break;
+ cnt = 0;
+ tmp = 0;
+ lines++;
+ }
+ }
+ //SET SIZE & PLACE
+ float diff = 0;
+ cnt = 0;
+
+ if ( ( m_position == MultiTabBar::Bottom ) || ( m_position == MultiTabBar::Top ) ) {
+
+ setFixedHeight( lines * 24 );
+ box->setFixedHeight( lines * 24 );
+ m_lines = height() / 24 - 1;
+ lines = 0;
+ CALCDIFF( m_tabs, diff, 0 )
+ tmp = -diff;
+
+ //kdDebug()<<"m_lines recalculated="<<m_lines<<endl;
+ for ( uint i = 0;i < tabCount;i++ ) {
+ MultiTabBarTab *tab = m_tabs.at( i );
+ cnt++;
+ tmp += tab->neededSize() + diff;
+ if ( tmp > space ) {
+ //kdDebug()<<"about to start new line"<<endl;
+ if ( cnt > 1 ) {
+ CALCDIFF( m_tabs, diff, i )
+ i--;
+ } else {
+ //kdDebug()<<"placing line on old line"<<endl;
+ kdDebug() << "diff=" << diff << endl;
+ tab->removeEventFilter( this );
+ tab->move( NEARBYINT( tmp - tab->neededSize() ), lines * 24 );
+ // tab->setFixedWidth(tab->neededSize()+diff);
+ tab->setFixedWidth( NEARBYINT( tmp + diff ) - tab->x() );;
+ tab->installEventFilter( this );
+ CALCDIFF( m_tabs, diff, ( i + 1 ) )
+
+ }
+ tmp = -diff;
+ cnt = 0;
+ lines++;
+ //kdDebug()<<"starting new line:"<<lines<<endl;
+
+ } else {
+ //kdDebug()<<"Placing line on line:"<<lines<<" pos: (x/y)=("<<tmp-m_tabs.at(i)->neededSize()<<"/"<<lines*24<<")"<<endl;
+ //kdDebug()<<"diff="<<diff<<endl;
+ tab->removeEventFilter( this );
+ tab->move( NEARBYINT( tmp - tab->neededSize() ), lines * 24 );
+ tab->setFixedWidth( NEARBYINT( tmp + diff ) - tab->x() );;
+
+ //tab->setFixedWidth(tab->neededSize()+diff);
+ tab->installEventFilter( this );
+
+ }
+ }
+ } else {
+ setFixedWidth( lines * 24 );
+ box->setFixedWidth( lines * 24 );
+ m_lines = lines = width() / 24;
+ lines = 0;
+ CALCDIFF( m_tabs, diff, 0 )
+ tmp = -diff;
+
+ for ( uint i = 0;i < tabCount;i++ ) {
+ MultiTabBarTab *tab = m_tabs.at( i );
+ cnt++;
+ tmp += tab->neededSize() + diff;
+ if ( tmp > space ) {
+ if ( cnt > 1 ) {
+ CALCDIFF( m_tabs, diff, i );
+ tmp = -diff;
+ i--;
+ } else {
+ tab->removeEventFilter( this );
+ tab->move( lines * 24, NEARBYINT( tmp - tab->neededSize() ) );
+ tab->setFixedHeight( NEARBYINT( tmp + diff ) - tab->y() );;
+ tab->installEventFilter( this );
+ }
+ cnt = 0;
+ tmp = -diff;
+ lines++;
+ } else {
+ tab->removeEventFilter( this );
+ tab->move( lines * 24, NEARBYINT( tmp - tab->neededSize() ) );
+ tab->setFixedHeight( NEARBYINT( tmp + diff ) - tab->y() );;
+ tab->installEventFilter( this );
+ }
+ }
+ }
+
+
+ //kdDebug()<<"needed lines:"<<m_lines<<endl;
+ } else {
+ int size = 0; /*move the calculation into another function and call it only on add tab and tab click events*/
+ for ( int i = 0;i < ( int ) m_tabs.count();i++ )
+ size += ( m_barMode == MultiTabBar::Vertical ? m_tabs.at( i ) ->height() : m_tabs.at( i ) ->width() );
+ if ( ( m_position == MultiTabBar::Bottom ) || ( m_position == MultiTabBar::Top ) )
+ box->setGeometry( 0, 0, size, height() );
+ else box->setGeometry( 0, 0, width(), size );
+
+ }
+}
+
+
+void MultiTabBarInternal::showActiveTabTexts( bool show )
+{
+ m_showActiveTabTexts = show;
+}
+
+
+MultiTabBarTab* MultiTabBarInternal::tab( int id ) const
+{
+ for ( QPtrListIterator<MultiTabBarTab> it( m_tabs );it.current();++it ) {
+ if ( it.current() ->id() == id ) return it.current();
+ }
+ return 0;
+}
+
+bool MultiTabBarInternal::eventFilter( QObject *, QEvent *e )
+{
+ if ( e->type() == QEvent::Resize )
+ resizeEvent( 0 );
+
+ //PATCH by markey: Allow switching of tabs with mouse wheel
+ if ( e->type() == QEvent::Wheel ) {
+ QWheelEvent* event = static_cast<QWheelEvent*>( e );
+ const int delta = event->delta() / 120;
+
+ // Determine which tab is currently active
+ uint i;
+ for( i = 0; i < m_tabs.count(); i++ )
+ if ( m_tabs.at( i )->isOn() ) break;
+
+ // Calculate index of the new tab to activate
+ int newTab = i - delta;
+ if ( newTab < 0 ) newTab = 0;
+ if ( newTab > (int)m_tabs.count() - 1 ) newTab = m_tabs.count() - 1;
+
+ if ( i < m_tabs.count() && newTab != (int)i )
+ m_tabs.at( newTab )->animateClick();
+
+ // Must return true here for the wheel to work properly
+ return true;
+ }
+
+ return false;
+}
+
+int MultiTabBarInternal::appendTab(const QPixmap &pic , int id, const QString &text)
+{
+ MultiTabBarTab *tab;
+
+ m_tabs.append(tab = new MultiTabBarTab(this, pic, text, id, box, m_position, m_style));
+ tab->installEventFilter(this);
+ tab->showActiveTabText(m_showActiveTabTexts);
+
+ if (m_style == MultiTabBar::KONQSBC) {
+ if (m_expandedTabSize < tab->neededSize()) {
+ m_expandedTabSize = tab->neededSize();
+
+ for (uint i = 0; i < m_tabs.count(); i++)
+ m_tabs.at(i)->setSize(m_expandedTabSize);
+ } else {
+ tab->setSize(m_expandedTabSize);
+ }
+ } else {
+ tab->updateState();
+ }
+
+ tab->show();
+ resizeEvent(0);
+
+ return 0;
+}
+
+void MultiTabBarInternal::removeTab( int id )
+{
+ for ( uint pos = 0;pos < m_tabs.count();pos++ ) {
+ if ( m_tabs.at( pos ) ->id() == id ) {
+ m_tabs.remove( pos );
+ resizeEvent( 0 );
+ break;
+ }
+ }
+}
+
+void MultiTabBarInternal::setPosition( enum MultiTabBar::MultiTabBarPosition pos )
+{
+ m_position = pos;
+ for ( uint i = 0;i < m_tabs.count();i++ )
+ m_tabs.at( i ) ->setTabsPosition( m_position );
+ viewport() ->repaint();
+}
+
+
+MultiTabBarButton::MultiTabBarButton(MultiTabBarInternal *tb, const QPixmap& pic, const QString& text, QPopupMenu *popup,
+ int id, QWidget *parent, MultiTabBar::MultiTabBarPosition pos, MultiTabBar::MultiTabBarStyle style )
+ : QPushButton(QIconSet(), text, parent),
+ m_tb(tb),
+ m_style(style),
+ m_animCount(0),
+ m_animTimer(new QTimer(this))
+{
+ setIconSet(pic);
+ setText(text);
+
+ m_position = pos;
+
+ if (popup)
+ setPopup( popup );
+
+ setFlat(true);
+ setFixedHeight(24);
+ setFixedWidth(24);
+ m_id = id;
+
+ connect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
+ connect(m_animTimer, SIGNAL(timeout()), this, SLOT(slotAnimTimer()));
+}
+
+MultiTabBarButton::MultiTabBarButton(MultiTabBarInternal *tb, const QString& text, QPopupMenu *popup,
+ int id, QWidget *parent, MultiTabBar::MultiTabBarPosition pos, MultiTabBar::MultiTabBarStyle style )
+ : QPushButton(QIconSet(), text, parent),
+ m_tb(tb),
+ m_style(style),
+ m_animCount(0),
+ m_animTimer(new QTimer(this))
+{
+ setText(text);
+ m_position = pos;
+
+ if (popup)
+ setPopup(popup);
+
+ setFlat(true);
+ setFixedHeight(24);
+ setFixedWidth(24);
+ m_id = id;
+
+ connect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
+ connect(m_animTimer, SIGNAL(timeout()), this, SLOT(slotAnimTimer()));
+}
+
+MultiTabBarButton::~MultiTabBarButton()
+{}
+
+int MultiTabBarButton::id() const
+{
+ return m_id;
+}
+
+void MultiTabBarButton::setText(const QString& text)
+{
+ QPushButton::setText(text);
+ m_text = text;
+}
+
+void MultiTabBarButton::slotClicked()
+{
+ emit clicked(m_id);
+}
+
+void MultiTabBarButton::setPosition(MultiTabBar::MultiTabBarPosition pos)
+{
+ m_position = pos;
+ repaint();
+}
+
+void MultiTabBarButton::setStyle(MultiTabBar::MultiTabBarStyle style)
+{
+ m_style = style;
+ repaint();
+}
+
+void MultiTabBarButton::hideEvent(QHideEvent* he)
+{
+ QPushButton::hideEvent(he);
+
+ MultiTabBar *tb = dynamic_cast<MultiTabBar*>(parentWidget());
+ if (tb)
+ tb->updateSeparator();
+}
+
+void MultiTabBarButton::showEvent(QShowEvent* he)
+{
+ QPushButton::showEvent(he);
+
+ MultiTabBar *tb = dynamic_cast<MultiTabBar*>(parentWidget());
+ if (tb)
+ tb->updateSeparator();
+}
+
+void MultiTabBarButton::enterEvent(QEvent*)
+{
+ m_animEnter = true;
+ m_animCount = 0;
+
+ m_animTimer->start(ANIM_INTERVAL);
+}
+
+void MultiTabBarButton::leaveEvent(QEvent*)
+{
+ if (m_animCount == 0)
+ m_animCount = 1;
+
+ m_animEnter = false;
+ m_animTimer->start(ANIM_INTERVAL);
+}
+
+void MultiTabBarButton::slotAnimTimer()
+{
+ if (m_animEnter) {
+ m_animCount += 1;
+ repaint(false);
+
+ if (m_animCount >= ANIM_MAX)
+ m_animTimer->stop();
+ } else {
+ m_animCount -= 1;
+ repaint(false);
+
+ if (m_animCount <= 0)
+ m_animTimer->stop();
+ }
+}
+
+QSize MultiTabBarButton::sizeHint() const
+{
+ constPolish();
+
+ int w = 0, h = 0;
+
+ // calculate contents size...
+#ifndef QT_NO_ICONSET
+ if ( iconSet() && !iconSet() ->isNull() ) {
+ int iw = iconSet() ->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4;
+ int ih = iconSet() ->pixmap( QIconSet::Small, QIconSet::Normal ).height();
+ w += iw;
+ h = QMAX( h, ih );
+ }
+#endif
+ if ( isMenuButton() )
+ w += style().pixelMetric( QStyle::PM_MenuButtonIndicator, this );
+
+ if ( pixmap() ) {
+ QPixmap * pm = ( QPixmap * ) pixmap();
+ w += pm->width();
+ h += pm->height();
+ } else {
+ QString s( text() );
+ bool empty = s.isEmpty();
+ if ( empty )
+ s = QString::fromLatin1( "XXXX" );
+ QFontMetrics fm = fontMetrics();
+ QSize sz = fm.size( ShowPrefix, s );
+ if ( !empty || !w )
+ w += sz.width();
+ if ( !empty || !h )
+ h = QMAX( h, sz.height() );
+ }
+
+ //PATCH by markey
+ if ((m_style == MultiTabBar::AMAROK)) {
+ if (m_position == MultiTabBar::Bottom)
+ w = (parentWidget()->width() - 3) / m_tb->tabs()->count();
+ else
+ h = (parentWidget()->height() - 3) / m_tb->tabs()->count();
+ }
+
+ return ( style().sizeFromContents( QStyle::CT_ToolButton, this, QSize( w, h ) ).
+ expandedTo( QApplication::globalStrut() ) );
+}
+
+
+MultiTabBarTab::MultiTabBarTab( MultiTabBarInternal *tb, const QPixmap& pic, const QString& text,
+ int id, QWidget *parent, MultiTabBar::MultiTabBarPosition pos,
+ MultiTabBar::MultiTabBarStyle style )
+ : MultiTabBarButton(tb, text, 0, id, parent, pos, style),
+ m_showActiveTabText(false)
+{
+ d = new MultiTabBarTabPrivate();
+ setIcon( pic );
+ m_expandedSize = 24;
+ setToggleButton( true );
+
+ // Prevent flicker on redraw
+ setWFlags( getWFlags() | Qt::WNoAutoErase );
+}
+
+MultiTabBarTab::~MultiTabBarTab()
+{
+ delete d;
+}
+
+
+void MultiTabBarTab::setTabsPosition( MultiTabBar::MultiTabBarPosition pos )
+{
+ if ( ( pos != m_position ) && ( ( pos == MultiTabBar::Left ) || ( pos == MultiTabBar::Right ) ) ) {
+ if ( !d->pix.isNull() ) {
+ QWMatrix temp; // (1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
+ temp.rotate( 180 );
+ d->pix = d->pix.xForm( temp );
+ setIconSet( d->pix );
+ }
+ }
+
+ setPosition( pos );
+ // repaint();
+}
+
+void MultiTabBarTab::setIcon( const QString& icon )
+{
+ QPixmap pic = SmallIcon( icon );
+ setIcon( pic );
+}
+
+void MultiTabBarTab::setIcon( const QPixmap& icon )
+{
+
+ if ( m_style != MultiTabBar::KDEV3 ) {
+ if ( ( m_position == MultiTabBar::Left ) || ( m_position == MultiTabBar::Right ) ) {
+ QWMatrix rotateMatrix;
+ if ( m_position == MultiTabBar::Left )
+ rotateMatrix.rotate( 90 );
+ else
+ rotateMatrix.rotate( -90 );
+ QPixmap pic = icon.xForm( rotateMatrix ); //TODO FIX THIS, THIS SHOWS WINDOW
+ d->pix = pic;
+ setIconSet( pic );
+ } else setIconSet( icon );
+ }
+}
+
+void MultiTabBarTab::slotClicked()
+{
+ if (m_animTimer->isActive()) {
+ m_animCount = ANIM_MAX;
+ m_animTimer->stop();
+ repaint();
+ }
+
+ updateState();
+ MultiTabBarButton::slotClicked();
+}
+
+void MultiTabBarTab::setState(bool b)
+{
+ setOn(b);
+ updateState();
+}
+
+void MultiTabBarTab::updateState()
+{
+
+ if ( m_style != MultiTabBar::KONQSBC ) {
+ if ( ( m_style == MultiTabBar::KDEV3 ) || ( m_style == MultiTabBar::KDEV3ICON ) || ( m_style == MultiTabBar::AMAROK ) || ( isOn() ) ) {
+ QPushButton::setText( m_text );
+ } else {
+ kdDebug() << "MultiTabBarTab::updateState(): setting text to an empty QString***************" << endl;
+ QPushButton::setText( QString::null );
+ }
+
+ if ( ( m_position == MultiTabBar::Right || m_position == MultiTabBar::Left ) ) {
+ setFixedWidth( 24 );
+ if ( ( m_style == MultiTabBar::KDEV3 ) || ( m_style == MultiTabBar::KDEV3ICON ) || ( m_style == MultiTabBar::AMAROK ) || ( isOn() ) ) {
+ setFixedHeight( MultiTabBarButton::sizeHint().width() );
+ } else setFixedHeight( 36 );
+ } else {
+ setFixedHeight( 24 );
+ if ( ( m_style == MultiTabBar::KDEV3 ) || ( m_style == MultiTabBar::KDEV3ICON ) || ( m_style == MultiTabBar::AMAROK ) || ( isOn() ) ) {
+ setFixedWidth( MultiTabBarButton::sizeHint().width() );
+ } else setFixedWidth( 36 );
+ }
+ } else {
+ if ( ( !isOn() ) || ( !m_showActiveTabText ) ) {
+ setFixedWidth( 24 );
+ setFixedHeight( 24 );
+ return ;
+ }
+ if ( ( m_position == MultiTabBar::Right || m_position == MultiTabBar::Left ) )
+ setFixedHeight( m_expandedSize );
+ else
+ setFixedWidth( m_expandedSize );
+ }
+ QApplication::sendPostedEvents( 0, QEvent::Paint | QEvent::Move | QEvent::Resize | QEvent::LayoutHint );
+ QApplication::flush();
+}
+
+int MultiTabBarTab::neededSize()
+{
+ if (m_style == MultiTabBar::AMAROK) {
+ if (m_position == MultiTabBar::Left)
+ return (parentWidget()->height() - 3) / m_tb->tabs()->count();
+ else
+ return (parentWidget()->width() - 3) / m_tb->tabs()->count();
+ } else {
+ return (((m_style != MultiTabBar::KDEV3 ) ? 24 : 0) + QFontMetrics(QFont()).width(m_text) + 6);
+ }
+}
+
+void MultiTabBarTab::setSize( int size )
+{
+ m_expandedSize = size;
+ updateState();
+}
+
+void MultiTabBarTab::showActiveTabText(bool show)
+{
+ m_showActiveTabText = show;
+}
+
+void MultiTabBarTab::drawButtonLabel(QPainter *p)
+{
+ drawButton(p);
+}
+void MultiTabBarTab::drawButton(QPainter *paint)
+{
+ if (m_style == MultiTabBar::AMAROK) {
+ drawButtonAmarok(paint);
+ } else if (m_style != MultiTabBar::KONQSBC) {
+ drawButtonStyled(paint);
+ } else {
+ drawButtonClassic(paint);
+ }
+}
+
+void MultiTabBarTab::drawButtonStyled( QPainter *paint )
+{
+
+ QSize sh;
+ const int width = 36; // rotated
+ const int height = 24;
+ if ( ( m_style == MultiTabBar::KDEV3 ) || ( m_style == MultiTabBar::KDEV3ICON ) || ( m_style == MultiTabBar::AMAROK ) || ( isOn() ) ) {
+ if ( ( m_position == MultiTabBar::Left ) || ( m_position == MultiTabBar::Right ) )
+ sh = QSize( this->height(), this->width() ); //MultiTabBarButton::sizeHint();
+ else sh = QSize( this->width(), this->height() );
+ } else
+ sh = QSize( width, height );
+
+ QPixmap pixmap( sh.width(), height ); ///,sh.height());
+ pixmap.fill( eraseColor() );
+ QPainter painter( &pixmap );
+
+
+ QStyle::SFlags st = QStyle::Style_Default;
+
+ st |= QStyle::Style_Enabled;
+
+ if ( isOn() ) st |= QStyle::Style_On;
+
+ style().drawControl( QStyle::CE_PushButton, &painter, this, QRect( 0, 0, pixmap.width(), pixmap.height() ), colorGroup(), st );
+ style().drawControl( QStyle::CE_PushButtonLabel, &painter, this, QRect( 0, 0, pixmap.width(), pixmap.height() ), colorGroup(), st );
+
+ switch ( m_position ) {
+ case MultiTabBar::Left:
+ paint->rotate( -90 );
+ paint->drawPixmap( 1 - pixmap.width(), 0, pixmap );
+ break;
+ case MultiTabBar::Right:
+ paint->rotate( 90 );
+ paint->drawPixmap( 0, 1 - pixmap.height(), pixmap );
+ break;
+
+ default:
+ paint->drawPixmap( 0, 0, pixmap );
+ break;
+ }
+ // style().drawControl(QStyle::CE_PushButtonLabel,painter,this, QRect(0,0,pixmap.width(),pixmap.height()),
+ // colorGroup(),QStyle::Style_Enabled);
+
+
+}
+
+void MultiTabBarTab::drawButtonClassic( QPainter *paint )
+{
+ QPixmap pixmap;
+ if ( iconSet() )
+ pixmap = iconSet() ->pixmap( QIconSet::Small, QIconSet::Normal );
+ paint->fillRect( 0, 0, 24, 24, colorGroup().background() );
+
+ if ( !isOn() ) {
+
+ if ( m_position == MultiTabBar::Right ) {
+ paint->fillRect( 0, 0, 21, 21, QBrush( colorGroup().background() ) );
+
+ paint->setPen( colorGroup().background().dark( 150 ) );
+ paint->drawLine( 0, 22, 23, 22 );
+
+ paint->drawPixmap( 12 - pixmap.width() / 2, 12 - pixmap.height() / 2, pixmap );
+
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, 0, 23 );
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 1, 0, 1, 23 );
+
+ } else
+ if ( ( m_position == MultiTabBar::Bottom ) || ( m_position == MultiTabBar::Top ) ) {
+ paint->fillRect( 0, 1, 23, 22, QBrush( colorGroup().background() ) );
+
+ paint->drawPixmap( 12 - pixmap.width() / 2, 12 - pixmap.height() / 2, pixmap );
+
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 23, 0, 23, 23 );
+
+
+ paint->setPen( colorGroup().light() );
+ paint->drawLine( 0, 22, 23, 22 );
+ paint->drawLine( 0, 23, 23, 23 );
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, 23, 0 );
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 0, 1, 23, 1 );
+
+ } else {
+ paint->setPen( colorGroup().background().dark( 120 ) );
+ paint->drawLine( 0, 23, 23, 23 );
+ paint->fillRect( 0, 0, 23, 21, QBrush( colorGroup().background() ) );
+ paint->drawPixmap( 12 - pixmap.width() / 2, 12 - pixmap.height() / 2, pixmap );
+
+ paint->setPen( colorGroup().light() );
+ paint->drawLine( 23, 0, 23, 23 );
+ paint->drawLine( 22, 0, 22, 23 );
+
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 0, 0, 23 );
+
+ }
+
+
+ } else {
+ if ( m_position == MultiTabBar::Right ) {
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, height() - 1, 23, height() - 1 );
+ paint->drawLine( 0, height() - 2, 23, height() - 2 );
+ paint->drawLine( 23, 0, 23, height() - 1 );
+ paint->drawLine( 22, 0, 22, height() - 1 );
+ paint->fillRect( 0, 0, 21, height() - 3, QBrush( colorGroup().light() ) );
+ paint->drawPixmap( 10 - pixmap.width() / 2, 10 - pixmap.height() / 2, pixmap );
+
+ if ( m_showActiveTabText ) {
+ if ( height() < 25 + 4 ) return ;
+
+ QPixmap tpixmap( height() - 25 - 3, width() - 2 );
+ QPainter painter( &tpixmap );
+
+ painter.fillRect( 0, 0, tpixmap.width(), tpixmap.height(), QBrush( colorGroup().light() ) );
+
+ painter.setPen( colorGroup().text() );
+ painter.drawText( 0, + width() / 2 + QFontMetrics( QFont() ).height() / 2, m_text );
+
+ paint->rotate( 90 );
+ kdDebug() << "tpixmap.width:" << tpixmap.width() << endl;
+ paint->drawPixmap( 25, -tpixmap.height() + 1, tpixmap );
+ }
+
+ } else
+ if ( m_position == MultiTabBar::Top ) {
+ paint->fillRect( 0, 0, width() - 1, 23, QBrush( colorGroup().light() ) );
+ paint->drawPixmap( 10 - pixmap.width() / 2, 10 - pixmap.height() / 2, pixmap );
+ if ( m_showActiveTabText ) {
+ paint->setPen( colorGroup().text() );
+ paint->drawText( 25, height() / 2 + QFontMetrics( QFont() ).height() / 2, m_text );
+ }
+ } else
+ if ( m_position == MultiTabBar::Bottom ) {
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, 23, width() - 1, 23 );
+ paint->drawLine( 0, 22, width() - 1, 22 );
+ paint->fillRect( 0, 0, width() - 1, 21, QBrush( colorGroup().light() ) );
+ paint->drawPixmap( 10 - pixmap.width() / 2, 10 - pixmap.height() / 2, pixmap );
+ if ( m_showActiveTabText ) {
+ paint->setPen( colorGroup().text() );
+ paint->drawText( 25, height() / 2 + QFontMetrics( QFont() ).height() / 2, m_text );
+ }
+
+ } else {
+
+
+ paint->setPen( colorGroup().shadow() );
+ paint->drawLine( 0, height() - 1, 23, height() - 1 );
+ paint->drawLine( 0, height() - 2, 23, height() - 2 );
+ paint->fillRect( 0, 0, 23, height() - 3, QBrush( colorGroup().light() ) );
+ paint->drawPixmap( 10 - pixmap.width() / 2, 10 - pixmap.height() / 2, pixmap );
+ if ( m_showActiveTabText ) {
+
+ if ( height() < 25 + 4 ) return ;
+
+ QPixmap tpixmap( height() - 25 - 3, width() - 2 );
+ QPainter painter( &tpixmap );
+
+ painter.fillRect( 0, 0, tpixmap.width(), tpixmap.height(), QBrush( colorGroup().light() ) );
+
+ painter.setPen( colorGroup().text() );
+ painter.drawText( tpixmap.width() - QFontMetrics( QFont() ).width( m_text ), + width() / 2 + QFontMetrics( QFont() ).height() / 2, m_text );
+
+ paint->rotate( -90 );
+ kdDebug() << "tpixmap.width:" << tpixmap.width() << endl;
+
+ paint->drawPixmap( -24 - tpixmap.width(), 2, tpixmap );
+
+ }
+
+ }
+
+ }
+}
+
+void MultiTabBarTab::drawButtonAmarok(QPainter *paint)
+{
+ QColor fillColor, textColor;
+
+ if (isOn()) {
+ fillColor = blendColors(colorGroup().highlight(), colorGroup().background(), static_cast<int>(m_animCount * 3.5));
+ textColor = blendColors(colorGroup().highlightedText(), colorGroup().text(), static_cast<int>(m_animCount * 4.5));
+ } else {
+ fillColor = blendColors(colorGroup().background(), colorGroup().highlight(), static_cast<int>(m_animCount * 3.5));
+ textColor = blendColors(colorGroup().text(), colorGroup().highlightedText(), static_cast<int>(m_animCount * 4.5));
+ }
+
+ const QPixmap icon = iconSet()->pixmap(QIconSet::Small, QIconSet::Normal);
+
+ // Our pixmap
+ QPixmap pixmap;
+
+ if (m_position == MultiTabBar::Left)
+ pixmap.resize(height(), width());
+ else
+ pixmap.resize(width(), height());
+
+ pixmap.fill(fillColor);
+ QPainter painter(&pixmap);
+
+ // Draw the frame
+ painter.setPen(colorGroup().mid());
+ if (m_id != m_tb->tabs()->count() - 1)
+ painter.drawLine(0, 0, 0, pixmap.height() - 1);
+ painter.drawLine(0, pixmap.height() - 1, pixmap.width() - 1, pixmap.height() - 1);
+
+ // Draw the text
+ QFont font;
+ font.setBold(isOn());
+ painter.setFont(font);
+ QString text = KStringHandler::rPixelSqueeze(m_text, QFontMetrics(font), pixmap.width() - icon.width() - 3);
+ text.replace( "...", ".." );
+ const int textX = pixmap.width() / 2 - QFontMetrics( font ).width( text ) / 2;
+ painter.setPen(textColor);
+ const QRect rect(textX + icon.width() / 2 + 2, 0, pixmap.width(), pixmap.height());
+ painter.drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, text);
+
+ // Draw the icon
+ painter.drawPixmap(textX - icon.width() / 2 - 2, pixmap.height() / 2 - icon.height() / 2, icon);
+
+ // Paint to widget
+ if (m_position == MultiTabBar::Left) {
+ paint->rotate(-90);
+ paint->drawPixmap(1 - pixmap.width(), 0, pixmap);
+ } else {
+ paint->drawPixmap(0, 0, pixmap);
+ }
+}
+
+QColor MultiTabBarTab::blendColors( const QColor& color1, const QColor& color2, int percent )
+{
+ const float factor1 = ( 100 - ( float ) percent ) / 100;
+ const float factor2 = ( float ) percent / 100;
+
+ const int r = static_cast<int>( color1.red() * factor1 + color2.red() * factor2 );
+ const int g = static_cast<int>( color1.green() * factor1 + color2.green() * factor2 );
+ const int b = static_cast<int>( color1.blue() * factor1 + color2.blue() * factor2 );
+
+ QColor result;
+ result.setRgb( r, g, b );
+
+ return result;
+}
+
+
+
+
+MultiTabBar::MultiTabBar( MultiTabBarMode bm, QWidget *parent, const char *name ) : QWidget( parent, name )
+{
+ m_buttons.setAutoDelete( false );
+ if ( bm == Vertical ) {
+ m_l = new QVBoxLayout( this );
+ setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding, true );
+ // setFixedWidth(24);
+ } else {
+ m_l = new QHBoxLayout( this );
+ setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed, true );
+ // setFixedHeight(24);
+ }
+ m_l->setMargin( 0 );
+ m_l->setAutoAdd( false );
+
+ m_internal = new MultiTabBarInternal( this, bm );
+ setPosition( ( bm == MultiTabBar::Vertical ) ? MultiTabBar::Right : MultiTabBar::Bottom );
+ setStyle( VSNET );
+ // setStyle(KDEV3);
+ //setStyle(KONQSBC);
+ m_l->insertWidget( 0, m_internal );
+ m_l->insertWidget( 0, m_btnTabSep = new QFrame( this ) );
+ m_btnTabSep->setFixedHeight( 4 );
+ m_btnTabSep->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ m_btnTabSep->setLineWidth( 2 );
+ m_btnTabSep->hide();
+
+ updateGeometry();
+}
+
+MultiTabBar::~MultiTabBar()
+{}
+
+/*int MultiTabBar::insertButton(QPixmap pic,int id ,const QString&)
+{
+(new KToolbarButton(pic,id,m_internal))->show();
+return 0;
+}*/
+
+int MultiTabBar::appendButton( const QPixmap &pic , int id, QPopupMenu *popup, const QString& )
+{
+ MultiTabBarButton * btn;
+ m_buttons.append(btn = new MultiTabBarButton(m_internal, pic, QString::null, popup, id, this, m_position, m_internal->m_style));
+
+ m_l->insertWidget( 0, btn );
+ btn->show();
+ m_btnTabSep->show();
+
+ return 0;
+}
+
+void MultiTabBar::updateSeparator()
+{
+ bool hideSep = true;
+ for ( QPtrListIterator<MultiTabBarButton> it( m_buttons );it.current();++it ) {
+ if ( it.current() ->isVisibleTo( this ) ) {
+ hideSep = false;
+ break;
+ }
+ }
+ if ( hideSep ) m_btnTabSep->hide();
+ else m_btnTabSep->show();
+
+}
+
+int MultiTabBar::appendTab( const QPixmap &pic , int id , const QString& text )
+{
+ m_internal->appendTab( pic, id, text );
+ return 0;
+}
+
+MultiTabBarButton* MultiTabBar::button( int id ) const
+{
+ for ( QPtrListIterator<MultiTabBarButton> it( m_buttons );it.current();++it ) {
+ if ( it.current() ->id() == id ) return it.current();
+ }
+ return 0;
+}
+
+MultiTabBarTab* MultiTabBar::tab( int id ) const
+{
+ return m_internal->tab( id );
+}
+
+
+
+void MultiTabBar::removeButton( int id )
+{
+ for ( uint pos = 0;pos < m_buttons.count();pos++ ) {
+ if ( m_buttons.at( pos ) ->id() == id ) {
+ m_buttons.take( pos ) ->deleteLater();
+ break;
+ }
+ }
+ if ( m_buttons.count() == 0 ) m_btnTabSep->hide();
+}
+
+void MultiTabBar::removeTab( int id )
+{
+ m_internal->removeTab( id );
+}
+
+void MultiTabBar::setTab( int id, bool state )
+{
+ MultiTabBarTab * ttab = tab( id );
+ if ( ttab ) {
+ ttab->setState( state );
+ }
+}
+
+bool MultiTabBar::isTabRaised( int id ) const
+{
+ MultiTabBarTab * ttab = tab( id );
+ if ( ttab ) {
+ return ttab->isOn();
+ }
+
+ return false;
+}
+
+
+void MultiTabBar::showActiveTabTexts( bool show )
+{
+ m_internal->showActiveTabTexts( show );
+}
+
+void MultiTabBar::setStyle( MultiTabBarStyle style )
+{
+ m_internal->setStyle( style );
+}
+
+void MultiTabBar::setPosition( MultiTabBarPosition pos )
+{
+ m_position = pos;
+ m_internal->setPosition( pos );
+ for ( uint i = 0;i < m_buttons.count();i++ )
+ m_buttons.at( i ) ->setPosition( pos );
+}
+void MultiTabBar::fontChange( const QFont& /* oldFont */ )
+{
+ for ( uint i = 0;i < tabs() ->count();i++ )
+ tabs() ->at( i ) ->resize();
+ repaint();
+}
+
+QPtrList<MultiTabBarTab> *MultiTabBar::tabs()
+{
+ return m_internal->tabs();
+}
+
+QPtrList<MultiTabBarButton>* MultiTabBar::buttons()
+{
+ return & m_buttons;
+}
+
+}
diff --git a/kftpgrabber/src/widgets/multitabbar.h b/kftpgrabber/src/widgets/multitabbar.h
new file mode 100644
index 0000000..ece61bf
--- /dev/null
+++ b/kftpgrabber/src/widgets/multitabbar.h
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2001-2003 by Joseph Wenninger <jowenn@kde.org>
+ * Copyright (C) 2005 by Mark Kretschmann <markey@web.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSMULTITABBAR_H
+#define KFTPWIDGETSMULTITABBAR_H
+
+#include <qscrollview.h>
+#include <qvbox.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+
+class QPixmap;
+class QPainter;
+class QFrame;
+
+namespace KFTPWidgets {
+
+class MultiTabBarPrivate;
+class MultiTabBarTabPrivate;
+class MultiTabBarButtonPrivate;
+class MultiTabBarInternal;
+
+/**
+ * A Widget for horizontal and vertical tabs.
+ * It is possible to add normal buttons to the top/left
+ * The handling if only one tab at a time or multiple tabs
+ * should be raisable is left to the "user".
+ *
+ * @author Joseph Wenninger
+ */
+class MultiTabBar: public QWidget
+{
+Q_OBJECT
+friend class MultiTabBarButton;
+public:
+ enum MultiTabBarMode { Horizontal, Vertical };
+ enum MultiTabBarPosition { Left, Right, Top, Bottom };
+
+ /**
+ * VSNET == Visual Studio .Net like (only show the text of active tabs
+ * KDEV3 == Kdevelop 3 like (always show the text)
+ * KONQSBC == konqy's classic sidebar style (unthemed), this one is disabled
+ * at the moment, but will be renabled soon too
+ * AMAROK == A nice clean style by the amaroK team
+ */
+ enum MultiTabBarStyle { VSNET = 0, KDEV3 = 1, KONQSBC = 2, KDEV3ICON = 3, AMAROK = 4, STYLELAST = 0xffff };
+
+ MultiTabBar(MultiTabBarMode bm, QWidget *parent = 0, const char *name = 0);
+ virtual ~MultiTabBar();
+
+ /**
+ * append a new button to the button area. The button can later on be accessed with button(ID)
+ * eg for connecting signals to it
+ *
+ * @param pic a pixmap for the button
+ * @param id an arbitraty ID value. It will be emitted in the clicked signal for identifying the button
+ * if more than one button is connected to a signals.
+ * @param popup A popup menu which should be displayed if the button is clicked
+ * @param not_used_yet will be used for a popup text in the future
+ */
+ int appendButton(const QPixmap &pic, int id = -1, QPopupMenu* popup = 0, const QString& not_used_yet = QString::null);
+
+ /**
+ * remove a button with the given ID
+ */
+ void removeButton(int id);
+
+ /**
+ * append a new tab to the tab area. It can be accessed lateron with tabb(id);
+ * @param pic a bitmap for the tab
+ * @param id an arbitrary ID which can be used later on to identify the tab
+ * @param text if a mode with text is used it will be the tab text, otherwise a mouse over hint
+ */
+ int appendTab( const QPixmap &pic, int id = -1, const QString& text = QString::null );
+
+ /**
+ * remove a tab with a given ID
+ */
+ void removeTab( int id );
+ /**
+ * set a tab to "raised"
+ * @param id The ID of the tab to manipulate
+ * @param state true == activated/raised, false == not active
+ */
+ void setTab( int id , bool state );
+ /**
+ * return the state of a tab, identified by it's ID
+ */
+ bool isTabRaised( int id ) const;
+ /**
+ * get a pointer to a button within the button area identified by its ID
+ */
+ class MultiTabBarButton *button( int id ) const;
+
+ /**
+ * get a pointer to a tab within the tab area, identiifed by its ID
+ */
+ class MultiTabBarTab *tab( int id ) const;
+ /**
+ * set the real position of the widget.
+ * @param pos if the mode is horizontal, only use top, bottom, if it is vertical use left or right
+ */
+ void setPosition( MultiTabBarPosition pos );
+ /**
+ * set the display style of the tabs
+ */
+ void setStyle( MultiTabBarStyle style );
+ /**
+ * be carefull, don't delete tabs yourself and don't delete the list itself
+ */
+ QPtrList<MultiTabBarTab>* tabs();
+ /**
+ * be carefull, don't delete buttons yourself and don't delete the list itself
+ */
+ QPtrList<MultiTabBarButton>* buttons();
+
+ /**
+ * might vanish, not sure yet
+ */
+ void showActiveTabTexts( bool show = true );
+protected:
+ virtual void fontChange( const QFont& );
+ void updateSeparator();
+private:
+ class MultiTabBarInternal *m_internal;
+ QBoxLayout *m_l;
+ QFrame *m_btnTabSep;
+ QPtrList<MultiTabBarButton> m_buttons;
+ MultiTabBarPosition m_position;
+ MultiTabBarPrivate *d;
+};
+
+/**
+ * This class should never be created except with the appendButton call of MultiTabBar
+ */
+class MultiTabBarButton: public QPushButton
+{
+Q_OBJECT
+public:
+ MultiTabBarButton(MultiTabBarInternal *tb, const QPixmap& pic, const QString&, QPopupMenu *popup,
+ int id, QWidget *parent, MultiTabBar::MultiTabBarPosition pos, MultiTabBar::MultiTabBarStyle style);
+ MultiTabBarButton(MultiTabBarInternal *tb, const QString&, QPopupMenu *popup,
+ int id, QWidget *parent, MultiTabBar::MultiTabBarPosition pos, MultiTabBar::MultiTabBarStyle style);
+ virtual ~MultiTabBarButton();
+
+ int id() const;
+public slots:
+ /**
+ * this is used internaly, but can be used by the user, if (s)he wants to
+ * It the according call of MultiTabBar is invoked though this modifications will be overwritten
+ */
+ void setPosition( MultiTabBar::MultiTabBarPosition );
+ /**
+ * this is used internaly, but can be used by the user, if (s)he wants to
+ * It the according call of MultiTabBar is invoked though this modifications will be overwritten
+ */
+ void setStyle( MultiTabBar::MultiTabBarStyle );
+
+ /**
+ * modify the text of the button
+ */
+ void setText( const QString & );
+
+ QSize sizeHint() const;
+protected:
+ static const int ANIM_INTERVAL = 18;
+ static const int ANIM_MAX = 20;
+
+ MultiTabBarInternal *m_tb;
+
+ MultiTabBar::MultiTabBarPosition m_position;
+ MultiTabBar::MultiTabBarStyle m_style;
+
+ QString m_text;
+ int m_id;
+ bool m_animEnter;
+ int m_animCount;
+ class QTimer *m_animTimer;
+
+ virtual void hideEvent(class QHideEvent*);
+ virtual void showEvent(class QShowEvent*);
+ virtual void enterEvent(class QEvent*);
+ virtual void leaveEvent(class QEvent*);
+private:
+ MultiTabBarButtonPrivate *d;
+signals:
+ /**
+ * this is emitted if the button is clicked
+ * @param id the ID identifying the button
+ */
+ void clicked(int id);
+protected slots:
+ virtual void slotClicked();
+ virtual void slotAnimTimer();
+};
+
+/**
+ * This class should never be created except with the appendTab call of MultiTabBar
+ */
+class MultiTabBarTab: public MultiTabBarButton
+{
+Q_OBJECT
+friend class MultiTabBarInternal;
+public:
+ MultiTabBarTab(MultiTabBarInternal *tb, const QPixmap &pic, const QString&, int id, QWidget *parent,
+ MultiTabBar::MultiTabBarPosition pos, MultiTabBar::MultiTabBarStyle style);
+ virtual ~MultiTabBarTab();
+ /**
+ * set the active state of the tab
+ * @param state true==active false==not active
+ */
+ void setState( bool state );
+ /**
+ * choose if the text should always be displayed
+ * this is only used in classic mode if at all
+ */
+ void showActiveTabText( bool show );
+ void resize() { setSize( neededSize() ); }
+private:
+ bool m_showActiveTabText;
+ int m_expandedSize;
+
+ MultiTabBarTabPrivate *d;
+protected:
+ void setSize( int );
+ int neededSize();
+ void updateState();
+ virtual void drawButton( QPainter * );
+ virtual void drawButtonLabel( QPainter * );
+ void drawButtonStyled( QPainter * );
+ void drawButtonClassic( QPainter * );
+ void drawButtonAmarok( QPainter * );
+ QColor blendColors( const QColor& color1, const QColor& color2, int percent );
+protected slots:
+ virtual void slotClicked();
+ void setTabsPosition( MultiTabBar::MultiTabBarPosition );
+public slots:
+ virtual void setIcon( const QString& );
+ virtual void setIcon( const QPixmap& );
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/multitabbar_p.h b/kftpgrabber/src/widgets/multitabbar_p.h
new file mode 100644
index 0000000..c630f61
--- /dev/null
+++ b/kftpgrabber/src/widgets/multitabbar_p.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2001-2003 by Joseph Wenninger <jowenn@kde.org>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSMULTITABBAR_P_H
+#define KFTPWIDGETSMULTITABBAR_P_H
+
+#include <qscrollview.h>
+
+#include "multitabbar.h"
+
+namespace KFTPWidgets {
+
+class MultiTabBarInternal : public QScrollView
+{
+Q_OBJECT
+friend class MultiTabBar;
+public:
+ MultiTabBarInternal(QWidget *parent,MultiTabBar::MultiTabBarMode bm);
+ int appendTab(const QPixmap &,int=-1,const QString& =QString::null);
+ MultiTabBarTab *tab(int) const;
+ void removeTab(int);
+ void setPosition(enum MultiTabBar::MultiTabBarPosition pos);
+ void setStyle(enum MultiTabBar::MultiTabBarStyle style);
+ void showActiveTabTexts(bool show);
+ QPtrList<MultiTabBarTab>* tabs() { return &m_tabs; }
+private:
+ QWidget *box;
+ QBoxLayout *mainLayout;
+ QPtrList<MultiTabBarTab> m_tabs;
+ enum MultiTabBar::MultiTabBarPosition m_position;
+ bool m_showActiveTabTexts;
+ enum MultiTabBar::MultiTabBarStyle m_style;
+ int m_expandedTabSize;
+ int m_lines;
+ MultiTabBar::MultiTabBarMode m_barMode;
+protected:
+ virtual bool eventFilter(QObject *,QEvent*);
+ virtual void drawContents ( QPainter *, int, int, int, int);
+
+ /**
+ * [contentsM|m]ousePressEvent are reimplemented from QScrollView
+ * in order to ignore all mouseEvents on the viewport, so that the
+ * parent can handle them.
+ */
+ virtual void contentsMousePressEvent(QMouseEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/overlaywidget.cpp b/kftpgrabber/src/widgets/overlaywidget.cpp
new file mode 100644
index 0000000..58ae387
--- /dev/null
+++ b/kftpgrabber/src/widgets/overlaywidget.cpp
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Max Howell <max.howell@methyblue.com>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "overlaywidget.h"
+
+#include <qpoint.h>
+
+namespace KFTPWidgets {
+
+OverlayWidget::OverlayWidget(QWidget *parent, QWidget *anchor)
+ : QFrame(parent->parentWidget()),
+ m_parent(parent),
+ m_anchor(anchor)
+{
+ parent->installEventFilter(this);
+ hide();
+}
+
+void OverlayWidget::reposition()
+{
+ setMaximumSize(parentWidget()->size());
+ adjustSize();
+
+ // P is in the alignWidget's coordinates
+ QPoint p;
+
+ p.setX(m_anchor->width() - width());
+ p.setY(-height());
+
+ // Position in the toplevelWidget's coordinates
+ QPoint pTopLevel = m_anchor->mapTo(topLevelWidget(), p);
+
+ // Position in the widget's parentWidget coordinates
+ QPoint pParent = parentWidget()->mapFrom(topLevelWidget(), pTopLevel);
+
+ if (pParent.x() < 0)
+ pParent.rx() = 0;
+
+ move(pParent);
+}
+
+bool OverlayWidget::event(QEvent *event)
+{
+ if (event->type() == QEvent::ChildInserted)
+ adjustSize();
+
+ return QFrame::event(event);
+}
+
+}
diff --git a/kftpgrabber/src/widgets/overlaywidget.h b/kftpgrabber/src/widgets/overlaywidget.h
new file mode 100644
index 0000000..1140909
--- /dev/null
+++ b/kftpgrabber/src/widgets/overlaywidget.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Max Howell <max.howell@methyblue.com>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETSOVERLAYWIDGET_H
+#define KFTPWIDGETSOVERLAYWIDGET_H
+
+#include <qhbox.h>
+
+namespace KFTPWidgets {
+
+/**
+ * @author Max Howell
+ */
+class OverlayWidget : public QFrame {
+public:
+ OverlayWidget(QWidget *parent, QWidget *anchor);
+ virtual void reposition();
+protected:
+ virtual bool event(QEvent *event);
+private:
+ QWidget *m_parent;
+ QWidget *m_anchor;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/popupmessage.cpp b/kftpgrabber/src/widgets/popupmessage.cpp
new file mode 100644
index 0000000..62ee3c8
--- /dev/null
+++ b/kftpgrabber/src/widgets/popupmessage.cpp
@@ -0,0 +1,314 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Max Howell <max.howell@methyblue.com>
+ * Copyright (C) 2005 Seb Ruiz <me@sebruiz.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "popupmessage.h"
+
+#include <kactivelabel.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include <qfont.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qmessagebox.h>
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+namespace KFTPWidgets {
+
+PopupMessage::PopupMessage(QWidget *parent, QWidget *anchor, int timeout)
+ : OverlayWidget(parent, anchor),
+ m_anchor(anchor),
+ m_parent(parent),
+ m_maskEffect(Slide),
+ m_dissolveSize(0),
+ m_dissolveDelta(-1),
+ m_offset(0),
+ m_counter(0),
+ m_stage(1),
+ m_timeout(timeout),
+ m_showCounter(true)
+{
+ setFrameStyle(QFrame::Panel | QFrame::Raised);
+ setFrameShape(QFrame::StyledPanel);
+ setWFlags(Qt::WX11BypassWM);
+
+ QPalette p = QToolTip::palette();
+ setPalette(p);
+
+ QHBoxLayout *hbox;
+ QLabel *label;
+ KActiveLabel *alabel;
+
+ m_layout = new QVBoxLayout(this, 9, 6);
+ hbox = new QHBoxLayout(m_layout, 12);
+
+ hbox->addWidget(m_countdownFrame = new QFrame(this, "counterVisual"));
+ m_countdownFrame->setFixedWidth(fontMetrics().width("X"));
+ m_countdownFrame->setFrameStyle(QFrame::Plain | QFrame::Box);
+ m_countdownFrame->setPaletteForegroundColor(paletteBackgroundColor().dark());
+
+ label = new QLabel(this, "image");
+ hbox->add(label);
+
+ alabel = new KActiveLabel(this, "label");
+ alabel->setTextFormat(Qt::RichText);
+ alabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+ alabel->setPalette(p);
+
+ hbox->add(alabel);
+
+ hbox = new QHBoxLayout(m_layout);
+
+ hbox->addItem(new QSpacerItem(4, 4, QSizePolicy::Expanding, QSizePolicy::Preferred));
+ hbox->add(new KPushButton(KStdGuiItem::close(), this, "closeButton"));
+
+ connect(child("closeButton"), SIGNAL(clicked()), SLOT(close()));
+}
+
+void PopupMessage::addWidget(QWidget *widget)
+{
+ m_layout->add(widget);
+ adjustSize();
+}
+
+void PopupMessage::setShowCloseButton(bool show)
+{
+ static_cast<KPushButton*>(child("closeButton"))->setShown(show);
+ adjustSize();
+}
+
+void PopupMessage::setShowCounter(bool show)
+{
+ m_showCounter = show;
+ static_cast<QFrame*>(child("counterVisual"))->setShown(show);
+ adjustSize();
+}
+
+void PopupMessage::setText(const QString &text)
+{
+ static_cast<KActiveLabel*>(child("label"))->setText(text);
+ adjustSize();
+}
+
+void PopupMessage::setImage(const QString &location)
+{
+ static_cast<QLabel*>(child("image"))->setPixmap(QPixmap(location));
+ adjustSize();
+}
+
+void PopupMessage::setImage(const QPixmap &pix)
+{
+ static_cast<QLabel*>(child("image"))->setPixmap(pix);
+ adjustSize();
+}
+
+void PopupMessage::close()
+{
+ m_stage = 3;
+ killTimer(m_timerId);
+ m_timerId = startTimer(6);
+}
+
+void PopupMessage::display()
+{
+ m_dissolveSize = 24;
+ m_dissolveDelta = -1;
+
+ if (m_maskEffect == Dissolve) {
+ m_mask.resize(width(), height());
+ dissolveMask();
+ m_timerId = startTimer(1000 / 30);
+ } else {
+ m_timerId = startTimer( 6 );
+ }
+
+ show();
+}
+
+void PopupMessage::timerEvent(QTimerEvent*)
+{
+ switch(m_maskEffect) {
+ case Plain: plainMask(); break;
+ case Slide: slideMask(); break;
+ case Dissolve: dissolveMask(); break;
+ }
+}
+
+void PopupMessage::countDown()
+{
+ if (!m_timeout) {
+ killTimer(m_timerId);
+ return;
+ }
+
+ QFrame *&h = m_countdownFrame;
+
+ if (m_counter < h->height() - 3)
+ QPainter(h).fillRect(2, 2, h->width() - 4, m_counter, palette().active().highlight());
+
+ if (!hasMouse())
+ m_counter++;
+
+ if (m_counter > h->height()) {
+ m_stage = 3;
+ killTimer(m_timerId);
+ m_timerId = startTimer(6);
+ } else {
+ killTimer(m_timerId);
+ m_timerId = startTimer(m_timeout / h->height());
+ }
+}
+
+void PopupMessage::dissolveMask()
+{
+ if (m_stage == 1) {
+ repaint(false);
+ QPainter maskPainter(&m_mask);
+
+ m_mask.fill(Qt::black);
+
+ maskPainter.setBrush(Qt::white);
+ maskPainter.setPen(Qt::white);
+ maskPainter.drawRect(m_mask.rect());
+
+ m_dissolveSize += m_dissolveDelta;
+
+ if (m_dissolveSize > 0) {
+ maskPainter.setRasterOp(Qt::EraseROP);
+
+ int x, y, s;
+ const int size = 16;
+
+ for (y = 0; y < height() + size; y += size) {
+ x = width();
+ s = m_dissolveSize * x / 128;
+
+ for (; x > size; x -= size, s -= 2) {
+ if (s < 0)
+ break;
+
+ maskPainter.drawEllipse(x - s / 2, y - s / 2, s, s);
+ }
+ }
+ } else if (m_dissolveSize < 0) {
+ m_dissolveDelta = 1;
+ killTimer(m_timerId);
+
+ if (m_timeout) {
+ m_timerId = startTimer(40);
+ m_stage = 2;
+ }
+ }
+
+ setMask(m_mask);
+ } else if (m_stage == 2) {
+ countDown();
+ } else {
+ deleteLater();
+ }
+}
+
+void PopupMessage::plainMask()
+{
+ switch (m_stage) {
+ case 1: {
+ // Raise
+ killTimer(m_timerId);
+
+ if (m_timeout) {
+ m_timerId = startTimer(40);
+ m_stage = 2;
+ }
+
+ break;
+ }
+
+ case 2: {
+ // Counter
+ countDown();
+ break;
+ }
+
+ case 3: {
+ // Lower/Remove
+ deleteLater();
+ break;
+ }
+ }
+}
+
+void PopupMessage::slideMask()
+{
+ switch (m_stage) {
+ case 1: {
+ // Raise
+ move(0, m_parent->y() - m_offset);
+ m_offset++;
+
+ if (m_offset > height()) {
+ killTimer(m_timerId);
+
+ if (m_timeout) {
+ m_timerId = startTimer(40);
+ m_stage = 2;
+ }
+ }
+ break;
+ }
+
+ case 2: {
+ // Fill in pause timer bar
+ countDown();
+ break;
+ }
+
+ case 3: {
+ // Lower
+ m_offset--;
+ move(0, m_parent->y() - m_offset);
+
+ if (m_offset < 0)
+ deleteLater();
+ }
+ }
+}
+
+}
+
+#include "popupmessage.moc"
diff --git a/kftpgrabber/src/widgets/popupmessage.h b/kftpgrabber/src/widgets/popupmessage.h
new file mode 100644
index 0000000..0550d41
--- /dev/null
+++ b/kftpgrabber/src/widgets/popupmessage.h
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Max Howell <max.howell@methyblue.com>
+ * Copyright (C) 2005 Seb Ruiz <me@sebruiz.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETSPOPUPMESSAGE_H
+#define KFTPWIDGETSPOPUPMESSAGE_H
+
+#include "overlaywidget.h"
+
+#include <qbitmap.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+
+namespace KFTPWidgets {
+
+/**
+ * Widget that animates itself into a position relative to an anchor widget.
+ */
+class PopupMessage : public OverlayWidget {
+Q_OBJECT
+public:
+ /**
+ * Possible animation effects.
+ */
+ enum MaskEffect {
+ Plain,
+ Slide,
+ Dissolve
+ };
+
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ * @param anchor Which widget to tie the popup widget to
+ * @param timeout How long to wait before auto closing
+ */
+ PopupMessage(QWidget *parent, QWidget *anchor, int timeout = 5000);
+
+ void addWidget(QWidget *widget);
+ void setShowCloseButton(bool show);
+ void setShowCounter(bool show);
+ void setImage(const QString &location);
+ void setImage(const QPixmap &pixmap);
+ void setMaskEffect(MaskEffect type) { m_maskEffect = type; }
+ void setText(const QString &text);
+ void setTimeout(int timeout) { m_timeout = timeout; }
+public slots:
+ void close();
+ void display();
+protected:
+ void timerEvent(QTimerEvent *event);
+ void countDown();
+
+ void dissolveMask();
+ void plainMask();
+ void slideMask();
+private:
+ QVBoxLayout *m_layout;
+ QFrame *m_countdownFrame;
+ QWidget *m_anchor;
+ QWidget *m_parent;
+ QBitmap m_mask;
+ MaskEffect m_maskEffect;
+
+ int m_dissolveSize;
+ int m_dissolveDelta;
+
+ int m_offset;
+ int m_counter;
+ int m_stage;
+ int m_timeout;
+ int m_timerId;
+
+ bool m_showCounter;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/queueview/Makefile.am b/kftpgrabber/src/widgets/queueview/Makefile.am
new file mode 100644
index 0000000..5ba11d9
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I$(srcdir)/../.. \
+ -I$(srcdir)/../../engine \
+ -I$(srcdir)/../../misc \
+ -I$(srcdir)/../../ui -I../../ui\
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../browser \
+ $(all_includes)
+
+METASOURCES = AUTO
+noinst_LIBRARIES = libqueueviewwidget.a
+libqueueviewwidget_a_SOURCES = queueeditor.cpp queueview.cpp threadview.cpp
+
+noinst_HEADERS = queueeditor.h queueview.h threadview.h
diff --git a/kftpgrabber/src/widgets/queueview/queueeditor.cpp b/kftpgrabber/src/widgets/queueview/queueeditor.cpp
new file mode 100644
index 0000000..c565165
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/queueeditor.cpp
@@ -0,0 +1,323 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "queueeditor.h"
+#include "kftpserverlineedit.h"
+#include "kftpbookmarks.h"
+#include "kftpqueueeditorlayout.h"
+
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <kcombobox.h>
+#include <klocale.h>
+
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+
+#define REMOTE_PROTOCOL m_transfer->getSourceUrl().isLocalFile() ? m_transfer->getDestUrl().protocol() : m_transfer->getSourceUrl().protocol()
+
+namespace KFTPWidgets {
+
+QueueEditor::QueueEditor(QWidget *parent, const char *name)
+: KDialogBase(parent, name, true, "Edit queue", KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true)
+{
+ m_layout = new KFTPQueueEditorLayout(this);
+ setMainWidget(m_layout);
+
+ connect(m_layout->srcPath, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+ connect(m_layout->dstPath, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+
+ connect(m_layout->srcHost, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+ connect(m_layout->srcUser, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+ connect(m_layout->srcPass, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+
+ connect(m_layout->dstHost, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+ connect(m_layout->dstUser, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+ connect(m_layout->dstPass, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged()));
+
+ connect(m_layout->srcName, SIGNAL(siteChanged(KFTPBookmarks::Site*)), this, SLOT(slotSourceSiteChanged(KFTPBookmarks::Site*)));
+ connect(m_layout->dstName, SIGNAL(siteChanged(KFTPBookmarks::Site*)), this, SLOT(slotDestSiteChanged(KFTPBookmarks::Site*)));
+
+ connect(m_layout->transferType, SIGNAL(activated(int)), this, SLOT(slotTransferModeChanged(int)));
+
+ setMaximumHeight(250);
+ setInitialSize(QSize(500, 250));
+
+ enableButtonOK(false);
+}
+
+void QueueEditor::resetTabs()
+{
+ m_layout->serverTab->setTabEnabled(m_layout->tab, false);
+ m_layout->serverTab->setTabEnabled(m_layout->tab_2, false);
+}
+
+void QueueEditor::resetServerData()
+{
+ // Source
+ m_layout->srcName->clear();
+ m_layout->srcHost->setText("");
+ m_layout->srcPort->setValue(21);
+ m_layout->srcUser->setText("");
+ m_layout->srcPass->erase();
+
+ // Destination
+ m_layout->dstName->clear();
+ m_layout->dstHost->setText("");
+ m_layout->dstPort->setValue(21);
+ m_layout->dstUser->setText("");
+ m_layout->dstPass->erase();
+}
+
+void QueueEditor::slotTransferModeChanged(int index)
+{
+ if (m_lastTransferType == index)
+ return;
+ else
+ m_lastTransferType = (KFTPQueue::TransferType) index;
+
+ resetTabs();
+ resetServerData();
+
+ switch (index) {
+ case 0: {
+ // Download - source: remote dest: local
+ m_layout->serverTab->setTabEnabled(m_layout->tab, true);
+ m_layout->serverTab->showPage(m_layout->tab);
+ break;
+ }
+ case 1: {
+ // Upload - source: local dest: remote
+ m_layout->serverTab->setTabEnabled(m_layout->tab_2, true);
+ m_layout->serverTab->showPage(m_layout->tab_2);
+ break;
+ }
+ case 2: {
+ // FXP - source: remote dest: remote
+ m_layout->serverTab->setTabEnabled(m_layout->tab, true);
+ m_layout->serverTab->setTabEnabled(m_layout->tab_2, true);
+ m_layout->serverTab->showPage(m_layout->tab);
+ break;
+ }
+ }
+
+ slotTextChanged();
+}
+
+bool QueueEditor::sourceIsValid()
+{
+ if (m_lastTransferType == 1) return true;
+
+ if (m_layout->srcHost->text().stripWhiteSpace().isEmpty() || m_layout->srcUser->text().stripWhiteSpace().isEmpty())
+ return false;
+ else
+ return true;
+}
+
+bool QueueEditor::destIsValid()
+{
+ if (m_lastTransferType == 0) return true;
+
+ if (m_layout->dstHost->text().stripWhiteSpace().isEmpty() || m_layout->dstUser->text().stripWhiteSpace().isEmpty())
+ return false;
+ else
+ return true;
+}
+
+void QueueEditor::slotTextChanged()
+{
+ if (m_layout->srcPath->text().stripWhiteSpace().isEmpty() || m_layout->dstPath->text().stripWhiteSpace().isEmpty() ||
+ m_layout->srcPath->text().left(1) != "/" || m_layout->dstPath->text().left(1) != "/" ||
+ !sourceIsValid() || !destIsValid() )
+ enableButtonOK(false);
+ else
+ enableButtonOK(true);
+}
+
+void QueueEditor::setData(KFTPQueue::Transfer *transfer)
+{
+ KURL sUrl, dUrl;
+
+ m_layout->srcPath->setText(transfer->getSourceUrl().path());
+ m_layout->dstPath->setText(transfer->getDestUrl().path());
+
+ // Source
+ sUrl = transfer->getSourceUrl();
+
+ if (!sUrl.isLocalFile()) {
+ m_layout->srcName->setCurrentSite(KFTPBookmarks::Manager::self()->findSite(sUrl));
+ m_layout->srcHost->setText(sUrl.host());
+ m_layout->srcPort->setValue(sUrl.port());
+ m_layout->srcUser->setText(sUrl.user());
+
+ m_layout->srcPass->erase();
+ m_layout->srcPass->insert(sUrl.pass());
+ } else {
+ m_layout->serverTab->setTabEnabled(m_layout->tab, false);
+ }
+
+ // Destination
+ dUrl = transfer->getDestUrl();
+
+ if (!dUrl.isLocalFile()) {
+ m_layout->dstName->setCurrentSite(KFTPBookmarks::Manager::self()->findSite(dUrl));
+ m_layout->dstHost->setText(dUrl.host());
+ m_layout->dstPort->setValue(dUrl.port());
+ m_layout->dstUser->setText(dUrl.user());
+
+ m_layout->dstPass->erase();
+ m_layout->dstPass->insert(dUrl.pass());
+ } else {
+ m_layout->serverTab->setTabEnabled(m_layout->tab_2, false);
+ }
+
+ // Transfer type
+ m_lastTransferType = transfer->getTransferType();
+ m_layout->transferType->setCurrentItem(m_lastTransferType);
+
+ m_transfer = transfer;
+}
+
+void QueueEditor::saveData()
+{
+ KURL sUrl, dUrl;
+
+ if (m_lastTransferType != 1) {
+ sUrl.setProtocol(REMOTE_PROTOCOL);
+ sUrl.setHost(m_layout->srcHost->text());
+ sUrl.setPort(m_layout->srcPort->value());
+ sUrl.setUser(m_layout->srcUser->text());
+ sUrl.setPass(m_layout->srcPass->password());
+
+ if (m_transfer->getSourceUrl().pass().isEmpty() && sUrl.pass().isEmpty())
+ sUrl.setPass(QString::null);
+ } else {
+ sUrl.setProtocol("file");
+ }
+
+ sUrl.setPath(m_layout->srcPath->text());
+
+ if (m_lastTransferType != 0) {
+ dUrl.setProtocol(REMOTE_PROTOCOL);
+ dUrl.setHost(m_layout->dstHost->text());
+ dUrl.setPort(m_layout->dstPort->value());
+ dUrl.setUser(m_layout->dstUser->text());
+ dUrl.setPass(m_layout->dstPass->password());
+
+ if (m_transfer->getDestUrl().pass().isEmpty() && dUrl.pass().isEmpty())
+ dUrl.setPass(QString::null);
+ } else {
+ dUrl.setProtocol("file");
+ }
+
+ dUrl.setPath(m_layout->dstPath->text());
+
+ m_transfer->setSourceUrl(sUrl);
+ m_transfer->setDestUrl(dUrl);
+ m_transfer->setTransferType(m_lastTransferType);
+
+ // If the transfer is a directory, we have to update all child transfers
+ // as well.
+ if (m_transfer->isDir())
+ recursiveSaveData(static_cast<KFTPQueue::TransferDir*>(m_transfer), sUrl, dUrl);
+}
+
+void QueueEditor::recursiveSaveData(KFTPQueue::TransferDir *parent, const KURL &srcUrl, const KURL &dstUrl)
+{
+ KFTPQueue::QueueObject *o;
+ QPtrList<KFTPQueue::QueueObject> children = parent->getChildrenList();
+
+ KURL sUrl, dUrl;
+
+ for (o = children.first(); o; o = children.next()) {
+ KFTPQueue::Transfer *i = static_cast<KFTPQueue::Transfer*>(o);
+
+ // Modify the urls
+ sUrl = srcUrl;
+ dUrl = dstUrl;
+
+ sUrl.addPath(i->getSourceUrl().fileName());
+ dUrl.addPath(i->getDestUrl().fileName());
+
+ // Set the urls
+ i->setSourceUrl(sUrl);
+ i->setDestUrl(dUrl);
+ i->setTransferType(m_lastTransferType);
+ i->emitUpdate();
+
+ if (i->isDir())
+ recursiveSaveData(static_cast<KFTPQueue::TransferDir*>(i), sUrl, dUrl);
+ }
+}
+
+void QueueEditor::slotSourceSiteChanged(KFTPBookmarks::Site *site)
+{
+ if (site) {
+ m_layout->srcHost->setText(site->getProperty("host"));
+ m_layout->srcPort->setValue(site->getIntProperty("port"));
+ m_layout->srcUser->setText(site->getProperty("username"));
+ m_layout->srcPass->erase();
+ m_layout->srcPass->insert(site->getProperty("password"));
+ } else {
+ m_layout->srcHost->clear();
+ m_layout->srcPort->setValue(21);
+ m_layout->srcUser->clear();
+ m_layout->srcPass->erase();
+ }
+}
+
+void QueueEditor::slotDestSiteChanged(KFTPBookmarks::Site *site)
+{
+ if (site) {
+ m_layout->dstHost->setText(site->getProperty("host"));
+ m_layout->dstPort->setValue(site->getIntProperty("port"));
+ m_layout->dstUser->setText(site->getProperty("username"));
+ m_layout->dstPass->erase();
+ m_layout->dstPass->insert(site->getProperty("password"));
+ } else {
+ m_layout->dstHost->clear();
+ m_layout->dstPort->setValue(21);
+ m_layout->dstUser->clear();
+ m_layout->dstPass->erase();
+ }
+}
+
+}
+
+#include "queueeditor.moc"
+
diff --git a/kftpgrabber/src/widgets/queueview/queueeditor.h b/kftpgrabber/src/widgets/queueview/queueeditor.h
new file mode 100644
index 0000000..09027d6
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/queueeditor.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEEDITOR_H
+#define KFTPQUEUEEDITOR_H
+
+#include "kftpqueue.h"
+#include "kftpbookmarks.h"
+
+#include <kdialogbase.h>
+#include <qdom.h>
+
+class KFTPQueueEditorLayout;
+
+namespace KFTPWidgets {
+
+/**
+@author Jernej Kos
+*/
+class QueueEditor : public KDialogBase
+{
+Q_OBJECT
+public:
+ QueueEditor(QWidget *parent = 0, const char *name = 0);
+
+ void setData(KFTPQueue::Transfer *transfer);
+ void saveData();
+private:
+ KFTPQueueEditorLayout *m_layout;
+ KFTPQueue::Transfer *m_transfer;
+ KFTPQueue::TransferType m_lastTransferType;
+
+ void resetTabs();
+ void resetServerData();
+
+ bool sourceIsValid();
+ bool destIsValid();
+
+ void recursiveSaveData(KFTPQueue::TransferDir *parent, const KURL &srcUrl, const KURL &dstUrl);
+private slots:
+ void slotTextChanged();
+ void slotTransferModeChanged(int index);
+
+ void slotSourceSiteChanged(KFTPBookmarks::Site *site);
+ void slotDestSiteChanged(KFTPBookmarks::Site *site);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/queueview/queueview.cpp b/kftpgrabber/src/widgets/queueview/queueview.cpp
new file mode 100644
index 0000000..4fc8e5f
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/queueview.cpp
@@ -0,0 +1,888 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "misc.h"
+
+#include "listview.h"
+#include "queueview.h"
+#include "kftpqueue.h"
+#include "kftpapi.h"
+#include "queueeditor.h"
+#include "widgets/searchdialog.h"
+#include "misc/config.h"
+
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <kiconloader.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kpopupmenu.h>
+#include <klistviewsearchline.h>
+#include <kdebug.h>
+
+#include <qspinbox.h>
+#include <qtooltip.h>
+
+using namespace KFTPGrabberBase;
+using namespace KFTPCore;
+
+namespace KFTPWidgets {
+
+QueueViewItem::QueueViewItem(QueueView *view, KFTPQueue::QueueObject *object, QListView *parent)
+ : QListViewItem(parent),
+ m_lastChild(0),
+ m_queueObject(object),
+ m_queueView(view),
+ m_queueId(object->getId())
+{
+ init();
+}
+
+QueueViewItem::QueueViewItem(QueueView *view, KFTPQueue::QueueObject *object, QListViewItem *parent)
+ : QListViewItem(parent, static_cast<QueueViewItem*>(parent)->lastChild()),
+ m_lastChild(0),
+ m_queueObject(object),
+ m_queueView(view),
+ m_queueId(object->getId())
+{
+ init();
+}
+
+QueueViewItem::~QueueViewItem()
+{
+ m_queueView->m_queuedItems.remove(m_queueId);
+}
+
+void QueueViewItem::insertItem(QListViewItem *newChild)
+{
+ QListViewItem::insertItem(newChild);
+ m_lastChild = newChild;
+}
+
+void QueueViewItem::takeItem(QListViewItem *item)
+{
+ if (item == m_lastChild) {
+ QListViewItem *above = item->itemAbove();
+
+ if (above->parent() == m_lastChild->parent())
+ m_lastChild = above;
+ else
+ m_lastChild = 0;
+ }
+
+ QListViewItem::takeItem(item);
+}
+
+void QueueViewItem::moveUp()
+{
+ QListViewItem *above = itemAbove();
+
+ if (above && above->parent() == QListViewItem::parent()) {
+ QListViewItem *previous = above->itemAbove();
+
+ if (previous && previous->parent() == QListViewItem::parent()) {
+ QueueViewItem *parent = static_cast<QueueViewItem*>(QListViewItem::parent());
+ moveItem(previous);
+
+ if (parent && this == parent->lastChild())
+ parent->m_lastChild = above;
+ } else {
+ moveToTop();
+ }
+ }
+}
+
+void QueueViewItem::moveDown()
+{
+ QueueViewItem *parent = static_cast<QueueViewItem*>(QListViewItem::parent());
+ QueueViewItem *next = static_cast<QueueViewItem*>(nextSibling());
+
+ if (next) {
+ moveItem(next);
+
+ if (parent && parent->lastChild() == next)
+ parent->m_lastChild = this;
+ }
+}
+
+void QueueViewItem::moveToTop()
+{
+ QueueViewItem *parent = static_cast<QueueViewItem*>(QListViewItem::parent());
+
+ // Just reinsert the item
+ if (parent) {
+ if (this == parent->lastChild())
+ parent->m_lastChild = itemAbove();
+
+ parent->QListViewItem::takeItem(this);
+ parent->QListViewItem::insertItem(this);
+ } else {
+ ListView *view = m_queueView->m_queue;
+ view->QListView::takeItem(this);
+ view->QListView::insertItem(this);
+ }
+}
+
+void QueueViewItem::moveToBottom()
+{
+ QueueViewItem *parent = static_cast<QueueViewItem*>(QListViewItem::parent());
+
+ // Just reinsert the item
+ if (parent) {
+ QListViewItem *last = parent->lastChild();
+
+ parent->takeItem(this);
+ parent->insertItem(this);
+ moveItem(last);
+ } else {
+ ListView *view = m_queueView->m_queue;
+ view->takeItem(this);
+ view->insertItem(this);
+ }
+}
+
+void QueueViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ QColorGroup _cg(cg);
+ QColor c = _cg.text();
+
+ QColor n_color;
+
+ if (m_queueObject && m_queueObject->isTransfer()) {
+ switch (m_queueObject->getStatus()) {
+ case KFTPQueue::Transfer::Running: n_color.setRgb(255, 0, 0); break;
+ case KFTPQueue::Transfer::Connecting: n_color.setRgb(0, 0, 255); break;
+ case KFTPQueue::Transfer::Waiting: n_color.setRgb(0, 0, 255); break;
+ case KFTPQueue::Transfer::Locked: n_color.setRgb(0, 150, 0); break;
+ default: break;
+ }
+ }
+
+ if (n_color.isValid())
+ _cg.setColor(QColorGroup::Text, n_color);
+
+ QListViewItem::paintCell(p, _cg, column, width, alignment);
+ _cg.setColor(QColorGroup::Text, c);
+}
+
+void QueueViewItem::init()
+{
+ if (m_queueObject->isTransfer()) {
+ KFTPQueue::Transfer *transfer = static_cast<KFTPQueue::Transfer*>(getObject());
+
+ setText(0, transfer->getSourceUrl().fileName());
+ setText(1, KIO::convertSize(transfer->getActualSize()));
+ setText(2, transfer->getSourceUrl().pathOrURL());
+ setText(3, transfer->getDestUrl().pathOrURL());
+
+ // Icon
+ QString iconText;
+ if (transfer->isDir()) {
+ iconText = "folder";
+ } else {
+ KMimeType::Ptr theType = KMimeType::findByURL("/" + transfer->getSourceUrl().path(), 0, false, true);
+ iconText = theType->icon(QString::null, false);
+ }
+
+ setPixmap(0, loadSmallPixmap(iconText));
+ } else if (m_queueObject->getType() == KFTPQueue::QueueObject::Site) {
+ KFTPQueue::Site *site = static_cast<KFTPQueue::Site*>(getObject());
+
+ setText(0, QString("%1:%2").arg(site->getUrl().host()).arg(site->getUrl().port()));
+ setText(1, KIO::convertSize(site->getActualSize()));
+
+ // Set the pixmap
+ setPixmap(0, loadSmallPixmap("server"));
+ }
+}
+
+void QueueViewItem::refresh()
+{
+ if (!m_queueObject)
+ return;
+
+ if (m_queueObject->isTransfer()) {
+ KFTPQueue::Transfer *transfer = static_cast<KFTPQueue::Transfer*>(getObject());
+
+ // Speed
+ bool noSpeed = transfer->getStatus() == KFTPQueue::Transfer::Waiting || transfer->getStatus() == KFTPQueue::Transfer::Connecting;
+ QString speed;
+ if ((!transfer->isDir() || !isOpen()) && transfer->isRunning() && !noSpeed) {
+ speed.sprintf( "%lld B/s", (transfer->getSpeed()) );
+
+ if (transfer->getSpeed() > 1024)
+ speed.sprintf( "%lld KB/s", (transfer->getSpeed() / 1024) );
+
+ if (transfer->getSpeed() > 1024*1024)
+ speed.sprintf("%lld MB/s", (transfer->getSpeed() / 1024) / 1024);
+
+ if (transfer->getSpeed() == 0 && transfer->getTransferType() != KFTPQueue::FXP)
+ speed = i18n("stalled");
+
+ if (transfer->getSpeed() == 0 && transfer->getTransferType() == KFTPQueue::FXP)
+ speed = i18n("running");
+ }
+
+ // ETA
+ QString eta;
+
+ if (transfer->isRunning() && transfer->getSpeed() > 0) {
+ eta = KIO::convertSeconds(KIO::calculateRemainingSeconds(transfer->getSize(),
+ transfer->getCompleted(),
+ transfer->getSpeed()));
+ } else {
+ eta = QString::null;
+ }
+
+ // Set the columns
+ setText(0, transfer->getSourceUrl().fileName());
+ setText(1, KIO::convertSize(transfer->getActualSize()));
+ setText(2, transfer->getSourceUrl().pathOrURL());
+ setText(3, transfer->getDestUrl().pathOrURL());
+ setText(5, speed);
+ setText(6, eta);
+
+ // Don't show the file:// for local src/dest
+ if (transfer->getSourceUrl().isLocalFile()) {
+ setText(2, transfer->getSourceUrl().path());
+ } else if (transfer->getDestUrl().isLocalFile()) {
+ setText(3, transfer->getDestUrl().path());
+ }
+
+ // Progress
+ int progress;
+ int r_progress;
+
+ if (transfer->getSize() == 0)
+ progress = 0;
+ else
+ progress = transfer->getCompleted()*100/transfer->getSize();
+
+ if (transfer->getResumed() == 0)
+ r_progress = 0;
+ else
+ r_progress = transfer->getResumed()*100/transfer->getSize();
+
+ if (transfer->getStatus() == KFTPQueue::Transfer::Waiting) {
+ // Transfer is waiting for a free connection
+ setText(4, i18n("Waiting for connection..."));
+ setPixmap(4, NULL);
+ } else if (transfer->getStatus() == KFTPQueue::Transfer::Connecting) {
+ // Transfer is not yet connected,
+ setText(4, i18n("Connecting..."));
+ setPixmap(4, NULL);
+ } else if (progress > 0) {
+ setPixmap(4, createProgressPixmap(progress, r_progress));
+
+ QString progressText;
+ progressText.sprintf("%d %%", progress);
+ setText(4, progressText);
+ } else {
+ setPixmap(4, NULL);
+ setText(4, QString::null);
+ }
+
+ // Icon
+ QString iconText;
+ if (transfer->isDir()) {
+ iconText = "folder";
+ } else {
+ KMimeType::Ptr theType = KMimeType::findByURL("/" + transfer->getSourceUrl().path(), 0, false, true);
+ iconText = theType->icon(QString::null, false);
+ }
+
+ setPixmap(0, loadSmallPixmap(iconText));
+ } else if (m_queueObject->getType() == KFTPQueue::QueueObject::Site) {
+ KFTPQueue::Site *site = static_cast<KFTPQueue::Site*>(getObject());
+
+ // Speed
+ QString speed;
+ speed.sprintf( "%lld B/s", (site->getSpeed()) );
+
+ if (site->getSpeed() > 1024)
+ speed.sprintf( "%lld KB/s", (site->getSpeed() / 1024) );
+
+ if (site->getSpeed() > 1024*1024)
+ speed.sprintf("%lld MB/s", (site->getSpeed() / 1024) / 1024);
+
+ if (site->getSpeed() == 0)
+ speed = QString::null;
+
+ // ETA
+ QString eta;
+
+ if (site->isRunning() && site->getSpeed() > 0) {
+ eta = KIO::convertSeconds(KIO::calculateRemainingSeconds(site->getSize(),
+ site->getCompleted(),
+ site->getSpeed()));
+ } else {
+ eta = "";
+ }
+
+ // Progress
+ if (site->isRunning()) {
+ int progress = 0;
+
+ if (site->getSize() > 0)
+ progress = site->getCompleted()*100/site->getSize();
+
+ setPixmap(4, createProgressPixmap(progress, 0));
+
+ QString progressText;
+ progressText.sprintf("%d %%", progress);
+ setText(4, progressText);
+ } else {
+ setPixmap(4, NULL);
+ setText(4, QString::null);
+ }
+
+ // Set the columns
+ setText(0, QString("%1:%2").arg(site->getUrl().host()).arg(site->getUrl().port()));
+ setText(1, KIO::convertSize(site->getActualSize()));
+ setText(5, speed);
+ setText(6, eta);
+
+ // Set the pixmap
+ setPixmap(0, loadSmallPixmap("server"));
+ }
+}
+
+QueueListView::QueueListView(QWidget *parent)
+ : ListView(parent)
+{
+}
+
+void QueueListView::insertItem(QListViewItem *item)
+{
+ QListViewItem *last = lastChild();
+ QListView::insertItem(item);
+
+ if (last)
+ item->moveItem(last);
+}
+
+QueueView::QueueView(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ m_toolBar = new KToolBar(this, "queueToolBar");
+ m_toolBar->setIconSize(16);
+ layout->addWidget(m_toolBar);
+
+ m_searchToolBar = new KToolBar(this, "searchToolBar");
+ m_searchToolBar->setEnableContextMenu(false);
+ m_searchToolBar->setMovingEnabled(false);
+ m_searchToolBar->setFullSize(true);
+
+ // Create the erase button
+ m_searchToolBar->insertButton(QApplication::reverseLayout() ? "clear_left" :"locationbar_erase", 0, SIGNAL(clicked()), this, SLOT(slotSearchEraseClicked()), true);
+
+ // Create the labels
+ QLabel *searchLabel = new QLabel(i18n("Filter: "), m_searchToolBar);
+ m_searchToolBar->insertWidget(1, 35, searchLabel);
+
+ // Create the list view
+ m_queue = new QueueListView(this);
+
+ // Create the search field
+ m_searchField = new KListViewSearchLine(m_searchToolBar, m_queue);
+
+ // Do some more stuff
+ m_searchToolBar->setItemAutoSized(1, true);
+ m_searchToolBar->setStretchableWidget(m_searchField);
+ m_searchToolBar->updateRects(true);
+ m_searchToolBar->hide();
+
+ layout->addWidget(m_searchToolBar);
+
+ // Create the columns
+ m_queue->addColumn(i18n("Name"), 150);
+ m_queue->addColumn(i18n("Size"), 75);
+ m_queue->addColumn(i18n("Source"), 250);
+ m_queue->addColumn(i18n("Destination"), 250);
+ m_queue->addColumn(i18n("Progress"), 140);
+ m_queue->addColumn(i18n("Speed"), 70);
+ m_queue->addColumn(i18n("ETA"), 80);
+
+ // Text when there is nothing queued
+ m_queue->setEmptyListText(i18n("You do not have any files in the queue."));
+
+ // Multi-select
+ m_queue->setSelectionModeExt(KListView::FileManager);
+ m_queue->setAllColumnsShowFocus(true);
+ m_queue->setRootIsDecorated(true);
+ m_queue->QListView::setSorting(-1);
+ m_queue->QListView::setSortColumn(-1);
+
+ layout->addWidget(m_queue);
+
+ // The signals
+ connect(m_queue, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), this,
+ SLOT(contextMenuRequested(KListView*, QListViewItem*, const QPoint&)));
+ connect(m_queue, SIGNAL(selectionChanged()), this, SLOT(updateActions()));
+
+ // Let us be up-to-date
+ connect(KFTPQueue::Manager::self(), SIGNAL(transferRemoved(long)), this, SLOT(slotObjectRemoved(long)));
+ connect(KFTPQueue::Manager::self(), SIGNAL(siteRemoved(long)), this, SLOT(slotObjectRemoved(long)));
+ connect(KFTPQueue::Manager::self(), SIGNAL(newTransfer(KFTPQueue::Transfer*)), this, SLOT(slotTransferAdded(KFTPQueue::Transfer*)));
+ connect(KFTPQueue::Manager::self(), SIGNAL(newSite(KFTPQueue::Site*)), this, SLOT(slotSiteAdded(KFTPQueue::Site*)));
+ connect(KFTPQueue::Manager::self(), SIGNAL(queueUpdate()), this, SLOT(updateActions()));
+
+ // Load the listview layout
+ loadLayout();
+
+ // Create the context menu actions
+ initActions();
+ initToolBar();
+ updateActions();
+
+ setMinimumHeight(150);
+}
+
+void QueueView::saveLayout()
+{
+ m_queue->saveLayout(kapp->config(), "queueViewLayout");
+}
+
+void QueueView::loadLayout()
+{
+ m_queue->restoreLayout(kapp->config(), "queueViewLayout");
+}
+
+void QueueView::initToolBar()
+{
+ // Plug all actions
+ m_loadAction->plug(m_toolBar);
+ m_saveAction->plug(m_toolBar);
+ m_toolBar->insertSeparator();
+ m_startAction->plug(m_toolBar);
+ m_pauseAction->plug(m_toolBar);
+ m_stopAction->plug(m_toolBar);
+ m_toolBar->insertSeparator();
+ m_addAction->plug(m_toolBar);
+ m_removeAction->plug(m_toolBar);
+ m_searchAction->plug(m_toolBar);
+ m_toolBar->insertSeparator();
+ m_filterAction->plug(m_toolBar);
+
+ // Create speed control widgets
+ m_toolBar->insertSeparator();
+
+ QSpinBox *downloadSpeed = new QSpinBox(0, 10240, 1, m_toolBar);
+ QToolTip::add(downloadSpeed, i18n("Limit download transfer speed"));
+ m_toolBar->insertWidget(1, 35, new QLabel(i18n("Down: "), m_toolBar));
+ m_toolBar->insertWidget(2, 35, downloadSpeed);
+ downloadSpeed->setValue(Config::downloadSpeedLimit());
+ connect(downloadSpeed, SIGNAL(valueChanged(int)), this, SLOT(slotDownloadLimitChanged(int)));
+
+ m_toolBar->insertSeparator();
+
+ QSpinBox *uploadSpeed = new QSpinBox(0, 10240, 1, m_toolBar);
+ QToolTip::add(uploadSpeed, i18n("Limit upload transfer speed"));
+ m_toolBar->insertWidget(3, 35, new QLabel(i18n("Up: "), m_toolBar));
+ m_toolBar->insertWidget(4, 35, uploadSpeed);
+ uploadSpeed->setValue(Config::uploadSpeedLimit());
+ connect(uploadSpeed, SIGNAL(valueChanged(int)), this, SLOT(slotUploadLimitChanged(int)));
+
+ // Create thread count control widget
+ m_toolBar->insertSeparator();
+
+ QSpinBox *threadCount = new QSpinBox(1, 10, 1, m_toolBar);
+ QToolTip::add(threadCount, i18n("Per-session transfer thread count"));
+ m_toolBar->insertWidget(5, 35, new QLabel(i18n("Threads: "), m_toolBar));
+ m_toolBar->insertWidget(6, 35, threadCount);
+ threadCount->setValue(Config::threadCount());
+ connect(threadCount, SIGNAL(valueChanged(int)), this, SLOT(slotThreadCountChanged(int)));
+}
+
+void QueueView::slotDownloadLimitChanged(int value)
+{
+ Config::setDownloadSpeedLimit(value);
+ Config::self()->emitChange();
+}
+
+void QueueView::slotUploadLimitChanged(int value)
+{
+ Config::setUploadSpeedLimit(value);
+ Config::self()->emitChange();
+}
+
+void QueueView::slotThreadCountChanged(int value)
+{
+ Config::setThreadCount(value);
+ Config::self()->emitChange();
+}
+
+void QueueView::initActions()
+{
+ m_actionCollection = new KActionCollection(this, this);
+
+ // Create all the actions
+ m_launchAction = new KAction(i18n("&Start Transfer"), "launch", KShortcut(), this, SLOT(slotLaunch()), m_actionCollection, "launch");
+ m_abortAction = new KAction(i18n("&Abort Transfer"), KShortcut(), this, SLOT(slotAbort()), m_actionCollection, "abort");
+ m_removeAction = new KAction(i18n("&Remove"), "editdelete", KShortcut(Qt::Key_Delete), this, SLOT(slotRemove()), m_actionCollection, "remove");
+ m_removeAllAction = new KAction(i18n("Remove &All"), KShortcut(), this, SLOT(slotRemoveAll()), m_actionCollection, "removeAll");
+ m_moveUpAction = new KAction(i18n("Move &Up"), "up", KShortcut(), this, SLOT(slotMoveUp()), m_actionCollection, "moveUp");
+ m_moveDownAction = new KAction(i18n("Move &Down"), "down", KShortcut("del"), this, SLOT(slotMoveDown()), m_actionCollection, "moveDown");
+ m_moveTopAction = new KAction(i18n("Move To &Top"), "top", KShortcut(), this, SLOT(slotMoveTop()), m_actionCollection, "moveTop");
+ m_moveBottomAction = new KAction(i18n("Move To &Bottom"), "bottom", KShortcut(), this, SLOT(slotMoveBottom()), m_actionCollection, "moveBottom");
+ m_editAction = new KAction(i18n("&Change Transfer Info"), KShortcut(), this, SLOT(slotEdit()), m_actionCollection, "changeTransfer");
+
+ // Create the toolbar actions
+ m_loadAction = new KAction(i18n("&Load Queue From File"), "fileopen", KShortcut(), this, SLOT(slotLoad()), m_actionCollection, "load");
+ m_saveAction = new KAction(i18n("&Save Queue to File"), "filesaveas", KShortcut(), this, SLOT(slotSave()), m_actionCollection, "save");
+ m_startAction = new KAction(i18n("S&tart"), "player_play", KShortcut(), this, SLOT(slotStart()), m_actionCollection, "start");
+ m_pauseAction = new KAction(i18n("&Pause"), "player_pause", KShortcut(), this, SLOT(slotPause()), m_actionCollection, "pause");
+ m_stopAction = new KAction(i18n("St&op"), "player_stop", KShortcut(), this, SLOT(slotStop()), m_actionCollection, "stop");
+ m_addAction = new KAction(i18n("&Add Transfer..."), "filenew", KShortcut(), this, SLOT(slotAdd()), m_actionCollection, "add");
+ m_searchAction = new KAction(i18n("&Search && Replace..."), "find", KShortcut(), this, SLOT(slotSearch()), m_actionCollection, "search");
+ m_filterAction = new KToggleAction(i18n("Show &Filter"), "filter", KShortcut(), this, SLOT(slotFilter()), m_actionCollection, "filter");
+
+ m_saveAction->setEnabled( false );
+ m_startAction->setEnabled(false);
+ m_pauseAction->setEnabled(false);
+ m_stopAction->setEnabled(false);
+ m_addAction->setEnabled(false);
+ m_removeAction->setEnabled(false);
+ m_searchAction->setEnabled(false);
+ m_filterAction->setEnabled(true);
+}
+
+void QueueView::updateActions()
+{
+ m_startAction->setEnabled(!KFTPQueue::Manager::self()->isProcessing() && KFTPQueue::Manager::self()->topLevelObject()->hasChildren() && !KFTPQueue::Manager::self()->getNumRunning());
+ m_stopAction->setEnabled(KFTPQueue::Manager::self()->isProcessing());
+ m_removeAllAction->setEnabled(!KFTPQueue::Manager::self()->isProcessing());
+
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+ QueueViewItem *firstItem = static_cast<QueueViewItem*>(selection.first());
+
+ m_removeAction->setEnabled((bool) firstItem);
+
+ if (!firstItem || !firstItem->getObject())
+ return;
+
+ bool locked = firstItem->getObject()->isLocked();
+ bool parentRunning = false;
+
+ if (firstItem->getObject()->hasParentObject())
+ parentRunning = firstItem->getObject()->parentObject()->isRunning();
+
+ m_launchAction->setEnabled(!firstItem->getObject()->isRunning() && !KFTPQueue::Manager::self()->isProcessing() && !locked);
+ m_abortAction->setEnabled(firstItem->getObject()->isRunning() && !KFTPQueue::Manager::self()->isProcessing());
+ m_removeAction->setEnabled(!firstItem->getObject()->isRunning() && !KFTPQueue::Manager::self()->isProcessing() && !locked);
+ m_editAction->setEnabled(!firstItem->getObject()->isRunning() && firstItem->getObject()->parentObject()->getType() == KFTPQueue::QueueObject::Site && !locked);
+
+ // Only allow moving of multi selections if they have the same parent
+ bool allowMove = true;
+ for (QListViewItem *i = selection.first(); i; i = selection.next()) {
+ if (i->parent() != static_cast<QListViewItem*>(firstItem)->parent()) {
+ allowMove = false;
+ break;
+ }
+ }
+
+ m_moveUpAction->setEnabled(allowMove && KFTPQueue::Manager::self()->canBeMovedUp(firstItem->getObject()) && !locked);
+ m_moveDownAction->setEnabled(allowMove && KFTPQueue::Manager::self()->canBeMovedDown(static_cast<QueueViewItem*>(selection.last())->getObject()) && !locked);
+
+ m_moveTopAction->setEnabled(allowMove && KFTPQueue::Manager::self()->canBeMovedUp(firstItem->getObject()) && !locked);
+ m_moveBottomAction->setEnabled(allowMove && KFTPQueue::Manager::self()->canBeMovedDown(static_cast<QueueViewItem*>(selection.last())->getObject()) && !locked);
+}
+
+void QueueView::slotSiteAdded(KFTPQueue::Site *site)
+{
+ // The site should be inserted top-level
+ m_queuedItems.insert(site->getId(), new QueueViewItem(this, site, m_queue));
+ connect(site, SIGNAL(objectUpdated()), this, SLOT(slotObjectUpdated()));
+}
+
+void QueueView::slotTransferAdded(KFTPQueue::Transfer *transfer)
+{
+ // This transfer should be inserted under some other transfer
+ QueueViewItem *parent = m_queuedItems.find(transfer->parentObject()->getId());
+
+ if (parent) {
+ m_queuedItems.insert(transfer->getId(), new QueueViewItem(this, transfer, parent));
+ connect(transfer, SIGNAL(objectUpdated()), this, SLOT(slotObjectUpdated()));
+ }
+
+ // Update actions
+ m_saveAction->setEnabled(true);
+ m_removeAllAction->setEnabled(true);
+ m_searchAction->setEnabled(true);
+}
+
+void QueueView::slotObjectRemoved(long id)
+{
+ // Delete the transfer
+ QueueViewItem *item = m_queuedItems.find(id);
+
+ if (item)
+ delete item;
+
+ // Update actions
+ bool empty = (m_queue->childCount() == 0);
+
+ m_saveAction->setEnabled(!empty);
+ if (empty) m_removeAction->setEnabled(false);
+ m_removeAllAction->setEnabled(!empty);
+ m_searchAction->setEnabled(!empty);
+}
+
+void QueueView::slotObjectUpdated()
+{
+ KFTPQueue::QueueObject *object = (KFTPQueue::QueueObject*) QObject::sender();
+
+ if (object) {
+ QueueViewItem *item = m_queuedItems.find(object->getId());
+
+ if (item)
+ item->refresh();
+ }
+}
+
+void QueueView::contextMenuRequested(KListView*, QListViewItem* item, const QPoint& p)
+{
+ if (!item)
+ return;
+
+ QueueViewItem *firstItem = static_cast<QueueViewItem*>(m_queue->selectedItems().first());
+ KPopupMenu *contextMenu = new KPopupMenu(m_queue);
+
+ // Populate context menu
+ if (firstItem->getObject()->isTransfer()) {
+ contextMenu->insertTitle(item->text(0) + ((m_queue->selectedItems().count() > 1) ? "..." : "" ));
+ m_launchAction->plug(contextMenu);
+ m_abortAction->plug(contextMenu);
+ contextMenu->insertSeparator();
+ m_removeAction->plug(contextMenu);
+ m_removeAllAction->plug(contextMenu);
+ contextMenu->insertSeparator();
+ m_moveTopAction->plug(contextMenu);
+ m_moveUpAction->plug(contextMenu);
+ m_moveDownAction->plug(contextMenu);
+ m_moveBottomAction->plug(contextMenu);
+ contextMenu->insertSeparator();
+ m_editAction->plug(contextMenu);
+ } else if (firstItem->getObject()->getType() == KFTPQueue::QueueObject::Site) {
+ contextMenu->insertTitle(i18n("Site"));
+ m_launchAction->plug(contextMenu);
+ m_abortAction->plug(contextMenu);
+ contextMenu->insertSeparator();
+ m_moveUpAction->plug(contextMenu);
+ m_moveDownAction->plug(contextMenu);
+ }
+
+ // Update the actions
+ updateActions();
+
+ // Show the context menu
+ contextMenu->exec(p);
+}
+
+void QueueView::slotLaunch()
+{
+ // Reset a possible preconfigured default action
+ KFTPQueue::Manager::self()->setDefaultFileExistsAction();
+
+ static_cast<QueueViewItem*>(m_queue->selectedItems().first())->getObject()->execute();
+}
+
+void QueueView::slotAbort()
+{
+ static_cast<QueueViewItem*>(m_queue->selectedItems().first())->getObject()->abort();
+}
+
+void QueueView::slotRemove()
+{
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove queued file(s)?")) == KMessageBox::Yes) {
+ KFTPQueue::Manager::self()->setEmitUpdate(false);
+
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+ for (QListViewItem *item = selection.first(); item; item = selection.next()) {
+ if (item && static_cast<QueueViewItem*>(item)->getObject())
+ KFTPQueue::Manager::self()->removeTransfer(static_cast<KFTPQueue::Transfer*>(static_cast<QueueViewItem*>(item)->getObject()));
+ }
+
+ KFTPQueue::Manager::self()->setEmitUpdate(true);
+ KFTPQueue::Manager::self()->doEmitUpdate();
+ }
+}
+
+void QueueView::slotRemoveAll()
+{
+ if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove ALL queued files?")) == KMessageBox::Yes) {
+ KFTPQueue::Manager::self()->clearQueue();
+ }
+}
+
+void QueueView::slotMoveUp()
+{
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+
+ for (QListViewItem *item = selection.first(); item; item = selection.next()) {
+ QueueViewItem *queueItem = static_cast<QueueViewItem*>(item);
+
+ // Move the transfer
+ KFTPQueue::Manager::self()->moveTransferUp(queueItem->getObject());
+ queueItem->moveUp();
+ }
+}
+
+void QueueView::slotMoveDown()
+{
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+
+ for (QListViewItem *item = selection.last(); item; item = selection.prev()) {
+ QueueViewItem *queueItem = static_cast<QueueViewItem*>(item);
+
+ // Move the transfer
+ KFTPQueue::Manager::self()->moveTransferDown(queueItem->getObject());
+ queueItem->moveDown();
+ }
+}
+
+void QueueView::slotMoveTop()
+{
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+
+ for (QListViewItem *item = selection.last(); item; item = selection.prev()) {
+ QueueViewItem *queueItem = static_cast<QueueViewItem*>(item);
+
+ // Move the transfer
+ KFTPQueue::Manager::self()->moveTransferTop(queueItem->getObject());
+ queueItem->moveToTop();
+ }
+}
+
+void QueueView::slotMoveBottom()
+{
+ QPtrList<QListViewItem> selection = m_queue->selectedItems();
+
+ for (QListViewItem *item = selection.first(); item; item = selection.next()) {
+ QueueViewItem *queueItem = static_cast<QueueViewItem*>(item);
+
+ // Move the transfer
+ KFTPQueue::Manager::self()->moveTransferBottom(queueItem->getObject());
+ queueItem->moveToBottom();
+ }
+}
+
+void QueueView::slotEdit()
+{
+ QueueEditor *editor = new QueueEditor(this);
+
+ QueueViewItem* item = static_cast<QueueViewItem*>(m_queue->selectedItems().first());
+ editor->setData(static_cast<KFTPQueue::Transfer*>(item->getObject()));
+
+ // Show the queue editor
+ if (editor->exec() == QDialog::Accepted) {
+ editor->saveData();
+
+ KFTPQueue::Manager::self()->revalidateTransfer(static_cast<KFTPQueue::Transfer*>(item->getObject()));
+ KFTPQueue::Manager::self()->doEmitUpdate();
+ item->refresh();
+ }
+}
+
+void QueueView::slotSearch()
+{
+ SearchDialog *dialog = new SearchDialog();
+
+ dialog->exec();
+ delete dialog;
+}
+
+void QueueView::slotLoad()
+{
+ if (m_queue->childCount() && KMessageBox::warningContinueCancel(0L, i18n("Loading a new queue will overwrite the existing one; are you sure you want to continue?"), i18n("Load Queue")) == KMessageBox::Cancel)
+ return;
+
+ QString loadPath = KFileDialog::getOpenFileName();
+
+ if (!loadPath.isEmpty()) {
+ KFTPQueue::Manager::self()->getConverter()->importQueue(loadPath);
+ }
+}
+
+void QueueView::slotSave()
+{
+ QString savePath = KFileDialog::getSaveFileName();
+
+ if (!savePath.isEmpty()) {
+ KFTPQueue::Manager::self()->getConverter()->exportQueue(savePath);
+ }
+}
+
+void QueueView::slotStart()
+{
+ // Begin queue processing
+ KFTPQueue::Manager::self()->start();
+}
+
+void QueueView::slotPause()
+{
+}
+
+void QueueView::slotStop()
+{
+ // Abort queue processing
+ KFTPQueue::Manager::self()->abort();
+}
+
+void QueueView::slotAdd()
+{
+}
+
+void QueueView::slotSearchEraseClicked()
+{
+ m_searchField->clear();
+}
+
+void QueueView::slotFilter()
+{
+ if (m_filterAction->isChecked())
+ m_searchToolBar->show();
+ else
+ m_searchToolBar->hide();
+}
+
+}
+
+#include "queueview.moc"
diff --git a/kftpgrabber/src/widgets/queueview/queueview.h b/kftpgrabber/src/widgets/queueview/queueview.h
new file mode 100644
index 0000000..d93e14f
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/queueview.h
@@ -0,0 +1,296 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2005 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPQUEUEVIEW_H
+#define KFTPQUEUEVIEW_H
+
+#include <qguardedptr.h>
+#include <qintdict.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <kaction.h>
+#include <klistview.h>
+
+#include "kftpqueue.h"
+
+class KToolBar;
+class KListViewSearchLine;
+
+namespace KFTPWidgets {
+
+class ListView;
+class QueueView;
+
+/**
+ * A visual representation of a queued object.
+ */
+class QueueViewItem : public QListViewItem
+{
+public:
+ /**
+ * Class constructor.
+ *
+ * @param view Queue view widget
+ * @param object Queue object
+ * @param parent Parent list view
+ */
+ QueueViewItem(QueueView *view, KFTPQueue::QueueObject *object, QListView *parent);
+
+ /**
+ * Class constructor.
+ *
+ * @param view Queue view widget
+ * @param object Queue object
+ * @param parent Parent item
+ */
+ QueueViewItem(QueueView *view, KFTPQueue::QueueObject *object, QListViewItem *parent);
+
+ /**
+ * Class destructor.
+ */
+ ~QueueViewItem();
+
+ /**
+ * Refresh the visual representation with data from the actual queue object.
+ */
+ void refresh();
+
+ /**
+ * Associate a queue object with this item.
+ *
+ * @param object A valid object pointer
+ */
+ void setObject(KFTPQueue::QueueObject *object) { m_queueObject = object; }
+
+ /**
+ * Returns the currently associated queue object.
+ */
+ KFTPQueue::QueueObject *getObject() const { return m_queueObject; }
+
+ /**
+ * Returns the last child item.
+ */
+ QListViewItem *lastChild() const { return m_lastChild; }
+
+ /**
+ * Moves this item one position up.
+ */
+ void moveUp();
+
+ /**
+ * Moves this item one position down.
+ */
+ void moveDown();
+
+ /**
+ * Moves this item to parent's top.
+ */
+ void moveToTop();
+
+ /**
+ * Moves this item to parent's bottom.
+ */
+ void moveToBottom();
+
+ /**
+ * @overload
+ * Reimplemented from QListViewItem.
+ */
+ void insertItem(QListViewItem *newChild);
+
+ /**
+ * @overload
+ * Reimplemented from QListViewItem.
+ */
+ void takeItem(QListViewItem *item);
+
+ /**
+ * @overload
+ * Reimplemented from QListViewItem for text colors.
+ */
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+
+ /**
+ * @overload
+ * Reimplemented from QListViewItem.
+ */
+ void sortChildItems(int, bool) {}
+protected:
+ /**
+ * Init the item.
+ */
+ void init();
+private:
+ QListViewItem *m_lastChild;
+
+ KFTPQueue::QueueObject *m_queueObject;
+ QueueView *m_queueView;
+ long m_queueId;
+};
+
+/**
+ * The list view widget.
+ *
+ * @author Jernej Kos
+ */
+class QueueListView : public ListView {
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ */
+ QueueListView(QWidget *parent);
+
+ /**
+ * @overload
+ * Reimplemented from QListView.
+ */
+ void insertItem(QListViewItem *item);
+
+ /**
+ * @overload
+ * Reimplemented from QListView.
+ */
+ void setSorting(int, bool = true) { QListView::setSorting(-1); }
+};
+
+/**
+ * A widget for displaying and manipulating the current queue.
+ *
+ * @author Jernej Kos
+ */
+class QueueView : public QWidget
+{
+friend class QueueViewItem;
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ */
+ QueueView(QWidget *parent, const char *name);
+
+ /**
+ * Load queue list layout from the configuration file.
+ */
+ void loadLayout();
+
+ /**
+ * Save queue list layout to the configuration file.
+ */
+ void saveLayout();
+public slots:
+ void updateActions();
+protected:
+ /**
+ * Initialize actions.
+ */
+ void initActions();
+
+ /**
+ * Initialize toolbar widgets.
+ */
+ void initToolBar();
+private:
+ KActionCollection *m_actionCollection;
+
+ // Actions
+ KAction *m_launchAction;
+ KAction *m_abortAction;
+ KAction *m_removeAction;
+ KAction *m_removeAllAction;
+ KAction *m_moveUpAction;
+ KAction *m_moveDownAction;
+ KAction *m_moveTopAction;
+ KAction *m_moveBottomAction;
+ KAction *m_editAction;
+
+ // Toolbar Actions
+ KAction *m_loadAction;
+ KAction *m_saveAction;
+ KAction *m_startAction;
+ KAction *m_pauseAction;
+ KAction *m_stopAction;
+ KAction *m_addAction;
+ KAction *m_searchAction;
+ KToggleAction *m_filterAction;
+
+ KListViewSearchLine *m_searchField;
+
+ KToolBar *m_toolBar;
+ KToolBar *m_searchToolBar;
+ QueueListView *m_queue;
+
+ QIntDict<QueueViewItem> m_queuedItems;
+private slots:
+ void slotObjectRemoved(long);
+ void slotObjectUpdated();
+ void slotTransferAdded(KFTPQueue::Transfer*);
+ void slotSiteAdded(KFTPQueue::Site*);
+
+ void contextMenuRequested(KListView*, QListViewItem*, const QPoint&);
+
+ void slotSearchEraseClicked();
+
+ // Slots for actions
+ void slotLaunch();
+ void slotAbort();
+ void slotRemove();
+ void slotRemoveAll();
+ void slotMoveUp();
+ void slotMoveDown();
+ void slotMoveTop();
+ void slotMoveBottom();
+ void slotEdit();
+
+ void slotLoad();
+ void slotSave();
+ void slotStart();
+ void slotPause();
+ void slotStop();
+ void slotAdd();
+ void slotSearch();
+ void slotFilter();
+
+ void slotDownloadLimitChanged(int value);
+ void slotUploadLimitChanged(int value);
+ void slotThreadCountChanged(int value);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/queueview/threadview.cpp b/kftpgrabber/src/widgets/queueview/threadview.cpp
new file mode 100644
index 0000000..fd1c663
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/threadview.cpp
@@ -0,0 +1,202 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "threadview.h"
+#include "kftpapi.h"
+
+#include "listview.h"
+
+#include <qlayout.h>
+#include <klocale.h>
+
+namespace KFTPWidgets {
+
+ThreadViewItem::ThreadViewItem(KFTPSession::Session *session, QListView *parent)
+ : QObject(parent),
+ QListViewItem(parent),
+ m_connection(0),
+ m_session(session)
+{
+ refresh();
+}
+
+ThreadViewItem::ThreadViewItem(KFTPSession::Connection *conn, QListViewItem *parent, int id)
+ : QObject(),
+ QListViewItem(parent),
+ m_id(id),
+ m_connection(conn),
+ m_session(0)
+{
+ connect(conn, SIGNAL(connectionRemoved()), this, SLOT(slotUpdateItemRequested()));
+ connect(conn, SIGNAL(connectionLost(KFTPSession::Connection*)), this, SLOT(slotUpdateItemRequested()));
+ connect(conn, SIGNAL(connectionEstablished()), this, SLOT(slotUpdateItemRequested()));
+
+ // Connect the transfer signals if the transfer is already present
+ KFTPQueue::Transfer *transfer = m_connection->getTransfer();
+ if (transfer) {
+ connect(transfer, SIGNAL(objectUpdated()), this, SLOT(slotUpdateItemRequested()));
+ } else {
+ connect(conn, SIGNAL(connectionAcquired()), this, SLOT(slotConnectionAcquired()));
+ }
+
+ refresh();
+}
+
+void ThreadViewItem::slotConnectionAcquired()
+{
+ if (!m_connection->getTransfer())
+ return;
+
+ connect(m_connection->getTransfer(), SIGNAL(objectUpdated()), this, SLOT(slotUpdateItemRequested()));
+ refresh();
+}
+
+void ThreadViewItem::refresh()
+{
+ if (m_session) {
+ // Set the columns
+ setText(0, i18n("Site session [%1]").arg(m_session->getClient()->socket()->getCurrentUrl().host()));
+ setPixmap(0, loadSmallPixmap("ftp"));
+ } else if (m_connection) {
+ setText(0, i18n("Thread %1").arg(m_id));
+ setPixmap(0, loadSmallPixmap("server"));
+ setText(1, m_connection->isConnected() ? i18n("idle") : i18n("disconnected"));
+ setText(2, "");
+
+ KFTPQueue::Transfer *transfer = m_connection->getTransfer();
+ if (transfer && transfer->isRunning()) {
+ QString speed;
+ filesize_t rawSpeed = transfer->getSpeed();
+
+ speed.sprintf( "%lld KB/s", (rawSpeed / 1024) );
+
+ if (rawSpeed > 1024*1024)
+ speed.sprintf("%lld MB/s", (rawSpeed / 1024) / 1024);
+ else if (rawSpeed == 0)
+ speed = "";
+
+ if (transfer->getStatus() == KFTPQueue::Transfer::Connecting) {
+ setText(1, i18n("connecting"));
+ } else {
+ setText(1, i18n("transferring"));
+ }
+
+ if (transfer->getTransferType() == KFTPQueue::FXP && rawSpeed == 0) {
+ KFTPSession::Connection *c = static_cast<KFTPQueue::TransferFile*>(transfer)->getOppositeConnection(m_connection);
+
+ setText(2, i18n("FXP - [%1]").arg(c->getUrl().host()));
+ } else {
+ setText(2, speed);
+ }
+ }
+ }
+}
+
+void ThreadViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ QListViewItem::paintCell(p, cg, column, width, alignment);
+}
+
+void ThreadViewItem::slotUpdateItemRequested()
+{
+ refresh();
+}
+
+KActionCollection *ThreadView::actionCollection()
+{
+ return KFTPAPI::getInstance()->mainWindow()->actionCollection();
+}
+
+ThreadView::ThreadView(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ // Create the list view
+ m_threads = new KFTPWidgets::ListView(this);
+
+ // Create the columns
+ m_threads->addColumn(i18n("Name"), 400);
+ m_threads->addColumn(i18n("Status"), 120);
+ m_threads->addColumn(i18n("Speed"), 70);
+
+ // Text when there are no threads
+ m_threads->setEmptyListText(i18n("There are no threads currently running."));
+
+ // Multi-select
+ m_threads->setSelectionModeExt(KListView::FileManager);
+ m_threads->setAllColumnsShowFocus(true);
+ m_threads->setRootIsDecorated(true);
+
+ layout->addWidget(m_threads);
+
+ connect(KFTPSession::Manager::self(), SIGNAL(update()), this, SLOT(slotUpdateSessions()));
+}
+
+ThreadView::~ThreadView()
+{
+}
+
+void ThreadView::slotUpdateSessions()
+{
+ KFTPSession::SessionList *list = KFTPSession::Manager::self()->getSessionList();
+ KFTPSession::Session *i;
+
+ m_threads->clear();
+
+ for (i = list->first(); i; i = list->next()) {
+ if (i->isRemote()) {
+ ThreadViewItem *site = new ThreadViewItem(i, m_threads);
+
+ QPtrList<KFTPSession::Connection> *c_list = i->getConnectionList();
+
+ if (c_list->count() > 0) {
+ KFTPSession::Connection *conn;
+ int id = 0;
+
+ for (conn = c_list->first(); conn; conn = c_list->next()) {
+ new ThreadViewItem(conn, site, ++id);
+ }
+
+ site->setOpen(true);
+ }
+ }
+ }
+}
+
+}
+
+#include "threadview.moc"
+
diff --git a/kftpgrabber/src/widgets/queueview/threadview.h b/kftpgrabber/src/widgets/queueview/threadview.h
new file mode 100644
index 0000000..5c37e6c
--- /dev/null
+++ b/kftpgrabber/src/widgets/queueview/threadview.h
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPTHREADVIEW_H
+#define KFTPTHREADVIEW_H
+
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <klistview.h>
+
+#include "kftpsession.h"
+
+class KToolBar;
+
+namespace KFTPWidgets {
+
+class ListView;
+
+class ThreadViewItem : public QObject, public QListViewItem
+{
+Q_OBJECT
+public:
+ ThreadViewItem(KFTPSession::Session *session, QListView *parent);
+ ThreadViewItem(KFTPSession::Connection *conn, QListViewItem *parent, int id);
+
+ void refresh();
+ virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+private:
+ int m_id;
+
+ QGuardedPtr<KFTPSession::Connection> m_connection;
+ QGuardedPtr<KFTPSession::Session> m_session;
+private slots:
+ void slotConnectionAcquired();
+ void slotUpdateItemRequested();
+};
+
+/**
+ * This widget shows all the currently used threads in KFTPGrabber transfers.
+ *
+ * @author Jernej Kos
+ */
+class ThreadView : public QWidget
+{
+Q_OBJECT
+public:
+ ThreadView(QWidget *parent = 0, const char *name = 0);
+ ~ThreadView();
+
+ KActionCollection *actionCollection();
+
+private slots:
+ void slotUpdateSessions();
+private:
+ KFTPWidgets::ListView *m_threads;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/quickconnect.cpp b/kftpgrabber/src/widgets/quickconnect.cpp
new file mode 100644
index 0000000..0eabdc8
--- /dev/null
+++ b/kftpgrabber/src/widgets/quickconnect.cpp
@@ -0,0 +1,476 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "widgets/quickconnect.h"
+#include "bookmarks/editortls.h"
+#include "misc/config.h"
+#include "kftpbookmarks.h"
+#include "misc.h"
+
+#include "engine/thread.h"
+#include "engine/ftpsocket.h"
+
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kcombobox.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+
+/* KSSL includes */
+#include <ksslpkcs12.h>
+
+#include <qcheckbox.h>
+#include <qspinbox.h>
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+QuickConnectDialog::QuickConnectDialog(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Quick Connect"), Ok|Cancel, Ok),
+ m_noUrlChange(false),
+ m_protocolAdvancedDialog(0L),
+ m_portChanged(false)
+{
+ // Create the main widget
+ m_layout = new KFTPQuickConnectLayout(this);
+
+ // Set the dialog options
+ setMainWidget(m_layout);
+ resize(QSize(200,300));
+
+ m_layout->clearRecent->setIconSet(loadSmallIcon("clear_left"));
+ m_layout->protoAdvanced->setIconSet(loadSmallIcon("configure"));
+
+ // Connect the slots
+ connect(m_layout->urlBox, SIGNAL(textChanged(const QString&)), this, SLOT(slotUrlChanged(const QString &)));
+ connect(m_layout->hostBox, SIGNAL(textChanged(const QString&)), this, SLOT(slotHostChanged(const QString&)));
+ connect(m_layout->protocolBox, SIGNAL(activated(int)), this, SLOT(slotProtocolChanged(int)));
+ connect(m_layout->protoAdvanced, SIGNAL(clicked()), this, SLOT(slotProtoAdvancedClicked()));
+ connect(m_layout->usernameBox, SIGNAL(textChanged(const QString&)), this, SLOT(slotUserChanged()));
+ connect(m_layout->passwordBox, SIGNAL(textChanged(const QString&)), this, SLOT(slotPassChanged()));
+ connect(m_layout->portBox, SIGNAL(valueChanged(int)), this, SLOT(slotPortChanged(int)));
+ connect(m_layout->anonLogin, SIGNAL(clicked()), this, SLOT(slotAnonClicked()));
+ connect(m_layout->recentConnections, SIGNAL(activated(int)), this, SLOT(slotRecentConnectionActivated(int)));
+ connect(m_layout->clearRecent, SIGNAL(clicked()), this, SLOT(slotClearRecentClicked()));
+
+ // Init url
+ m_url.setProtocol("ftp");
+ m_url.setPort(21);
+ m_url.setPath("/");
+ m_layout->urlBox->setURL(m_url);
+
+ // Use anonymous account by default
+ m_layout->anonLogin->setChecked(true);
+ slotAnonClicked();
+
+ // Init the protocol advanced button
+ m_layout->protoAdvanced->setEnabled(false);
+
+ // Populate charsets
+ QStringList charsets = KGlobal::charsets()->descriptiveEncodingNames();
+ m_layout->serverEncoding->insertStringList(charsets);
+
+ QString defEncoding = KFTPCore::Config::defEncoding();
+ defEncoding = QString("%1 ( %2 )").arg(KGlobal::charsets()->languageForEncoding(defEncoding)).arg(defEncoding);
+ m_layout->serverEncoding->setCurrentText(defEncoding);
+
+ // Populate recent sites
+ int index = 0;
+ QStringList recentSites = KFTPCore::Config::recentSites();
+ for (QStringList::Iterator i = recentSites.begin(); i != recentSites.end(); ++i) {
+ KURL siteUrl = KURL(*i);
+
+ m_recentSites[index] = siteUrl;
+ m_layout->recentConnections->insertItem(siteUrl.prettyURL(), index++);
+ }
+
+ slotRecentConnectionActivated(0);
+}
+
+QuickConnectDialog::~QuickConnectDialog()
+{
+ if (m_protocolAdvancedDialog)
+ delete m_protocolAdvancedDialog;
+}
+
+void QuickConnectDialog::slotRecentConnectionActivated(int index)
+{
+ KURL url = m_recentSites[index];
+ m_layout->urlBox->setText(url.url());
+
+ if (m_url.user() == "anonymous") {
+ m_layout->anonLogin->setChecked(true);
+ slotAnonClicked();
+ }
+}
+
+void QuickConnectDialog::slotClearRecentClicked()
+{
+ if (KMessageBox::questionYesNo(0, i18n("Clear list of recently accessed sites ?")) == KMessageBox::No)
+ return;
+
+ m_layout->recentConnections->clear();
+ m_recentSites.clear();
+ KFTPCore::Config::setRecentSites(QStringList());
+}
+
+void QuickConnectDialog::slotUrlChanged(const QString &text)
+{
+ if (m_noUrlChange)
+ return;
+
+ m_layout->anonLogin->setChecked(false);
+ m_layout->usernameBox->setEnabled(true);
+ m_layout->passwordBox->setEnabled(true);
+
+ KURL tmpUrl = text;
+ if (!tmpUrl.isValid())
+ return;
+ else
+ m_url = tmpUrl;
+
+ m_noUrlChange = true;
+
+ if (m_url.protocol() == "ftp")
+ m_layout->protocolBox->setCurrentItem(SP_FTP);
+ else if (m_url.protocol() == "sftp")
+ m_layout->protocolBox->setCurrentItem(SP_SFTP);
+ else {
+ // Force FTP protocol
+ m_url.setProtocol("ftp");
+ m_layout->protocolBox->setCurrentItem(SP_FTP);
+ }
+
+ m_layout->hostBox->setText(m_url.host());
+ m_layout->usernameBox->setText(m_url.user());
+
+ if (m_url.hasPass()) {
+ m_layout->passwordBox->erase();
+ m_layout->passwordBox->insert(m_url.pass());
+ }
+
+ if (m_url.port() == 0) {
+ switch (m_layout->protocolBox->currentItem()) {
+ case SP_SFTP: {
+ m_layout->portBox->setValue(22);
+ m_url.setPort(22);
+ break;
+ }
+ default: {
+ m_layout->portBox->setValue(21);
+ m_url.setPort(21);
+ break;
+ }
+ }
+ } else {
+ m_layout->portBox->setValue(m_url.port());
+ }
+
+ m_layout->urlBox->setText(m_url.prettyURL());
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotHostChanged(const QString &text)
+{
+ if (m_noUrlChange) return;
+
+ m_noUrlChange = true;
+ m_url.setHost(text);
+ m_layout->urlBox->setURL(m_url);
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotPortChanged(int port)
+{
+ if (m_noUrlChange) return;
+
+ m_noUrlChange = true;
+ m_url.setPort(port);
+ m_layout->urlBox->setURL(m_url);
+ m_portChanged = true;
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotUserChanged()
+{
+ if (m_noUrlChange) return;
+
+ m_noUrlChange = true;
+ m_url.setUser(m_layout->usernameBox->text());
+ m_layout->urlBox->setURL(m_url);
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotPassChanged()
+{
+ if (m_noUrlChange) return;
+
+ m_noUrlChange = true;
+ m_url.setPass(m_layout->passwordBox->password());
+ m_layout->urlBox->setURL(m_url);
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotAnonClicked()
+{
+ static QString tmpUser, tmpPass;
+
+ if (m_layout->anonLogin->isChecked()) {
+ m_layout->usernameBox->setEnabled(false);
+ m_layout->passwordBox->setEnabled(false);
+ tmpUser = m_layout->usernameBox->text();
+ tmpPass = m_layout->passwordBox->text();
+ m_layout->usernameBox->setText("anonymous");
+ m_layout->passwordBox->erase();
+
+ // Use the appropriate e-mail address for anonymous accounts
+ if (!KFTPCore::Config::anonMail().isEmpty())
+ m_layout->passwordBox->insert(KFTPCore::Config::anonMail());
+ else
+ m_layout->passwordBox->insert("userlogin@anonymo.us");
+ } else {
+ m_layout->usernameBox->setText(tmpUser);
+ m_layout->passwordBox->erase();
+ m_layout->passwordBox->insert(tmpPass);
+ m_layout->usernameBox->setEnabled(true);
+ m_layout->passwordBox->setEnabled(true);
+ }
+
+ slotUserChanged();
+ slotPassChanged();
+}
+
+void QuickConnectDialog::slotOk()
+{
+ // Construct a nice error message
+ QString errorMessage;
+
+ if (m_url.host().isEmpty())
+ errorMessage = i18n("a hostname");
+
+ if (m_url.port() < 1)
+ errorMessage += (errorMessage.isEmpty() ? QString::null : QString::fromLatin1(", ")) + i18n("a valid port");
+
+ if (m_url.user().isEmpty())
+ errorMessage += (errorMessage.isEmpty() ? QString::null : QString::fromLatin1(", ")) + i18n("your username");
+
+ if (m_url.pass().isEmpty())
+ errorMessage += (errorMessage.isEmpty() ? QString::null : QString::fromLatin1(", ")) + i18n("your password");
+
+ if (errorMessage.findRev(",") != -1)
+ errorMessage = errorMessage.replace(errorMessage.findRev(","), 1 , i18n(" and"));
+
+ if (!errorMessage.isEmpty()) {
+ KMessageBox::sorry(0, i18n("Please enter ") + errorMessage + ".");
+ return;
+ }
+
+ if (m_layout->addBookmark->isChecked()) {
+ // Add the current connection to bookmarks. Use hostname as the bookmark name.
+ KFTPBookmarks::Site *root = KFTPBookmarks::Manager::self()->findCategory("root");
+ KFTPBookmarks::Site *site = root->addSite();
+ site->setAttribute("name", m_url.host());
+
+ site->setProperty("host", m_url.host());
+ site->setProperty("port", m_url.port());
+ site->setProperty("username", m_url.user());
+ site->setProperty("password", encodePassword(m_url.pass()));
+ site->setProperty("defremotepath", "/");
+ site->setProperty("protocol", m_layout->protocolBox->currentItem() == SP_SFTP ? "sftp" : "ftp");
+ site->setProperty("use_tls", m_layout->protocolBox->currentItem() == SP_SSL_EXPLICIT);
+ site->setProperty("use_implicit", m_layout->protocolBox->currentItem() == SP_SSL_IMPLICIT);
+ site->setProperty("encoding", KGlobal::charsets()->encodingForName(m_layout->serverEncoding->currentText()));
+
+ // Save TLS options
+ if (m_protocolAdvancedDialog) {
+ site->setProperty("tls_data_mode", m_protocolAdvancedDialog->getTLSMode());
+ site->setProperty("use_cert", m_protocolAdvancedDialog->isCertChecked());
+
+ if (m_protocolAdvancedDialog->isCertChecked())
+ site->setProperty("tls_cert_path", m_protocolAdvancedDialog->getCertPath());
+ }
+
+ KFTPBookmarks::Manager::self()->emitUpdate();
+ }
+
+ // Save to recent sites
+ QStringList recentSites = KFTPCore::Config::recentSites();
+ if (m_url.user() == "anonymous")
+ m_url.setPass("");
+
+ if (recentSites.findIndex(m_url.url()) == -1) {
+ recentSites.prepend(m_url.url());
+
+ if (recentSites.count() > 10)
+ recentSites.pop_back();
+
+ KFTPCore::Config::setRecentSites(recentSites);
+ }
+
+ // Close the dialog
+ accept();
+}
+
+void QuickConnectDialog::setupClient(KFTPEngine::Thread *client)
+{
+ // First activate the correct socket and reset the old flags
+ client->selectSocketForProtocol(KURL(QString("%1://test/").arg(m_layout->protocolBox->currentItem() == SP_SFTP ? "sftp" : "ftp")));
+ client->socket()->initConfig();
+
+ client->socket()->setConfig("retry", 0);
+
+ client->socket()->setConfig("ssl.use_tls", m_layout->protocolBox->currentItem() == SP_SSL_EXPLICIT);
+ client->socket()->setConfig("ssl.use_implicit", m_layout->protocolBox->currentItem() == SP_SSL_IMPLICIT);
+ client->socket()->setConfig("encoding", KGlobal::charsets()->encodingForName(m_layout->serverEncoding->currentText()));
+
+ // Set TLS options
+ if (m_protocolAdvancedDialog) {
+ client->socket()->setConfig("ssl.prot_mode", m_protocolAdvancedDialog->getTLSMode());
+
+ // Should we use a X509 certificate ?
+ if (m_protocolAdvancedDialog->isCertChecked() && m_layout->protocolBox->currentItem() == SP_FTP) {
+ // Ask the user for the decryption password
+ QCString certPass;
+ KPasswordDialog::getPassword(certPass, i18n("Please provide your X509 certificate decryption password."));
+
+ static_cast<KFTPEngine::FtpSocket*>(client->socket())->setSslClientCertificate(KSSLPKCS12::loadCertFile(m_protocolAdvancedDialog->getCertPath(), certPass));
+ }
+ }
+}
+
+void QuickConnectDialog::slotProtocolChanged(int item)
+{
+ if (m_noUrlChange) return;
+
+ // Enable/Disable the SSL/TLS settings if needed
+ m_layout->protoAdvanced->setEnabled( item == SP_SSL_EXPLICIT || item == SP_SSL_IMPLICIT );
+
+ // Set the default port
+ if (!m_portChanged) {
+ switch (item) {
+ case SP_SSL_IMPLICIT:
+ if (m_layout->portBox->value() == 21 || m_layout->portBox->value() == 22)
+ m_layout->portBox->setValue(993);
+ break;
+ case SP_SFTP:
+ if (m_layout->portBox->value() == 21 || m_layout->portBox->value() == 993)
+ m_layout->portBox->setValue(22);
+ break;
+ default:
+ if (m_layout->portBox->value() == 22 || m_layout->portBox->value() == 993)
+ m_layout->portBox->setValue(21);
+ break;
+ }
+
+ m_portChanged = false;
+ }
+
+ m_noUrlChange = true;
+ m_url.setProtocol( item == SP_SFTP ? "sftp" : "ftp");
+ m_url.setPort( m_layout->portBox->value() );
+ m_layout->urlBox->setURL(m_url);
+ m_noUrlChange = false;
+}
+
+void QuickConnectDialog::slotProtoAdvancedClicked()
+{
+ if (!m_protocolAdvancedDialog)
+ m_protocolAdvancedDialog = new KFTPWidgets::Bookmarks::BookmarkEditorTLS(this);
+
+ QChar tlsMode = m_protocolAdvancedDialog->getTLSMode();
+ bool certChecked = m_protocolAdvancedDialog->isCertChecked();
+ QString certPath = m_protocolAdvancedDialog->getCertPath();
+ m_protocolAdvancedDialog->slotChangeActiveX509Group();
+
+ if (!m_protocolAdvancedDialog->exec()) {
+ m_protocolAdvancedDialog->setTLSMode(tlsMode);
+ m_protocolAdvancedDialog->setCertChecked(certChecked);
+ m_protocolAdvancedDialog->setCertPath(certPath);
+ m_protocolAdvancedDialog->slotChangeActiveX509Group();
+ }
+}
+
+QChar QuickConnectDialog::getTLSMode()
+{
+ if (!m_protocolAdvancedDialog)
+ return false;
+
+ return m_protocolAdvancedDialog->getTLSMode();
+}
+
+bool QuickConnectDialog::isCertChecked()
+{
+ if (!m_protocolAdvancedDialog)
+ return false;
+
+ return m_protocolAdvancedDialog->isCertChecked();
+}
+
+QString QuickConnectDialog::getCertPath()
+{
+ if (!m_protocolAdvancedDialog)
+ return QString::null;
+
+ return m_protocolAdvancedDialog->getCertPath();
+}
+
+int QuickConnectDialog::getFTPMode()
+{
+ return m_layout->protocolBox->currentItem();
+}
+
+void QuickConnectDialog::setHost(const QString &host)
+{
+ m_layout->hostBox->setText(host);
+}
+
+void QuickConnectDialog::setPort(int port)
+{
+ m_layout->portBox->setValue(port);
+}
+
+void QuickConnectDialog::setFocusToUser()
+{
+ m_layout->usernameBox->setFocus();
+}
+
+}
+
+#include "quickconnect.moc"
diff --git a/kftpgrabber/src/widgets/quickconnect.h b/kftpgrabber/src/widgets/quickconnect.h
new file mode 100644
index 0000000..ad88212
--- /dev/null
+++ b/kftpgrabber/src/widgets/quickconnect.h
@@ -0,0 +1,162 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSQUICKCONNECT_H
+#define KFTPWIDGETSQUICKCONNECT_H
+
+#include "kftpquickconnectlayout.h"
+
+#include <qmap.h>
+
+#include <kdialogbase.h>
+#include <kurl.h>
+
+namespace KFTPEngine {
+ class Thread;
+}
+
+namespace KFTPWidgets {
+
+namespace Bookmarks {
+ class BookmarkEditorTLS;
+}
+
+/**
+ * @author Jernej Kos
+ */
+class QuickConnectDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ QuickConnectDialog(QWidget *parent = 0, const char *name = 0);
+ ~QuickConnectDialog();
+
+ /**
+ * Get URL the user is trying to connect to.
+ *
+ * @return a KURL representing the destination server
+ */
+ KURL getUrl() { return m_url; }
+
+ /**
+ * Get the currently selected file transfer protocol.
+ *
+ * @return index of currently selected protocol
+ */
+ int getFTPMode();
+
+ /**
+ * Get the currently set TLS operation mode.
+ *
+ * @return currently set TLS operation mode
+ */
+ QChar getTLSMode();
+
+ /**
+ * Is the "use custom X509 certificate" checked or not.
+ *
+ * @return true if the "use custom X509 certificate" is checked
+ */
+ bool isCertChecked();
+
+ /**
+ * Get the currently set X509 certificate path.
+ *
+ * @return path to the user's X509 certificate for this connection
+ */
+ QString getCertPath();
+
+ /**
+ * Set the destination server's hostname.
+ *
+ * @param host destination server's hostname
+ */
+ void setHost(const QString &host);
+
+ /**
+ * Set the destination server's port.
+ *
+ * @param port destination server's port
+ */
+ void setPort(int port);
+
+ /**
+ * Focus the dialog's username input field.
+ */
+ void setFocusToUser();
+
+ /**
+ * Configures the client acoording to the settings chosen in the quick connect
+ * dialog.
+ *
+ * @param client a disconnected client socket
+ */
+ void setupClient(KFTPEngine::Thread *client);
+private:
+ enum ServerProtocol {
+ SP_FTP = 0,
+ SP_SSL_EXPLICIT,
+ SP_SSL_IMPLICIT,
+ SP_SFTP
+ };
+
+ KFTPQuickConnectLayout *m_layout;
+
+ bool m_noUrlChange;
+ KURL m_url;
+ Bookmarks::BookmarkEditorTLS *m_protocolAdvancedDialog;
+
+ bool m_portChanged;
+
+ QMap<int, KURL> m_recentSites;
+private slots:
+ virtual void slotOk();
+
+ void slotUrlChanged(const QString &);
+ void slotHostChanged(const QString&);
+ void slotPortChanged(int);
+ void slotUserChanged();
+ void slotPassChanged();
+ void slotAnonClicked();
+ void slotProtocolChanged(int);
+ void slotProtoAdvancedClicked();
+ void slotRecentConnectionActivated(int index);
+ void slotClearRecentClicked();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/searchdialog.cpp b/kftpgrabber/src/widgets/searchdialog.cpp
new file mode 100644
index 0000000..ade32c3
--- /dev/null
+++ b/kftpgrabber/src/widgets/searchdialog.cpp
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "widgets/searchdialog.h"
+#include "kftpsearchlayout.h"
+#include "kftpserverlineedit.h"
+#include "kftpbookmarks.h"
+#include "kftpqueue.h"
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qspinbox.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+
+namespace KFTPWidgets {
+
+SearchDialog::SearchDialog(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Search & Replace"), Ok|Cancel, Ok)
+{
+ // Create the main widget
+ m_layout = new KFTPSearchLayout(this);
+
+ // Set the dialog options
+ setMainWidget(m_layout);
+ setInitialSize(QSize(500,400));
+
+ connect(m_layout->searchServer, SIGNAL(clicked()), this, SLOT(slotSearchServerClicked()));
+ connect(m_layout->searchServerName, SIGNAL(siteChanged(KFTPBookmarks::Site*)), this, SLOT(slotSiteChanged(KFTPBookmarks::Site*)));
+}
+
+QString SearchDialog::replaceCap(QStringList cap, const QString &text)
+{
+ QString tmp = text;
+
+ QStringList::Iterator end( cap.end() );
+ for(QStringList::Iterator i( cap.begin() ); i != end; ++i) {
+ tmp.replace("$" + QString::number(cap.findIndex(*i)), *i);
+ }
+
+ return tmp;
+}
+
+void SearchDialog::replace(KFTPQueue::Transfer *i)
+{
+ QRegExp s, d;
+
+ s.setPattern(m_layout->searchSrcPath->text());
+ d.setPattern(m_layout->searchDstPath->text());
+
+ KURL tmp = i->getSourceUrl().isLocalFile() ? i->getDestUrl() : i->getSourceUrl();
+ tmp.setPath("/");
+
+ KURL match;
+ match.setProtocol("ftp");
+ match.setHost(m_layout->searchServerHost->text());
+ match.setPort(m_layout->searchServerPort->value());
+ match.setUser(m_layout->searchServerUser->text());
+ match.setPass(m_layout->searchServerPass->password());
+ match.setPath("/");
+
+ if (s.search(i->getSourceUrl().path()) != -1 && d.search(i->getDestUrl().path()) != -1 &&
+ (!m_layout->searchServer->isChecked() || tmp.url() == match.url())) {
+ // Do the replacing
+ KURL newSource = i->getSourceUrl();
+ KURL newDest = i->getDestUrl();
+
+ newSource.setPath(replaceCap(s.capturedTexts(), m_layout->replaceSrcPath->text()));
+ newDest.setPath(replaceCap(d.capturedTexts(), m_layout->replaceDstPath->text()));
+
+ i->setSourceUrl(newSource);
+ i->setDestUrl(newDest);
+
+ i->emitUpdate();
+ }
+}
+
+void SearchDialog::searchAndReplace(KFTPQueue::QueueObject *parent)
+{
+ if (parent->isLocked())
+ return;
+
+ QPtrList<KFTPQueue::QueueObject> list = parent->getChildrenList();
+
+ KFTPQueue::QueueObject *i;
+ for (i = list.first(); i; i = list.next()) {
+ if (i->hasChildren() && !i->isLocked()) {
+ searchAndReplace(i);
+ }
+
+ if (i->isTransfer() && !i->isLocked())
+ replace(static_cast<KFTPQueue::Transfer*>(i));
+ }
+}
+
+void SearchDialog::searchAndReplace()
+{
+ searchAndReplace(KFTPQueue::Manager::self()->topLevelObject());
+}
+
+void SearchDialog::slotOk()
+{
+ searchAndReplace();
+ accept();
+}
+
+void SearchDialog::slotSearchServerClicked()
+{
+ m_layout->groupBox1->setEnabled(m_layout->searchServer->isChecked());
+}
+
+void SearchDialog::slotSiteChanged(KFTPBookmarks::Site *site)
+{
+ if (site) {
+ m_layout->searchServerHost->setText(site->getProperty("host"));
+ m_layout->searchServerPort->setValue(site->getIntProperty("port"));
+ m_layout->searchServerUser->setText(site->getProperty("username"));
+ m_layout->searchServerPass->erase();
+ m_layout->searchServerPass->insert(site->getProperty("password"));
+ } else {
+ m_layout->searchServerHost->clear();
+ m_layout->searchServerPort->setValue(21);
+ m_layout->searchServerUser->clear();
+ m_layout->searchServerPass->erase();
+ }
+}
+
+}
+
+#include "searchdialog.moc"
diff --git a/kftpgrabber/src/widgets/searchdialog.h b/kftpgrabber/src/widgets/searchdialog.h
new file mode 100644
index 0000000..7ae598b
--- /dev/null
+++ b/kftpgrabber/src/widgets/searchdialog.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2004 by the KFTPGrabber developers
+ * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSSEARCHDIALOG_H
+#define KFTPWIDGETSSEARCHDIALOG_H
+
+#include <qdom.h>
+#include <kdialogbase.h>
+
+namespace KFTPQueue {
+ class Transfer;
+ class QueueObject;
+}
+
+namespace KFTPBookmarks {
+ class Site;
+}
+
+class KFTPSearchLayout;
+
+namespace KFTPWidgets {
+
+/**
+ * This dialog provides search & replace functionality for queued transfers.
+ *
+ * @author Jernej Kos
+ */
+class SearchDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ */
+ SearchDialog(QWidget *parent = 0, const char *name = 0);
+private:
+ KFTPSearchLayout *m_layout;
+
+ void replace(KFTPQueue::Transfer *i);
+
+ QString replaceCap(QStringList cap, const QString &text);
+ void searchAndReplace(KFTPQueue::QueueObject *parent);
+ void searchAndReplace();
+private slots:
+ virtual void slotOk();
+
+ void slotSearchServerClicked();
+ void slotSiteChanged(KFTPBookmarks::Site *site);
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/sidebar.cpp b/kftpgrabber/src/widgets/sidebar.cpp
new file mode 100644
index 0000000..bd7545b
--- /dev/null
+++ b/kftpgrabber/src/widgets/sidebar.cpp
@@ -0,0 +1,393 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004-2005 Max Howell <max.howell@methylblue.com>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "misc.h"
+#include "sidebar.h"
+#include "multitabbar.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qsignalmapper.h>
+#include <qstyle.h>
+
+namespace KFTPWidgets {
+
+class Splitter : public QWidget {
+public:
+ Splitter(Sidebar *w)
+ : QWidget(w, "divider"),
+ m_position(w->m_position)
+ {
+ if (m_position == Sidebar::Left)
+ setCursor(QCursor(SplitHCursor));
+ else
+ setCursor(QCursor(SplitVCursor));
+
+ styleChange(style());
+ }
+
+ virtual void paintEvent(QPaintEvent*)
+ {
+ QPainter p(this);
+ parentWidget()->style().drawPrimitive(QStyle::PE_Splitter, &p, rect(), colorGroup(), m_position == Sidebar::Left ? QStyle::Style_Horizontal : 0);
+ }
+
+ virtual void styleChange(QStyle&)
+ {
+ if (m_position == Sidebar::Left)
+ setFixedWidth(style().pixelMetric(QStyle::PM_SplitterWidth, this));
+ else
+ setFixedHeight(style().pixelMetric(QStyle::PM_SplitterWidth, this));
+ }
+
+ virtual void mouseMoveEvent(QMouseEvent *e)
+ {
+ static_cast<Sidebar*>(parent())->mouseMovedOverSplitter(e);
+ }
+private:
+ Sidebar::Position m_position;
+};
+
+Sidebar::Sidebar(QWidget *parent, Position position)
+ : QWidget(parent, "Sidebar"),
+ m_position(position),
+ m_divider(new KFTPWidgets::Splitter(this)),
+ m_content(new QVBox(this)),
+ m_tabBar(new MultiTabBar(position == Left ? MultiTabBar::Vertical : MultiTabBar::Horizontal, this)),
+ m_sidebarBox(new QWidget(this)),
+ m_currentIndex(-1),
+ m_lastIndex(-1),
+ m_mapper(new QSignalMapper(this))
+{
+ m_tabBar->setStyle(MultiTabBar::AMAROK);
+ m_tabBar->setPosition(m_position == Left ? MultiTabBar::Left : MultiTabBar::Bottom);
+ m_tabBar->showActiveTabTexts(true);
+
+ if (m_position == Left) {
+ m_pos = m_tabBar->sizeHint().width() + 5;
+
+ m_tabBar->setFixedWidth(m_pos);
+ m_tabBar->move(0, 3);
+
+ QVBoxLayout *layout = new QVBoxLayout(m_sidebarBox);
+ layout->addSpacing(3);
+ layout->setAutoAdd(true);
+
+ m_sidebarBox->move(m_pos, 0);
+ m_sidebarBox->hide();
+ m_divider->hide();
+ m_content->setSpacing(1);
+ } else {
+ m_pos = m_tabBar->sizeHint().height() + 5;
+ m_tabBar->setFixedHeight(m_pos);
+
+ QVBoxLayout *layout = new QVBoxLayout(m_sidebarBox);
+ layout->setAutoAdd(true);
+
+ m_sidebarBox->hide();
+ m_divider->hide();
+ m_content->setSpacing(1);
+ }
+
+ connect(m_mapper, SIGNAL(mapped(int)), SLOT(showHideSidebar(int)));
+}
+
+void Sidebar::setVisible(bool visible)
+{
+ if (m_position == Left) {
+ m_pos = m_tabBar->sizeHint().width() + 5;
+ m_tabBar->setFixedWidth(visible ? m_pos : 0);
+ } else {
+ m_pos = m_tabBar->sizeHint().height() + 5;
+ m_tabBar->setFixedHeight(visible ? m_pos : 0);
+ }
+
+ if (m_currentIndex != -1)
+ showHideSidebar(m_currentIndex);
+
+ if (visible)
+ m_tabBar->show();
+ else
+ m_tabBar->hide();
+
+ adjustWidgetSizes();
+}
+
+Sidebar::~Sidebar()
+{
+ KConfig *config;
+ // Save the currently selected sidebar
+ if (m_position == Left) {
+ config = KFTPGrabberBase::config(QString("Sidebar_%1").arg("Left"));
+ } else {
+ config = KFTPGrabberBase::config(QString("Sidebar_%1").arg("Bottom"));
+ }
+ if (m_currentIndex != -1) {
+ config->writeEntry("CurrentSidebar", currentSidebar()->name());
+ } else {
+ config->writeEntry("CurrentSidebar", QString::null);
+ }
+ if (m_position == Left) {
+ config->writeEntry("Size", m_sidebarBox->width());
+ } else {
+ config->writeEntry("Size", m_sidebarBox->height());
+ }
+}
+
+void Sidebar::polish()
+{
+ QWidget::polish();
+
+ KConfig *config = KFTPGrabberBase::config(QString("Sidebar_%1").arg(m_position == Left ? "Left" : "Bottom"));
+ const int index = indexForName(config->readEntry("CurrentSidebar"));
+
+ if (m_position == Left) {
+ uint M = 0;
+ for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
+ const uint m = (*it)->minimumWidth();
+ if (m > M) M = m;
+ }
+
+ const int width = config->readNumEntry("Size", sidebar(index)->sizeHint().width());
+
+ if (M > 250) {
+ M = 250;
+ }
+
+ m_sidebarBox->setMinimumWidth(M);
+ m_sidebarBox->resize(width, height());
+ } else {
+ uint M = 0;
+ for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
+ const uint m = (*it)->minimumHeight();
+ if (m > M) M = m;
+ }
+
+ const int height = config->readNumEntry("Size", sidebar(index)->height());
+
+ if (M > 250) {
+ M = 250;
+ }
+
+ m_sidebarBox->setMinimumHeight(M);
+ m_sidebarBox->resize(width(), height);
+ }
+
+ // If any sidebar should be open, open it
+ if (index != -1)
+ showHideSidebar(index);
+}
+
+void Sidebar::adjustWidgetSizes()
+{
+ if (m_position == Left) {
+ const uint w = width();
+ const uint h = height();
+ const uint mxW = maxSidebarWidth();
+ const uint p = (m_pos < mxW) ? m_pos : mxW;
+ const uint ppw = p + m_divider->width();
+ const uint tbw = m_tabBar->width();
+
+ m_divider->move(p, 0);
+
+ const uint offset = !m_divider->isHidden() ? ppw : tbw;
+
+ m_sidebarBox->resize(p - tbw, h);
+ m_content->setGeometry(offset, 0, w - offset, h);
+ } else {
+ const uint w = width();
+ const uint h = height();
+ const uint mxH = maxSidebarHeight();
+ const uint p = (m_pos < mxH) ? m_pos : mxH;
+ const uint pph = p + m_divider->height();
+ const uint tbh = m_tabBar->height();
+
+ m_divider->move(0, h - pph);
+
+ const uint offset = !m_divider->isHidden() ? pph : tbh;
+
+ m_sidebarBox->setGeometry(0, h - p, w, p - tbh);
+ m_content->setGeometry(0, 0, w, h - offset);
+ m_tabBar->move(0, h - tbh);
+ }
+}
+
+void Sidebar::mouseMovedOverSplitter(QMouseEvent *e)
+{
+ const uint oldPos = m_pos;
+ uint newPos;
+ uint minPos;
+ uint maxPos;
+
+ if (m_position == Left) {
+ newPos = mapFromGlobal(e->globalPos()).x();
+ minPos = m_tabBar->width() + m_sidebarBox->minimumWidth();
+ maxPos = maxSidebarWidth();
+ } else {
+ newPos = height() - mapFromGlobal(e->globalPos()).y();
+ minPos = m_tabBar->height() + m_sidebarBox->minimumHeight();
+ maxPos = maxSidebarHeight();
+ }
+
+ if (newPos < minPos)
+ m_pos = minPos;
+ else if (newPos > maxPos)
+ m_pos = maxPos;
+ else
+ m_pos = newPos;
+
+ if (m_pos != oldPos)
+ adjustWidgetSizes();
+}
+
+bool Sidebar::event(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::LayoutHint: {
+ if (m_position == Left) {
+ setMinimumWidth(m_tabBar->minimumWidth() + m_divider->minimumWidth() + m_sidebarBox->width() + m_content->minimumWidth());
+ } else {
+ setMinimumHeight(m_tabBar->minimumHeight() + m_divider->minimumHeight() + m_sidebarBox->height() + m_content->minimumHeight());
+ }
+ break;
+ }
+ case QEvent::Resize: {
+ if (m_position == Left) {
+ m_divider->resize(0, height());
+ m_tabBar->resize(0, height());
+ } else {
+ m_divider->resize(width(), 0);
+ m_tabBar->resize(width(), 0);
+ }
+
+ adjustWidgetSizes();
+ return true;
+ }
+ default: break;
+ }
+
+ return QWidget::event(e);
+}
+
+void Sidebar::addSidebar(QWidget *widget, const QString &title, const QString &icon)
+{
+ const int id = m_tabBar->tabs()->count(); // the next available id
+ const QString name(widget->name());
+ QWidget *tab;
+
+ widget->reparent(m_sidebarBox, QPoint());
+ widget->hide();
+
+ m_tabBar->appendTab(SmallIcon(icon), id, title);
+ tab = m_tabBar->tab(id);
+ tab->setFocusPolicy(QWidget::NoFocus);
+
+ // We use a SignalMapper to show/hide the corresponding browser when tabs are clicked
+ connect(tab, SIGNAL(clicked()), m_mapper, SLOT(map()));
+ m_mapper->setMapping(tab, id);
+
+ m_sidebars.push_back(widget);
+}
+
+void Sidebar::showHideSidebar(int index)
+{
+ const int prevIndex = m_currentIndex;
+
+ if (m_currentIndex != -1) {
+ // First we need to hide the currentBrowser
+ m_currentIndex = -1;
+
+ m_sidebars[prevIndex]->hide();
+ m_tabBar->setTab(prevIndex, false);
+ }
+
+ if (index == prevIndex) {
+ // Close the sidebar
+ m_sidebarBox->hide();
+ m_divider->hide();
+
+ adjustWidgetSizes();
+ } else if ((uint) index < m_sidebars.count()) {
+ // Open up target
+ QWidget* const target = m_sidebars[index];
+ m_currentIndex = index;
+
+ m_divider->show();
+ target->show();
+ target->setFocus();
+ m_sidebarBox->show();
+ m_tabBar->setTab(index, true);
+
+ if (prevIndex == -1) {
+ if (m_position == Left) {
+ m_pos = m_sidebarBox->width() + m_tabBar->width();
+ } else {
+ m_pos = currentSidebar()->height() + m_tabBar->height();
+ }
+
+ adjustWidgetSizes();
+ }
+ }
+}
+
+QWidget *Sidebar::sidebar(const QString &name) const
+{
+ for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
+ if (name == (*it)->name())
+ return *it;
+ }
+
+ return 0;
+}
+
+int Sidebar::indexForName(const QString &name) const
+{
+ for (uint x = 0; x < m_sidebars.count(); ++x) {
+ if (name == m_sidebars[x]->name())
+ return x;
+ }
+
+ return -1;
+}
+
+}
+
+#include "sidebar.moc"
diff --git a/kftpgrabber/src/widgets/sidebar.h b/kftpgrabber/src/widgets/sidebar.h
new file mode 100644
index 0000000..4d4f710
--- /dev/null
+++ b/kftpgrabber/src/widgets/sidebar.h
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ * Copyright (C) 2004-2005 Max Howell <max.howell@methylblue.com>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSSIDEBAR_H
+#define KFTPWIDGETSSIDEBAR_H
+
+#include <qwidget.h>
+#include <qvaluevector.h>
+
+class KURL;
+class QSignalMapper;
+class QVBox;
+
+namespace KFTPWidgets {
+
+typedef QValueVector<QWidget*> SidebarList;
+
+class MultiTabBar;
+class MultiTabBarTab;
+
+/**
+ * This class provides a sidebar. It uses a customised version of KMultiTabBar for
+ * displaying the actual tabs. It uses a QSignalMapper to map signals from the
+ * tabs to the actual sidebars.
+ *
+ * This class was copied from amaroK (BrowserBar) and modified to support bottom
+ * sidebars as well.
+ *
+ * @author amaroK developers
+ * @author Jernej Kos
+ */
+class Sidebar : public QWidget
+{
+Q_OBJECT
+friend class Splitter;
+public:
+ enum Position {
+ Left,
+ Bottom
+ };
+
+ Sidebar(QWidget *parent, Position position);
+ ~Sidebar();
+
+ /**
+ * Toggle visibility of this sidebar.
+ *
+ * @param visible True if the sidbar should be visible, false otherwise
+ */
+ void setVisible(bool visible);
+
+ /**
+ * Returns the content box of the sidebar. This should be where the widget, that
+ * should be resized when the sidebar resizes, should be placed.
+ *
+ * @return A QVBox container widget
+ */
+ QVBox *content() { return m_content; }
+
+ /**
+ * Returns the sidebar by it's name.
+ *
+ * @param name The sidebar's name
+ * @return A QWidget representing the sidebar or NULL if it is not found
+ */
+ QWidget *sidebar(const QString &name) const;
+
+ /**
+ * Returns the sidebar by it's index.
+ *
+ * @param index The sidebar's index
+ * @return A QWidget representing the sidebar or NULL if the index is invalid
+ */
+ QWidget *sidebar(int index) const { if (index < 0) index = 0; return m_sidebars[index]; }
+
+ /**
+ * Returns the currently open sidebar.
+ *
+ * @return A QWidget representing the current sidebar
+ */
+ QWidget *currentSidebar() const { return sidebar(m_currentIndex); }
+
+ /**
+ * Adds a new sidebar widget.
+ *
+ * @param widget The widget to add
+ * @param title The title that will be displayed on the tab
+ * @param icon Name of the icon that will be displayed besides the title
+ */
+ void addSidebar(QWidget *widget, const QString &title, const QString &icon);
+protected:
+ virtual void polish();
+ virtual bool event(QEvent *e);
+public slots:
+ void showSidebar(const QString &name) { showSidebar(indexForName(name)); }
+ void showSidebar(int index) { if (index != m_currentIndex) showHideSidebar(index); }
+ void showHideSidebar(int);
+ void closeCurrentSidebar() { showHideSidebar(m_currentIndex); }
+private:
+ int indexForName(const QString&) const;
+ void mouseMovedOverSplitter(QMouseEvent *e);
+
+ void adjustWidgetSizes();
+ uint maxSidebarWidth() const { return width() / 2; }
+ uint maxSidebarHeight() const { return height() / 2; }
+
+ Position m_position;
+ uint m_pos;
+
+ QWidget *m_divider;
+ QVBox *m_content;
+ MultiTabBar *m_tabBar;
+ QWidget *m_sidebarBox;
+
+ SidebarList m_sidebars;
+ int m_currentIndex;
+ int m_lastIndex;
+
+ QSignalMapper *m_mapper;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/systemtray.cpp b/kftpgrabber/src/widgets/systemtray.cpp
new file mode 100644
index 0000000..98179b7
--- /dev/null
+++ b/kftpgrabber/src/widgets/systemtray.cpp
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kwin.h>
+
+#include "widgets/systemtray.h"
+#include "widgets/balloon.h"
+#include "mainwindow.h"
+#include "mainactions.h"
+#include "kftpbookmarks.h"
+#include "kftpqueue.h"
+#include "misc.h"
+#include "misc/config.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+SystemTray *SystemTray::m_self = 0L;
+
+SystemTray::SystemTray(MainWindow *parent)
+ : KSystemTray(parent),
+ m_balloon(0),
+ m_actions(parent->getActions())
+{
+ m_self = this;
+
+ // Set icon and show it
+ setPixmap(loadToolbarPixmap("kftpgrabber"));
+
+ if (KFTPCore::Config::showSystrayIcon())
+ show();
+
+ // Add some actions
+ m_bookmarkMenu = new KActionMenu(i18n("Bookmarks"));
+ slotUpdateBookmarks();
+
+ // Let our bookmarks be up to date
+ connect(KFTPBookmarks::Manager::self(), SIGNAL(update()), this, SLOT(slotUpdateBookmarks()));
+
+ m_actions->m_fileConnectAction->plug(contextMenu(), 1);
+ m_bookmarkMenu->plug(contextMenu(), 1);
+
+ // Ensure that we actually quit
+ connect(this, SIGNAL(quitSelected()), this, SLOT(slotQuitSelected()));
+}
+
+SystemTray::~SystemTray()
+{
+ delete m_bookmarkMenu;
+}
+
+void SystemTray::slotQuitSelected()
+{
+ m_actions->m_closeApp = true;
+}
+
+void SystemTray::slotUpdateBookmarks()
+{
+ // Re-create the bookmarks menu
+ m_bookmarkMenu->popupMenu()->clear();
+ KFTPBookmarks::Manager::self()->guiPopulateBookmarksMenu(m_bookmarkMenu, QDomNode(), false);
+}
+
+void SystemTray::showBalloon(const QString &text)
+{
+ // Create a little modified Kopete balloon
+ if (m_balloon) {
+ m_balloon->hide();
+ delete m_balloon;
+ }
+
+ m_balloon = new Balloon("<qt><nobr><b>KFTPGrabber</b><br>" + text + "</nobr></qt>", "info");
+ m_balloon->setAnchor(mapToGlobal(pos()));
+ m_balloon->show();
+ KWin::setOnAllDesktops(m_balloon->winId() , true);
+
+ // Hide the balloon after 5 sec
+ QTimer::singleShot(5000, this, SLOT(slotHideBalloon()));
+}
+
+void SystemTray::slotHideBalloon()
+{
+ m_balloon->hide();
+}
+
+}
+
+#include "systemtray.moc"
diff --git a/kftpgrabber/src/widgets/systemtray.h b/kftpgrabber/src/widgets/systemtray.h
new file mode 100644
index 0000000..ce77811
--- /dev/null
+++ b/kftpgrabber/src/widgets/systemtray.h
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSSYSTEMTRAY_H
+#define KFTPWIDGETSSYSTEMTRAY_H
+
+#include <ksystemtray.h>
+#include <kaction.h>
+
+#include <qmenudata.h>
+
+class MainWindow;
+class MainActions;
+
+namespace KFTPWidgets {
+
+class Balloon;
+
+/**
+ * A system tray icon that is used for some actions.
+ *
+ * @author Jernej Kos
+ */
+class SystemTray : public KSystemTray
+{
+Q_OBJECT
+public:
+ /**
+ * Get the global system tray instance.
+ */
+ static SystemTray *self() { return SystemTray::m_self; }
+
+ /**
+ * Class constructor.
+ */
+ SystemTray(MainWindow *parent);
+
+ /**
+ * Class destructor.
+ */
+ ~SystemTray();
+
+ /**
+ * Show a balloon notification widget.
+ */
+ void showBalloon(const QString &text);
+protected:
+ static SystemTray *m_self;
+private:
+ KActionMenu *m_bookmarkMenu;
+ Balloon *m_balloon;
+ MainActions *m_actions;
+private slots:
+ void slotHideBalloon();
+ void slotUpdateBookmarks();
+ void slotQuitSelected();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/trafficgraph.cpp b/kftpgrabber/src/widgets/trafficgraph.cpp
new file mode 100644
index 0000000..ddd339f
--- /dev/null
+++ b/kftpgrabber/src/widgets/trafficgraph.cpp
@@ -0,0 +1,628 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2004 by the KFTPGrabber developers
+ * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <klocale.h>
+
+#include "trafficgraph.h"
+
+namespace KFTPWidgets {
+
+static inline int min(int a, int b)
+{
+ return (a < b ? a : b);
+}
+
+TrafficGraph::TrafficGraph(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ // Auto deletion does not work for pointer to arrays.
+ m_beamData.setAutoDelete(false);
+
+ setBackgroundMode(NoBackground);
+
+ m_samples = 0;
+ m_minValue = m_maxValue = 0.0;
+ m_useAutoRange = true;
+
+ m_graphStyle = GRAPH_POLYGON;
+
+ // Anything smaller than this does not make sense.
+ setMinimumSize(16, 100);
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
+ QSizePolicy::Expanding, false));
+
+ m_showVerticalLines = true;
+ m_verticalLinesColor = QColor(0x04FB1D);
+ m_verticalLinesDistance = 30;
+ m_verticalLinesScroll = true;
+ m_verticalLinesOffset = 0;
+ m_horizontalScale = 1;
+
+ m_showHorizontalLines = true;
+ m_horizontalLinesColor = QColor(0x04FB1D);
+ m_horizontalLinesCount = 5;
+
+ m_showLabels = true;
+ m_showTopBar = false;
+ m_fontSize = 8;
+
+ m_backgroundColor = QColor(0x313031);
+}
+
+
+TrafficGraph::~TrafficGraph()
+{
+ for (double* p = m_beamData.first(); p; p = m_beamData.next())
+ delete [] p;
+}
+
+bool TrafficGraph::addBeam(const QColor &color)
+{
+ double* d = new double[m_samples];
+ memset(d, 0, sizeof(double) * m_samples);
+ m_beamData.append(d);
+ m_beamColor.append(color);
+
+ return true;
+}
+
+void TrafficGraph::addSample(const QValueList<double>& sampleBuf)
+{
+ if (m_beamData.count() != sampleBuf.count())
+ return;
+
+ double* d;
+ if (m_useAutoRange) {
+ double sum = 0;
+ for (d = m_beamData.first(); d; d = m_beamData.next()) {
+ sum += d[0];
+ if (sum < m_minValue)
+ m_minValue = sum;
+ if (sum > m_maxValue)
+ m_maxValue = sum;
+ }
+ }
+
+ /* If the vertical lines are scrolling, increment the offset
+ * so they move with the data. The vOffset / hScale confusion
+ * is because v refers to Vertical Lines, and h to the horizontal
+ * distance between the vertical lines. */
+ if (m_verticalLinesScroll) {
+ m_verticalLinesOffset = (m_verticalLinesOffset + m_horizontalScale)
+ % m_verticalLinesDistance;
+ }
+
+ // Shift data buffers one sample down and insert new samples.
+ QValueList<double>::ConstIterator s;
+ for (d = m_beamData.first(), s = sampleBuf.begin(); d; d = m_beamData.next(), ++s) {
+ memmove(d, d + 1, (m_samples - 1) * sizeof(double));
+ d[m_samples - 1] = *s;
+ }
+
+ update();
+}
+
+void TrafficGraph::changeRange(int beam, double min, double max)
+{
+ // Only the first beam affects range calculation.
+ if (beam > 1)
+ return;
+
+ m_minValue = min;
+ m_maxValue = max;
+}
+
+QValueList<QColor> &TrafficGraph::beamColors()
+{
+ return m_beamColor;
+}
+
+void TrafficGraph::removeBeam(uint pos)
+{
+ m_beamColor.remove(m_beamColor.at(pos));
+ m_beamData.remove(pos);
+}
+
+void TrafficGraph::setUseAutoRange(bool value)
+{
+ m_useAutoRange = value;
+}
+
+bool TrafficGraph::useAutoRange() const
+{
+ return m_useAutoRange;
+}
+
+void TrafficGraph::setMinValue(double min)
+{
+ m_minValue = min;
+}
+
+double TrafficGraph::minValue() const
+{
+ return (m_useAutoRange ? 0 : m_minValue);
+}
+
+void TrafficGraph::setMaxValue(double max)
+{
+ m_maxValue = max;
+}
+
+double TrafficGraph::maxValue() const
+{
+ return (m_useAutoRange ? 0 : m_maxValue);
+}
+
+void TrafficGraph::setGraphStyle(uint style)
+{
+ m_graphStyle = style;
+}
+
+uint TrafficGraph::graphStyle() const
+{
+ return m_graphStyle;
+}
+
+void TrafficGraph::setHorizontalScale(uint scale)
+{
+ if (scale == m_horizontalScale)
+ return;
+
+ m_horizontalScale = scale;
+ if (isVisible())
+ updateDataBuffers();
+}
+
+uint TrafficGraph::horizontalScale() const
+{
+ return m_horizontalScale;
+}
+
+void TrafficGraph::setShowVerticalLines(bool value)
+{
+ m_showVerticalLines = value;
+}
+
+bool TrafficGraph::showVerticalLines() const
+{
+ return m_showVerticalLines;
+}
+
+void TrafficGraph::setVerticalLinesColor(const QColor &color)
+{
+ m_verticalLinesColor = color;
+}
+
+QColor TrafficGraph::verticalLinesColor() const
+{
+ return m_verticalLinesColor;
+}
+
+void TrafficGraph::setVerticalLinesDistance(int distance)
+{
+ m_verticalLinesDistance = distance;
+}
+
+int TrafficGraph::verticalLinesDistance() const
+{
+ return m_verticalLinesDistance;
+}
+
+void TrafficGraph::setVerticalLinesScroll(bool value)
+{
+ m_verticalLinesScroll = value;
+}
+
+bool TrafficGraph::verticalLinesScroll() const
+{
+ return m_verticalLinesScroll;
+}
+
+void TrafficGraph::setShowHorizontalLines(bool value)
+{
+ m_showHorizontalLines = value;
+}
+
+bool TrafficGraph::showHorizontalLines() const
+{
+ return m_showHorizontalLines;
+}
+
+void TrafficGraph::setHorizontalLinesColor(const QColor &color)
+{
+ m_horizontalLinesColor = color;
+}
+
+QColor TrafficGraph::horizontalLinesColor() const
+{
+ return m_horizontalLinesColor;
+}
+
+void TrafficGraph::setHorizontalLinesCount(int count)
+{
+ m_horizontalLinesCount = count;
+}
+
+int TrafficGraph::horizontalLinesCount() const
+{
+ return m_horizontalLinesCount;
+}
+
+void TrafficGraph::setShowLabels(bool value)
+{
+ m_showLabels = value;
+}
+
+bool TrafficGraph::showLabels() const
+{
+ return m_showLabels;
+}
+
+void TrafficGraph::setShowTopBar(bool value)
+{
+ m_showTopBar = value;
+}
+
+bool TrafficGraph::showTopBar() const
+{
+ return m_showTopBar;
+}
+
+void TrafficGraph::setFontSize(int size)
+{
+ m_fontSize = size;
+}
+
+int TrafficGraph::fontSize() const
+{
+ return m_fontSize;
+}
+
+void TrafficGraph::setBackgroundColor(const QColor &color)
+{
+ m_backgroundColor = color;
+}
+
+QColor TrafficGraph::backgroundColor() const
+{
+ return m_backgroundColor;
+}
+
+void TrafficGraph::resizeEvent(QResizeEvent*)
+{
+ updateDataBuffers();
+}
+
+void TrafficGraph::updateDataBuffers()
+{
+ /* Since the data buffers for the beams are equal in size to the
+ * width of the widget minus 2 we have to enlarge or shrink the
+ * buffers accordingly when a resize occures. To have a nicer
+ * display we try to keep as much data as possible. Data that is
+ * lost due to shrinking the buffers cannot be recovered on
+ * enlarging though. */
+
+ /* Determine new number of samples first.
+ * +0.5 to ensure rounding up
+ * +2 for extra data points so there is
+ * 1) no wasted space and
+ * 2) no loss of precision when drawing the first data point. */
+ uint newSampleNum = static_cast<uint>(((width() - 2 ) / m_horizontalScale) + 2.5);
+
+ // overlap between the old and the new buffers.
+ int overlap = min(m_samples, newSampleNum);
+
+ for (uint i = 0; i < m_beamData.count(); ++i) {
+ double* nd = new double[newSampleNum];
+
+ // initialize new part of the new buffer
+ if (newSampleNum > (uint) overlap)
+ memset(nd, 0, sizeof(double) * (newSampleNum - overlap));
+
+ // copy overlap from old buffer to new buffer
+ memcpy(nd + (newSampleNum - overlap), m_beamData.at(i) + (m_samples - overlap), overlap * sizeof(double));
+
+ m_beamData.remove(i);
+ m_beamData.insert(i, nd);
+ }
+
+ m_samples = newSampleNum;
+}
+
+void TrafficGraph::paintEvent(QPaintEvent*)
+{
+ uint w = width();
+ uint h = height();
+
+ /* Do not do repaints when the widget is not yet setup properly. */
+ if (w <= 2)
+ return;
+
+ QPixmap pm(w, h);
+ QPainter p;
+ p.begin(&pm, this);
+
+ pm.fill(m_backgroundColor);
+ /* Draw white line along the bottom and the right side of the
+ * widget to create a 3D like look. */
+ p.setPen(QColor(colorGroup().light()));
+ p.drawLine(0, h - 1, w - 1, h - 1);
+ p.drawLine(w - 1, 0, w - 1, h - 1);
+
+ p.setClipRect(1, 1, w - 2, h - 2);
+ double range = m_maxValue - m_minValue;
+
+ /* If the range is too small we will force it to 1.0 since it
+ * looks a lot nicer. */
+ if (range < 0.000001)
+ range = 1.0;
+
+ double minValue = m_minValue;
+ if (m_useAutoRange) {
+ if (m_minValue != 0.0) {
+ double dim = pow(10, floor(log10(fabs(m_minValue )))) / 2;
+ if (m_minValue < 0.0)
+ minValue = dim * floor(m_minValue / dim);
+ else
+ minValue = dim * ceil(m_minValue / dim);
+ range = m_maxValue - minValue;
+ if (range < 0.000001)
+ range = 1.0;
+ }
+
+ // Massage the range so that the grid shows some nice values.
+ double step = range / m_horizontalLinesCount;
+ double dim = pow(10, floor(log10(step))) / 2;
+ range = dim * ceil(step / dim) * m_horizontalLinesCount;
+ }
+
+ double maxValue = minValue + range;
+
+ int top = 0;
+ if (m_showTopBar && h > (m_fontSize + 2 + m_horizontalLinesCount * 10)) {
+ /* Draw horizontal bar with current sensor values at top of display. */
+ p.setPen(m_horizontalLinesColor);
+ int x0 = w / 2;
+ p.setFont(QFont(p.font().family(), m_fontSize));
+ top = p.fontMetrics().height();
+ h -= top;
+ int h0 = top - 2;
+ p.drawText(0, 0, x0, top - 2, Qt::AlignCenter, i18n("Bandwidth usage"));
+
+ p.drawLine(x0 - 1, 1, x0 - 1, h0);
+ p.drawLine(0, top - 1, w - 2, top - 1);
+
+ double bias = -minValue;
+ double scaleFac = ( w - x0 - 2 ) / range;
+ QValueList<QColor>::Iterator col;
+ col = m_beamColor.begin();
+
+ for (double *d = m_beamData.first(); d; d = m_beamData.next(), ++col) {
+ int start = x0 + (int) (bias * scaleFac);
+ int end = x0 + (int) ((bias += d[ w - 3 ]) * scaleFac);
+
+ /* If the rect is wider than 2 pixels we draw only the last
+ * pixels with the bright color. The rest is painted with
+ * a 50% darker color. */
+ if (end - start > 1) {
+ p.setPen((*col).dark(150));
+ p.setBrush((*col).dark(150));
+ p.drawRect(start, 1, end - start, h0);
+ p.setPen(*col);
+ p.drawLine(end, 1, end, h0);
+ } else if (start - end > 1) {
+ p.setPen((*col).dark(150));
+ p.setBrush((*col).dark(150));
+ p.drawRect(end, 1, start - end, h0);
+ p.setPen(*col);
+ p.drawLine(end, 1, end, h0);
+ } else {
+ p.setPen(*col);
+ p.drawLine(start, 1, start, h0);
+ }
+ }
+ }
+
+ /* Draw scope-like grid vertical lines */
+ if (m_showVerticalLines && w > 60) {
+ p.setPen(m_verticalLinesColor);
+ for (uint x = m_verticalLinesOffset; x < (w - 2); x += m_verticalLinesDistance)
+ p.drawLine(w - x, top, w - x, h + top - 2);
+ }
+
+ /* In autoRange mode we determine the range and plot the values in
+ * one go. This is more efficiently than running through the
+ * buffers twice but we do react on recently discarded samples as
+ * well as new samples one plot too late. So the range is not
+ * correct if the recently discarded samples are larger or smaller
+ * than the current extreme values. But we can probably live with
+ * this. */
+ if (m_useAutoRange)
+ m_minValue = m_maxValue = 0.0;
+
+ /* Plot stacked values */
+ double scaleFac = (h - 2) / range;
+ if (m_graphStyle == GRAPH_ORIGINAL) {
+ int xPos = 0;
+
+ for (int i = 0; i < m_samples; i++, xPos += m_horizontalScale) {
+ double bias = -minValue;
+ QValueList<QColor>::Iterator col;
+ col = m_beamColor.begin();
+ double sum = 0.0;
+
+ for (double *d = m_beamData.first(); d; d = m_beamData.next(), ++col) {
+ if (m_useAutoRange) {
+ sum += d[i];
+ if (sum < m_minValue)
+ m_minValue = sum;
+ if (sum > m_maxValue)
+ m_maxValue = sum;
+ }
+
+ int start = top + h - 2 - (int) (bias * scaleFac);
+ int end = top + h - 2 - (int) ((bias + d[ i ] ) * scaleFac);
+ bias += d[i];
+
+ /* If the line is longer than 2 pixels we draw only the last
+ * 2 pixels with the bright color. The rest is painted with
+ * a 50% darker color. */
+ if (end - start > 2) {
+ p.fillRect(xPos, start, m_horizontalScale, end - start - 1, (*col).dark(150));
+ p.fillRect(xPos, end - 1, m_horizontalScale, 2, *col);
+ } else if (start - end > 2) {
+ p.fillRect(xPos, start, m_horizontalScale, end - start + 1, (*col).dark(150));
+ p.fillRect(xPos, end + 1, m_horizontalScale, 2, *col);
+ } else
+ p.fillRect(xPos, start, m_horizontalScale, end - start, *col);
+
+ }
+ }
+ } else if (m_graphStyle == GRAPH_POLYGON) {
+ int *prevVals = new int[m_beamData.count()];
+ int hack[4];
+ int x1 = w - ((m_samples + 1) * m_horizontalScale);
+
+ for (int i = 0; i < m_samples; i++) {
+ QValueList<QColor>::Iterator col;
+ col = m_beamColor.begin();
+ double sum = 0.0;
+ int y = top + h - 2;
+ int oldY = top + h;
+ int oldPrevY = oldY;
+ int height = 0;
+ int j = 0;
+ int jMax = m_beamData.count() - 1;
+ x1 += m_horizontalScale;
+ int x2 = x1 + m_horizontalScale;
+
+ for (double *d = m_beamData.first(); d; d = m_beamData.next(), ++col, j++) {
+ if (m_useAutoRange) {
+ sum += d[i];
+
+ if ( sum < m_minValue )
+ m_minValue = sum;
+ if ( sum > m_maxValue )
+ m_maxValue = sum;
+ }
+
+ height = (int) ((d[i] - minValue) * scaleFac);
+ y -= height;
+
+ /* If the line is longer than 2 pixels we draw only the last
+ * 2 pixels with the bright color. The rest is painted with
+ * a 50% darker color. */
+ QPen lastPen = QPen(p.pen());
+ p.setPen((*col).dark(150));
+ p.setBrush((*col).dark(150));
+ QPointArray pa(4);
+ int prevY = (i == 0) ? y : prevVals[j];
+ pa.putPoints(0, 1, x1, prevY);
+ pa.putPoints(1, 1, x2, y);
+ pa.putPoints(2, 1, x2, oldY);
+ pa.putPoints(3, 1, x1, oldPrevY);
+ p.drawPolygon(pa);
+ p.setPen(lastPen);
+ if (jMax == 0) {
+ // draw as normal, no deferred drawing req'd.
+ p.setPen(*col);
+ p.drawLine(x1, prevY, x2, y);
+ } else if (j == jMax) {
+ // draw previous values and current values
+ p.drawLine(hack[0], hack[1], hack[2], hack[3]);
+ p.setPen(*col);
+ p.drawLine(x1, prevY, x2, y);
+ } else if (j == 0) {
+ // save values only
+ hack[0] = x1;
+ hack[1] = prevY;
+ hack[2] = x2;
+ hack[3] = y;
+ p.setPen(*col);
+ } else {
+ p.drawLine(hack[0], hack[1], hack[2], hack[3]);
+ hack[0] = x1;
+ hack[1] = prevY;
+ hack[2] = x2;
+ hack[3] = y;
+ p.setPen(*col);
+ }
+
+ prevVals[j] = y;
+ oldY = y;
+ oldPrevY = prevY;
+ }
+ }
+
+ delete[] prevVals;
+ }
+
+ /* Draw horizontal lines and values. Lines are drawn when the
+ * height is greater than 10 times hCount + 1, values are shown
+ * when width is greater than 60 */
+ if (m_showHorizontalLines && h > (10 * (m_horizontalLinesCount + 1))) {
+ p.setPen(m_horizontalLinesColor);
+ p.setFont(QFont(p.font().family(), m_fontSize));
+ QString val;
+
+ for (uint y = 1; y < m_horizontalLinesCount; y++) {
+ p.drawLine(0, top + y * (h / m_horizontalLinesCount), w - 2, top + y * (h / m_horizontalLinesCount));
+
+ if (m_showLabels && h > (m_fontSize + 1) * (m_horizontalLinesCount + 1) && w > 60 ) {
+ val = QString("%1").arg(maxValue - y * (range / m_horizontalLinesCount));
+ p.drawText(6, top + y * (h / m_horizontalLinesCount) - 1, val);
+ }
+ }
+
+ if (m_showLabels && h > (m_fontSize + 1) * (m_horizontalLinesCount + 1) && w > 60) {
+ val = QString("%1").arg(minValue);
+ p.drawText(6, top + h - 2, val);
+ }
+ }
+
+ p.end();
+ bitBlt(this, 0, 0, &pm);
+}
+
+}
+
+#include "trafficgraph.moc"
diff --git a/kftpgrabber/src/widgets/trafficgraph.h b/kftpgrabber/src/widgets/trafficgraph.h
new file mode 100644
index 0000000..3822165
--- /dev/null
+++ b/kftpgrabber/src/widgets/trafficgraph.h
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPTRAFFICGRAPH_H
+#define KFTPTRAFFICGRAPH_H
+
+#include <qpoint.h>
+#include <qdialog.h>
+#include <qptrlist.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+#define GRAPH_POLYGON 0
+#define GRAPH_ORIGINAL 1
+
+class QColor;
+
+namespace KFTPWidgets {
+
+/**
+@author Jernej Kos
+*/
+class TrafficGraph : public QWidget
+{
+Q_OBJECT
+public:
+ TrafficGraph(QWidget *parent = 0, const char *name = 0);
+ ~TrafficGraph();
+
+ bool addBeam(const QColor &color);
+ void addSample(const QValueList<double> &samples);
+
+ void removeBeam(uint pos);
+
+ void changeRange(int beam, double min, double max);
+
+ QValueList<QColor> &beamColors();
+
+ void setUseAutoRange(bool value);
+ bool useAutoRange() const;
+
+ void setMinValue(double min);
+ double minValue() const;
+
+ void setMaxValue(double max);
+ double maxValue() const;
+
+ void setGraphStyle(uint style);
+ uint graphStyle() const;
+
+ void setHorizontalScale(uint scale);
+ uint horizontalScale() const;
+
+ void setShowVerticalLines(bool value);
+ bool showVerticalLines() const;
+
+ void setVerticalLinesColor(const QColor &color);
+ QColor verticalLinesColor() const;
+
+ void setVerticalLinesDistance(int distance);
+ int verticalLinesDistance() const;
+
+ void setVerticalLinesScroll(bool value);
+ bool verticalLinesScroll() const;
+
+ void setShowHorizontalLines(bool value);
+ bool showHorizontalLines() const;
+
+ void setHorizontalLinesColor(const QColor &color);
+ QColor horizontalLinesColor() const;
+
+ void setHorizontalLinesCount(int count);
+ int horizontalLinesCount() const;
+
+ void setShowLabels(bool value);
+ bool showLabels() const;
+
+ void setShowTopBar(bool value);
+ bool showTopBar() const;
+
+ void setFontSize(int size);
+ int fontSize() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+protected:
+ void updateDataBuffers();
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void paintEvent(QPaintEvent*);
+private:
+ double m_minValue;
+ double m_maxValue;
+ bool m_useAutoRange;
+
+ uint m_graphStyle;
+
+ bool m_showVerticalLines;
+ QColor m_verticalLinesColor;
+ uint m_verticalLinesDistance;
+ bool m_verticalLinesScroll;
+ uint m_verticalLinesOffset;
+ uint m_horizontalScale;
+
+ bool m_showHorizontalLines;
+ QColor m_horizontalLinesColor;
+ uint m_horizontalLinesCount;
+
+ bool m_showLabels;
+ bool m_showTopBar;
+ uint m_fontSize;
+
+ QColor m_backgroundColor;
+
+ QPtrList<double> m_beamData;
+ QValueList<QColor> m_beamColor;
+
+ int m_samples;
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/verifier.cpp b/kftpgrabber/src/widgets/verifier.cpp
new file mode 100644
index 0000000..1f6c9d9
--- /dev/null
+++ b/kftpgrabber/src/widgets/verifier.cpp
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#include "verifier.h"
+#include "misc.h"
+#include "listviewitem.h"
+
+#include <klocale.h>
+#include <klistview.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <kurl.h>
+
+#include <qheader.h>
+#include <qlabel.h>
+
+// UI layouts
+#include "ui/checksum_verifier.h"
+
+using namespace KFTPGrabberBase;
+
+namespace KFTPWidgets {
+
+Verifier::Verifier(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Checksum verifier"), Cancel, Cancel, true),
+ m_verifier(0)
+{
+ m_layout = new VerifierLayout(this);
+ setMainWidget(m_layout);
+
+ // Create columns
+ m_layout->fileList->addColumn(i18n("Filename"));
+ m_layout->fileList->addColumn(i18n("Checksum"), 100);
+
+ m_layout->fileList->setAllColumnsShowFocus(true);
+ m_layout->fileList->header()->setStretchEnabled(true, 0);
+}
+
+Verifier::~Verifier()
+{
+ if (m_verifier)
+ delete m_verifier;
+}
+
+void Verifier::setFile(const QString &filename)
+{
+ // Create the verifier
+ m_verifier = new KFTPCore::ChecksumVerifier(filename);
+ m_layout->currentFile->setText(KURL(filename).fileName());
+
+ connect(m_verifier, SIGNAL(fileList(QValueList<QPair<QString, QString> >)), this, SLOT(slotHaveFileList(QValueList<QPair<QString, QString> >)));
+ connect(m_verifier, SIGNAL(fileDone(const QString&, KFTPCore::ChecksumVerifier::Result)), this, SLOT(slotFileDone(const QString&, KFTPCore::ChecksumVerifier::Result)));
+ connect(m_verifier, SIGNAL(progress(int)), this, SLOT(slotProgress(int)));
+ connect(m_verifier, SIGNAL(error()), this, SLOT(slotError()));
+
+ // Start the verification
+ m_verifier->verify();
+}
+
+void Verifier::slotHaveFileList(QValueList<QPair<QString, QString> > list)
+{
+ for (QValueList<QPair<QString, QString> >::iterator i = list.begin(); i != list.end(); ++i) {
+ KFTPWidgets::ListViewItem *item = new KFTPWidgets::ListViewItem(m_layout->fileList);
+ item->setText(0, (*i).first);
+ item->setText(1, (*i).second);
+ }
+}
+
+void Verifier::slotFileDone(const QString &filename, KFTPCore::ChecksumVerifier::Result result)
+{
+ KFTPWidgets::ListViewItem *item = static_cast<KFTPWidgets::ListViewItem*>(m_layout->fileList->findItem(filename, 0));
+
+ if (item) {
+ switch (result) {
+ case KFTPCore::ChecksumVerifier::Ok: {
+ item->setPixmap(0, loadSmallPixmap("ok"));
+ item->setTextColor(0, QColor(0, 200, 0));
+ item->setTextColor(1, QColor(0, 200, 0));
+ break;
+ }
+ case KFTPCore::ChecksumVerifier::NotFound: {
+ item->setPixmap(0, loadSmallPixmap("error"));
+ item->setTextColor(0, QColor(128, 128, 128));
+ item->setTextColor(1, QColor(128, 128, 128));
+ break;
+ }
+ case KFTPCore::ChecksumVerifier::Error: {
+ item->setPixmap(0, loadSmallPixmap("error"));
+ item->setTextColor(0, QColor(255, 0, 0));
+ item->setTextColor(1, QColor(255, 0, 0));
+ break;
+ }
+ }
+
+ m_layout->fileList->ensureItemVisible(item);
+ }
+}
+
+void Verifier::slotProgress(int percent)
+{
+ m_layout->checkProgress->setProgress(percent);
+
+ if (percent == 100) {
+ KMessageBox::information(0, i18n("Verification complete!"));
+ }
+}
+
+void Verifier::slotError()
+{
+ KMessageBox::error(0, i18n("Unable to open checksum file or file has an incorrect format!"));
+ close();
+}
+
+}
+#include "verifier.moc"
diff --git a/kftpgrabber/src/widgets/verifier.h b/kftpgrabber/src/widgets/verifier.h
new file mode 100644
index 0000000..6f1ee1e
--- /dev/null
+++ b/kftpgrabber/src/widgets/verifier.h
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2005 by the KFTPGrabber developers
+ * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+#ifndef KFTPWIDGETSVERIFIER_H
+#define KFTPWIDGETSVERIFIER_H
+
+#include "checksumverifier.h"
+
+#include <kdialogbase.h>
+
+#include <qvaluelist.h>
+#include <qpair.h>
+
+class VerifierLayout;
+
+namespace KFTPWidgets {
+
+/**
+ * @author Jernej Kos
+ */
+class Verifier : public KDialogBase
+{
+Q_OBJECT
+public:
+ Verifier(QWidget *parent = 0, const char *name = 0);
+ ~Verifier();
+
+ void setFile(const QString &filename);
+private:
+ VerifierLayout *m_layout;
+ KFTPCore::ChecksumVerifier *m_verifier;
+private slots:
+ void slotHaveFileList(QValueList<QPair<QString, QString> > list);
+ void slotFileDone(const QString &filename, KFTPCore::ChecksumVerifier::Result result);
+ void slotProgress(int percent);
+ void slotError();
+};
+
+}
+
+#endif
diff --git a/kftpgrabber/src/widgets/widgetlister.cpp b/kftpgrabber/src/widgets/widgetlister.cpp
new file mode 100644
index 0000000..55022d2
--- /dev/null
+++ b/kftpgrabber/src/widgets/widgetlister.cpp
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#include "widgetlister.h"
+
+#include <klocale.h>
+#include <kguiitem.h>
+#include <kpushbutton.h>
+#include <kdialog.h>
+
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qhbox.h>
+
+namespace KFTPWidgets {
+
+WidgetLister::WidgetLister(QWidget *parent, int minWidgets, int maxWidgets)
+ : QWidget(parent)
+{
+ m_widgetList.setAutoDelete(true);
+
+ m_minWidgets = QMAX(minWidgets, 0);
+ m_maxWidgets = QMAX(maxWidgets, m_minWidgets + 1);
+
+ // The button box
+ m_layout = new QVBoxLayout(this, 0, 4);
+ m_buttonBox = new QHBox(this);
+ m_buttonBox->setSpacing(KDialog::spacingHint());
+ m_layout->addWidget(m_buttonBox);
+
+ m_buttonMore = new KPushButton(KGuiItem(i18n("more widgets", "More"), "button_more"), m_buttonBox);
+ m_buttonBox->setStretchFactor(m_buttonMore, 0);
+
+ m_buttonFewer = new KPushButton(KGuiItem(i18n("fewer widgets", "Fewer"), "button_fewer"), m_buttonBox);
+ m_buttonBox->setStretchFactor(m_buttonFewer, 0);
+
+ QWidget *spacer = new QWidget(m_buttonBox);
+ m_buttonBox->setStretchFactor(spacer, 1);
+
+ m_buttonClear = new KPushButton(KGuiItem(i18n("clear widgets", "Clear"), "locationbar_erase"), m_buttonBox);
+ m_buttonBox->setStretchFactor(m_buttonClear, 0);
+
+ // Connect signals
+ connect(m_buttonMore, SIGNAL(clicked()), this, SLOT(slotMore()));
+ connect(m_buttonFewer, SIGNAL(clicked()), this, SLOT(slotFewer()));
+ connect(m_buttonClear, SIGNAL(clicked()), this, SLOT(slotClear()));
+
+ enableControls();
+}
+
+WidgetLister::~WidgetLister()
+{
+}
+
+void WidgetLister::slotMore()
+{
+ addWidget();
+ enableControls();
+}
+
+void WidgetLister::slotFewer()
+{
+ removeWidget();
+ enableControls();
+}
+
+void WidgetLister::clear()
+{
+ setNumberShown(m_minWidgets);
+
+ // Clear remaining widgets
+ QPtrListIterator<QWidget> i(m_widgetList);
+ for (i.toFirst(); i.current(); ++i)
+ clearWidget((*i));
+
+ enableControls();
+ emit clearWidgets();
+}
+
+void WidgetLister::slotClear()
+{
+ clear();
+}
+
+void WidgetLister::addWidget(QWidget *widget)
+{
+ if (!widget)
+ widget = createWidget(this);
+
+ m_layout->insertWidget(m_layout->findWidget(m_buttonBox), widget);
+ m_widgetList.append(widget);
+ widget->show();
+
+ enableControls();
+ emit widgetAdded(widget);
+}
+
+void WidgetLister::removeWidget()
+{
+ m_widgetList.removeLast();
+
+ enableControls();
+ emit widgetRemoved();
+}
+
+void WidgetLister::clearWidget(QWidget *widget)
+{
+ Q_UNUSED(widget)
+}
+
+QWidget *WidgetLister::createWidget(QWidget* parent)
+{
+ return new QWidget(parent);
+}
+
+void WidgetLister::setNumberShown(int number)
+{
+ int superfluousWidgets = QMAX((int) m_widgetList.count() - number, 0);
+ int missingWidgets = QMAX(number - (int) m_widgetList.count(), 0);
+
+ // Remove superfluous widgets
+ for (; superfluousWidgets; superfluousWidgets--)
+ removeWidget();
+
+ // Add missing widgets
+ for (; missingWidgets; missingWidgets--)
+ addWidget();
+}
+
+void WidgetLister::enableControls()
+{
+ int count = m_widgetList.count();
+ bool isMaxWidgets = (count >= m_maxWidgets);
+ bool isMinWidgets = (count <= m_minWidgets);
+
+ m_buttonMore->setEnabled(!isMaxWidgets);
+ m_buttonFewer->setEnabled(!isMinWidgets);
+}
+
+}
diff --git a/kftpgrabber/src/widgets/widgetlister.h b/kftpgrabber/src/widgets/widgetlister.h
new file mode 100644
index 0000000..8aac58f
--- /dev/null
+++ b/kftpgrabber/src/widgets/widgetlister.h
@@ -0,0 +1,177 @@
+/*
+ * This file is part of the KFTPGrabber project
+ *
+ * Copyright (C) 2003-2006 by the KFTPGrabber developers
+ * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ *
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+#ifndef KFTPWIDGETSWIDGETLISTER_H
+#define KFTPWIDGETSWIDGETLISTER_H
+
+#include <qwidget.h>
+#include <qptrlist.h>
+
+class QPushButton;
+class QVBoxLayout;
+class QHBox;
+
+namespace KFTPWidgets {
+
+/**
+ * This class has been adopted from KDEPIM with slight functional and style
+ * changes.
+ *
+ * @author Jernej Kos
+ * @author Marc Mutz <marc@mutz.com>
+ */
+class WidgetLister : public QWidget
+{
+Q_OBJECT
+public:
+ /**
+ * Class constructor.
+ *
+ * @param parent Parent widget
+ * @param minWidgets Minimum number of widgets in the list
+ * @param maxWidgets Maximum number of widgets in the list
+ */
+ WidgetLister(QWidget* parent, int minWidgets, int maxWidgets);
+
+ /**
+ * Class destructor.
+ */
+ virtual ~WidgetLister();
+
+ /**
+ * Clears all widgets.
+ */
+ void clear();
+
+ /**
+ * Sets the number of currently shown widgets on screen.
+ *
+ * @param number The number of widgets that should be visible
+ */
+ virtual void setNumberShown(int number);
+protected slots:
+ /**
+ * Called whenever the user clicks on the 'more' button. Reimplementations
+ * should call this method, because this implementation does all the dirty
+ * work with adding the widgets to the layout (through @ref addWidget)
+ * and enabling/disabling the control buttons.
+ */
+ virtual void slotMore();
+
+ /**
+ * Called whenever the user clicks on the 'fewer' button. Reimplementations
+ * should call this method, because this implementation does all the dirty
+ * work with removing the widgets from the layout (through @ref removeWidget)
+ * and enabling/disabling the control buttons.
+ */
+ virtual void slotFewer();
+
+ /**
+ * Called whenever the user clicks on the 'clear' button. Reimplementations
+ * should call this method, because this implementation does all the dirty
+ * work with removing all but @ref m_minWidgets widgets from the layout and
+ * enabling/disabling the control buttons.
+ */
+ virtual void slotClear();
+protected:
+ /**
+ * Adds a single widget. Doesn't care if there are already @ref m_maxWidgets
+ * on screen and whether it should enable/disable any controls. It simply does
+ * what it is asked to do. You want to reimplement this method if you want to
+ * initialize the the widget when showing it on screen. Make sure you call this
+ * implementaion, though, since you cannot put the widget on screen from derived
+ * classes (@p m_layout is private). Make sure the parent of the QWidget to add is
+ * this WidgetLister.
+ *
+ * @param widget The widget that should be added
+ */
+ virtual void addWidget(QWidget *widget = 0);
+
+ /**
+ * Removes a single (always the last) widget. Doesn't care if there are still only
+ * @ref m_minWidgets left on screen and whether it should enable/disable any controls.
+ * It simply does what it is asked to do. You want to reimplement this method if you
+ * want to save the the widget's state before removing it from screen. Make sure you
+ * call this implementaion, though, since you should not remove the widget from
+ * screen from derived classes.
+ */
+ virtual void removeWidget();
+
+ /**
+ * Called to clear a given widget. The default implementation does nothing.
+ *
+ * @param widget The widget that should be cleared
+ */
+ virtual void clearWidget(QWidget *widget);
+
+ /**
+ * This method should return a new widget to add to the widget list.
+ *
+ * @param parent The parent widget
+ * @return A valid QWidget
+ */
+ virtual QWidget *createWidget(QWidget *parent);
+protected:
+ QPtrList<QWidget> m_widgetList;
+ int m_minWidgets;
+ int m_maxWidgets;
+signals:
+ /**
+ * This signal is emitted whenever a widget gets added.
+ */
+ void widgetAdded(QWidget *widget);
+
+ /**
+ * This signal is emitted whenever a widget gets removed.
+ */
+ void widgetRemoved();
+
+ /**
+ * This signal is emitted whenever the clear button is clicked.
+ */
+ void clearWidgets();
+private:
+ void enableControls();
+
+ QPushButton *m_buttonMore;
+ QPushButton *m_buttonFewer;
+ QPushButton *m_buttonClear;
+ QVBoxLayout *m_layout;
+ QHBox *m_buttonBox;
+};
+
+}
+
+#endif