diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2013-07-24 15:21:35 -0500 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2013-07-24 15:21:35 -0500 |
commit | 093ed975800ab1e5c0d73759f07fedf8d5aa2ca6 (patch) | |
tree | d625ba687b185a3984a410f56d9c2e0dade7b114 /kftpgrabber | |
download | kftpgrabber-093ed975800ab1e5c0d73759f07fedf8d5aa2ca6.tar.gz kftpgrabber-093ed975800ab1e5c0d73759f07fedf8d5aa2ca6.zip |
Initial import of kftpgrabber 0.8.1 from sources
Diffstat (limited to 'kftpgrabber')
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 ¶m1) +{ + // 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 ¶m1, const QString ¶m2) +{ + 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 ¶m1 = 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 ¶m1 = 0, const QString ¶m2 = 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 Binary files differnew file mode 100644 index 0000000..d529947 --- /dev/null +++ b/kftpgrabber/src/hi16-app-kftpgrabber.png diff --git a/kftpgrabber/src/hi22-app-kftpgrabber.png b/kftpgrabber/src/hi22-app-kftpgrabber.png Binary files differnew file mode 100644 index 0000000..e58a6b6 --- /dev/null +++ b/kftpgrabber/src/hi22-app-kftpgrabber.png diff --git a/kftpgrabber/src/hi32-app-kftpgrabber.png b/kftpgrabber/src/hi32-app-kftpgrabber.png Binary files differnew file mode 100644 index 0000000..ebdef81 --- /dev/null +++ b/kftpgrabber/src/hi32-app-kftpgrabber.png diff --git a/kftpgrabber/src/hi48-app-kftpgrabber.png b/kftpgrabber/src/hi48-app-kftpgrabber.png Binary files differnew file mode 100644 index 0000000..d26533c --- /dev/null +++ b/kftpgrabber/src/hi48-app-kftpgrabber.png diff --git a/kftpgrabber/src/hi64-app-kftpgrabber.png b/kftpgrabber/src/hi64-app-kftpgrabber.png Binary files differnew file mode 100644 index 0000000..9cd1acd --- /dev/null +++ b/kftpgrabber/src/hi64-app-kftpgrabber.png 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 Binary files differnew file mode 100644 index 0000000..906e4c8 --- /dev/null +++ b/kftpgrabber/src/kftpgrabber-bi-wizard.png diff --git a/kftpgrabber/src/kftpgrabber-logo.png b/kftpgrabber/src/kftpgrabber-logo.png Binary files differnew file mode 100644 index 0000000..b5ba48c --- /dev/null +++ b/kftpgrabber/src/kftpgrabber-logo.png 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>&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 §ion) +{ + 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 §ion); + 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>&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>&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>&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&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 && &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&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><b>File:</b></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&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 &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 &hidden files and directories</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_showTree</cstring> + </property> + <property name="text"> + <string>Show &directory tree</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_showSizeInBytes</cstring> + </property> + <property name="text"> + <string>Show filesi&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 &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 &size</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>&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&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>&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 &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>&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&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&zed to systray</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_showSplash</cstring> + </property> + <property name="text"> + <string>Show &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&ystray icon</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_exitOnClose</cstring> + </property> + <property name="text"> + <string>Ex&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>&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&ypt bookmark file</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_showWalletSites</cstring> + </property> + <property name="text"> + <string>&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>&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>&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 &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&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>&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 && 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>&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>&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>&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&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 &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>&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&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&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 &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&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>&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>&Queue files (instead of transferring) when "dragged && dropped"</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_skipEmptyDirs</cstring> + </property> + <property name="text"> + <string>Skip &empty directories when queueing</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_confirmDisconnects</cstring> + </property> + <property name="text"> + <string>Confirm disconnects &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: <b>Select Import Plugin</b></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: <b>Select Bookmark File to Import</b></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><b>Bookmark path:</b></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: <b>Importing Bookmarks...</b></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><b>Import progress:</b></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><b>Source:</b></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string><b>Destination:</b></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>&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&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 &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 & 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. <b>Changes cannot be undone.</b></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 ¤tUrl = 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 ¤tFilename() 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>>>></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("<", "<"); + line.replace(">", ">"); + 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 |