summaryrefslogtreecommitdiffstats
path: root/src/libs/kdtools
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/kdtools')
-rw-r--r--src/libs/kdtools/LICENSE.LGPL488
-rw-r--r--src/libs/kdtools/environment.cpp41
-rw-r--r--src/libs/kdtools/environment.h39
-rw-r--r--src/libs/kdtools/kdgenericfactory.cpp250
-rw-r--r--src/libs/kdtools/kdgenericfactory.h67
-rw-r--r--src/libs/kdtools/kdjob.cpp236
-rw-r--r--src/libs/kdtools/kdjob.h109
-rw-r--r--src/libs/kdtools/kdlockfile.cpp91
-rw-r--r--src/libs/kdtools/kdlockfile.h45
-rw-r--r--src/libs/kdtools/kdlockfile_p.h52
-rw-r--r--src/libs/kdtools/kdlockfile_unix.cpp77
-rw-r--r--src/libs/kdtools/kdlockfile_win.cpp68
-rw-r--r--src/libs/kdtools/kdrunoncechecker.cpp122
-rw-r--r--src/libs/kdtools/kdrunoncechecker.h45
-rw-r--r--src/libs/kdtools/kdsavefile.cpp521
-rw-r--r--src/libs/kdtools/kdsavefile.h84
-rw-r--r--src/libs/kdtools/kdselfrestarter.cpp87
-rw-r--r--src/libs/kdtools/kdselfrestarter.h43
-rw-r--r--src/libs/kdtools/kdsysinfo.cpp149
-rw-r--r--src/libs/kdtools/kdsysinfo.h83
-rw-r--r--src/libs/kdtools/kdsysinfo_mac.cpp134
-rw-r--r--src/libs/kdtools/kdsysinfo_win.cpp212
-rw-r--r--src/libs/kdtools/kdsysinfo_x11.cpp118
-rw-r--r--src/libs/kdtools/kdtools.pri60
-rw-r--r--src/libs/kdtools/kdtoolsglobal.h39
-rw-r--r--src/libs/kdtools/kdupdater.h52
-rw-r--r--src/libs/kdtools/kdupdaterapplication.cpp310
-rw-r--r--src/libs/kdtools/kdupdaterapplication.h87
-rw-r--r--src/libs/kdtools/kdupdaterfiledownloader.cpp1294
-rw-r--r--src/libs/kdtools/kdupdaterfiledownloader.h134
-rw-r--r--src/libs/kdtools/kdupdaterfiledownloader_p.h209
-rw-r--r--src/libs/kdtools/kdupdaterfiledownloaderfactory.cpp112
-rw-r--r--src/libs/kdtools/kdupdaterfiledownloaderfactory.h79
-rw-r--r--src/libs/kdtools/kdupdaterpackagesinfo.cpp581
-rw-r--r--src/libs/kdtools/kdupdaterpackagesinfo.h123
-rw-r--r--src/libs/kdtools/kdupdatersignatureverificationrunnable.cpp137
-rw-r--r--src/libs/kdtools/kdupdatersignatureverificationrunnable.h92
-rw-r--r--src/libs/kdtools/kdupdatertask.cpp431
-rw-r--r--src/libs/kdtools/kdupdatertask.h108
-rw-r--r--src/libs/kdtools/kdupdaterupdate.cpp311
-rw-r--r--src/libs/kdtools/kdupdaterupdate.h89
-rw-r--r--src/libs/kdtools/kdupdaterupdatefinder.cpp842
-rw-r--r--src/libs/kdtools/kdupdaterupdatefinder.h74
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperation.cpp388
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperation.h97
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperationfactory.cpp74
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperationfactory.h55
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperations.cpp1058
-rw-r--r--src/libs/kdtools/kdupdaterupdateoperations.h177
-rw-r--r--src/libs/kdtools/kdupdaterupdatesinfo.cpp352
-rw-r--r--src/libs/kdtools/kdupdaterupdatesinfo_p.h101
-rw-r--r--src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp500
-rw-r--r--src/libs/kdtools/kdupdaterupdatesourcesinfo.h114
53 files changed, 11241 insertions, 0 deletions
diff --git a/src/libs/kdtools/LICENSE.LGPL b/src/libs/kdtools/LICENSE.LGPL
new file mode 100644
index 000000000..ea164db15
--- /dev/null
+++ b/src/libs/kdtools/LICENSE.LGPL
@@ -0,0 +1,488 @@
+
+ The KD Tools Library is Copyright (C) 2001-2009 Klarälvdalens Datakonsult AB.
+
+ You may use, distribute and copy the KD Tools Library under the terms of
+ GNU Library General Public License version 2, which is displayed below.
+
+-------------------------------------------------------------------------
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, 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.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public 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.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/src/libs/kdtools/environment.cpp b/src/libs/kdtools/environment.cpp
new file mode 100644
index 000000000..4b031ecc4
--- /dev/null
+++ b/src/libs/kdtools/environment.cpp
@@ -0,0 +1,41 @@
+#include "environment.h"
+
+#include <QHash>
+#include <QProcess>
+#include <QProcessEnvironment>
+
+using namespace KDUpdater;
+
+Environment &Environment::instance()
+{
+ static Environment s_instance;
+ return s_instance;
+}
+
+QString Environment::value(const QString &key, const QString &defvalue) const
+{
+ const QHash<QString, QString>::ConstIterator it = m_tempValues.constFind(key);
+ if (it != m_tempValues.constEnd())
+ return *it;
+ return QProcessEnvironment::systemEnvironment().value(key, defvalue);
+}
+
+void Environment::setTemporaryValue(const QString &key, const QString &value)
+{
+ m_tempValues.insert(key, value);
+}
+
+QProcessEnvironment Environment::applyTo(const QProcessEnvironment &qpe_) const
+{
+ QProcessEnvironment qpe(qpe_);
+ QHash<QString, QString>::ConstIterator it = m_tempValues.constBegin();
+ const QHash<QString, QString>::ConstIterator end = m_tempValues.constEnd();
+ for ( ; it != end; ++it)
+ qpe.insert(it.key(), it.value());
+ return qpe;
+}
+
+void Environment::applyTo(QProcess *proc)
+{
+ proc->setProcessEnvironment(applyTo(proc->processEnvironment()));
+}
diff --git a/src/libs/kdtools/environment.h b/src/libs/kdtools/environment.h
new file mode 100644
index 000000000..917da4f45
--- /dev/null
+++ b/src/libs/kdtools/environment.h
@@ -0,0 +1,39 @@
+#ifndef LIBINSTALLER_ENVIRONMENT_H
+#define LIBINSTALLER_ENVIRONMENT_H
+
+#include "kdtoolsglobal.h"
+
+#include <QString>
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+class QProcess;
+class QProcessEnvironment;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+
+class KDTOOLS_EXPORT Environment
+{
+public:
+ static Environment &instance();
+
+ ~Environment() {}
+
+ QString value(const QString &key, const QString &defaultValue = QString()) const;
+ void setTemporaryValue(const QString &key, const QString &value);
+
+ QProcessEnvironment applyTo(const QProcessEnvironment &qpe) const;
+ void applyTo(QProcess *process);
+
+private:
+ Environment() {}
+
+private:
+ Q_DISABLE_COPY(Environment)
+ QHash<QString, QString> m_tempValues;
+};
+
+} // namespace KDUpdater
+
+#endif
diff --git a/src/libs/kdtools/kdgenericfactory.cpp b/src/libs/kdtools/kdgenericfactory.cpp
new file mode 100644
index 000000000..9352f83cd
--- /dev/null
+++ b/src/libs/kdtools/kdgenericfactory.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdgenericfactory.h"
+
+/*!
+ \class KDGenericFactory
+ \ingroup core
+ \brief Template based generic factory implementation
+ \since_c 2.1
+
+ (The exception safety of this class has not been evaluated yet.)
+
+ KDGenericFactory is an implemention of of the factory pattern. It can be used to
+ "produce" instances of different classes having a common superclass
+ T_Product. The user of the
+ factory registers those producable classes in the factory by using an identifier
+ (T_Identifier, defaulting to QString). That identifer can then be used to
+ produce as many instances of the registered product as he wants.
+
+ The advanced user can even choose the type of the map the factory is using to store its
+ FactoryFunctions by passing a T_Map template parameter. It defaults to QHash. KDGenericFactory
+ expects it to be a template class accepting T_Identifier and FactoryFunction as parameters.
+ Additionally it needs to provide:
+
+ \li\link QHash::const_iterator a nested %const_iterator \endlink typedef for an iterator type that when dereferenced has type ((const) reference to) FactoryFunction (Qt convention),
+ \li\link QHash::insert %insert( T_Identifier, FactoryFunction ) \endlink, which must overwrite any existing entries with the same identifier.
+ \li\link QHash::find %find( T_Identifier ) \endlink,
+ \li\link QHash::end %end() \endlink,
+ \li\link QHash::size %size() \endlink,
+ \li\link QHash::remove %remove( T_Identifier ) \endlink, and
+ \li\link QHash::keys %keys ) \endlink, returning a QList<T_Identifier>.
+
+ The only two class templates that currently match this concept are
+ QHash and QMap. QMultiHash and QMulitMap do not work, since they
+ violate the requirement on insert() above, and std::map and
+ std::unordered_map do not match because they don't have keys() and
+ because a dereferenced iterator has type
+ std::pair<const T_Identifier,FactoryFunction>
+ instead of just FactoryFunction.
+
+ \section general-use General Use
+
+ The following example shows how the general use case of KDGenericFactory looks like:
+
+ \code
+
+ class Fruit
+ {
+ };
+
+ class Apple : public Fruit
+ {
+ };
+
+ class Pear : public Fruit
+ {
+ };
+
+ int main()
+ {
+ // creates a common fruit "factory"
+ KDGenericFactory< Fruit > fruitPlantation;
+ // registers the product "Apple"
+ fruitPlantation.registerProduct< Apple >( "Apple" );
+ // registers the product "Pear"
+ fruitPlantation.registerProduct< Pear >( "Pear" );
+
+ // lets create some stuff - here comes our tasty apple:
+ Fruit* myApple = fruitPlantation.create( "Apple" );
+
+ // and a pear, please:
+ Fruit* myPear = fruitPlantation.create( "Pear" );
+
+ // ohh - that doesn't work, returns a null pointer:
+ Fruit* myCherry = fruitPlantation.create( "Cherry" );
+ }
+
+ \endcode
+*/
+
+/*!
+ \fn KDGenericFactory::~KDGenericFactory
+
+ Destructor.
+*/
+
+/*!
+ \typedef KDGenericFactory::FactoryFunction
+
+ This typedef defines a factory function producing an object of type T_Product.
+*/
+
+/*!
+ \fn KDGenericFactory::registerProduct( const T_Identifier& name )
+
+ Registers a product of type T, identified by \a name in the factory.
+ Any type with the same name gets unregistered.
+
+ If a product was registered via this method, it will be created using its
+ default constructor.
+*/
+
+/*!
+ \fn KDGenericFactory::unregisterProduct( const T_Identifier& name )
+
+ Unregisters the previously registered product identified by \a name from the factory.
+ If no such product is known, nothing is done.
+*/
+
+/*!
+ \fn KDGenericFactory::productCount() const
+
+ Returns the number of different products known to the factory.
+*/
+
+/*!
+ \fn KDGenericFactory::availableProducts() const
+
+ Returns the list of products known to the factory.
+*/
+
+/*!
+ \fn KDGenericFactory::create( const T_Identifier& name ) const
+
+ Creates and returns a product of the type identified by \a name.
+ Ownership of the product is transferred to the caller.
+*/
+
+/*!
+ \fn KDGenericFactory::registerProductionFunction( const T_Identifier& name, FactoryFunction create )
+
+ Subclasses can use this method to register their own FactoryFunction \a create to create products of
+ type T, identified by \a name. When a product is registered via this method, it will be created
+ by calling create().
+*/
+
+#ifdef KDTOOLSCORE_UNITTESTS
+
+#include <KDUnitTest/test.h>
+
+#include <QStringList>
+#include <QMap>
+
+class Fruit
+{
+public:
+ virtual ~Fruit() {}
+};
+
+class Apple : public Fruit
+{
+};
+
+class Pear : public Fruit
+{
+};
+
+std::ostream& operator<<( std::ostream& stream, const QStringList& list )
+{
+ stream << "QStringList(";
+ for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
+ {
+ stream << " " << it->toLocal8Bit().data();
+ if( it + 1 != list.end() )
+ stream << ",";
+ }
+ stream << " )";
+ return stream;
+}
+
+class KDGenericFactoryTest : public KDUnitTest::Test {
+public:
+ KDGenericFactoryTest() : Test( "KDGenericFactory" ) {}
+ void run() {
+ doRun<QHash>();
+ doRun<QMap>();
+ }
+
+ template <template <typename U, typename V> class T_Map>
+ void doRun();
+};
+
+KDAB_EXPORT_UNITTEST( KDGenericFactoryTest, "kdcoretools" )
+
+template <template <typename U, typename V> class T_Map>
+void KDGenericFactoryTest::doRun() {
+
+ {
+ KDGenericFactory< Fruit, QString, T_Map > fruitPlantation;
+ assertEqual( fruitPlantation.productCount(), 0U );
+ assertEqual( fruitPlantation.availableProducts(), QStringList() );
+
+ fruitPlantation.template registerProduct< Apple >( QLatin1String( "Apple" ) );
+ assertEqual( fruitPlantation.productCount(), 1U );
+ assertEqual( fruitPlantation.availableProducts(), QStringList( QLatin1String( "Apple" ) ) );
+
+ fruitPlantation.template registerProduct< Pear >( QLatin1String( "Pear" ) );
+ assertEqual( fruitPlantation.productCount(), 2U );
+
+ Fruit* fruit = 0;
+ fruit = fruitPlantation.create( QLatin1String( "Apple" ) );
+ assertNotNull( fruit );
+ assertNotNull( dynamic_cast< Apple* >( fruit ) );
+
+ fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
+ assertNotNull( fruit );
+ assertNotNull( dynamic_cast< Pear* >( fruit ) );
+
+ fruit = fruitPlantation.create( QLatin1String( "Cherry" ) );
+ assertNull( fruit );
+
+ fruitPlantation.unregisterProduct( QLatin1String( "Apple" ) );
+ assertEqual( fruitPlantation.productCount(), 1U );
+ assertEqual( fruitPlantation.availableProducts(), QStringList( QLatin1String( "Pear" ) ) );
+ fruit = fruitPlantation.create( QLatin1String( "Apple" ) );
+ assertNull( fruit );
+
+ fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
+ assertNotNull( fruit );
+ assertNotNull( dynamic_cast< Pear* >( fruit ) );
+
+
+ fruitPlantation.unregisterProduct( QLatin1String( "Pear" ) );
+ assertEqual( fruitPlantation.productCount(), 0U );
+ fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
+ assertNull( fruit );
+ }
+
+}
+#endif // KDTOOLSCORE_UNITTESTS
diff --git a/src/libs/kdtools/kdgenericfactory.h b/src/libs/kdtools/kdgenericfactory.h
new file mode 100644
index 000000000..d12c35785
--- /dev/null
+++ b/src/libs/kdtools/kdgenericfactory.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS__KDGENERICFACTORY_H
+#define KDTOOLS__KDGENERICFACTORY_H
+
+#include <kdtoolsglobal.h>
+
+#include <QtCore/QHash>
+
+template <typename T_Product, typename T_Identifier = QString>
+class KDGenericFactory
+{
+public:
+ virtual ~KDGenericFactory() {}
+
+ typedef T_Product *(*FactoryFunction)();
+
+ template <typename T>
+ void registerProduct(const T_Identifier &name)
+ {
+#ifdef Q_CC_MSVC
+ FactoryFunction function = &KDGenericFactory::create<T>;
+#else // compile fix for old gcc
+ FactoryFunction function = &create<T>;
+#endif
+ map.insert(name, function);
+ }
+
+ T_Product *create(const T_Identifier &name) const
+ {
+ const typename QHash<T_Identifier, FactoryFunction>::const_iterator it = map.find(name);
+ if (it == map.end())
+ return 0;
+ return (*it)();
+ }
+
+private:
+ template <typename T>
+ static T_Product *create()
+ {
+ return new T;
+ }
+
+ QHash<T_Identifier, FactoryFunction> map;
+};
+
+#endif
diff --git a/src/libs/kdtools/kdjob.cpp b/src/libs/kdtools/kdjob.cpp
new file mode 100644
index 000000000..47a30a836
--- /dev/null
+++ b/src/libs/kdtools/kdjob.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdjob.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QEventLoop>
+#include <QtCore/QTimer>
+
+
+// -- KDJob::Private
+
+class KDJob::Private
+{
+ KDJob *const q;
+
+public:
+ explicit Private(KDJob *qq)
+ : q(qq)
+ , error(KDJob::NoError)
+ , caps(KDJob::NoCapabilities)
+ , autoDelete(true)
+ , totalAmount(100)
+ , processedAmount(0)
+ , m_timeout(-1)
+ {
+ connect(&m_timer, SIGNAL(timeout()), q, SLOT(cancel()));
+ }
+
+ ~Private()
+ {
+ m_timer.stop();
+ }
+
+ void delayedStart()
+ {
+ q->doStart();
+ emit q->started(q);
+ }
+
+ void waitForSignal(const char *sig)
+ {
+ QEventLoop loop;
+ q->connect(q, sig, &loop, SLOT(quit()));
+
+ if (m_timeout >= 0)
+ m_timer.start(m_timeout);
+ else
+ m_timer.stop();
+
+ loop.exec();
+ }
+
+ int error;
+ QString errorString;
+ KDJob::Capabilities caps;
+ bool autoDelete;
+ quint64 totalAmount;
+ quint64 processedAmount;
+ int m_timeout;
+ QTimer m_timer;
+};
+
+
+// -- KDJob
+
+KDJob::KDJob(QObject *parent)
+ : QObject(parent),
+ d(new Private(this))
+{
+ connect(this, SIGNAL(finished(KDJob*)), this, SLOT(onFinished()));
+}
+
+KDJob::~KDJob()
+{
+ delete d;
+}
+
+bool KDJob::autoDelete() const
+{
+ return d->autoDelete;
+}
+
+void KDJob::setAutoDelete(bool autoDelete)
+{
+ d->autoDelete = autoDelete;
+}
+
+int KDJob::error() const
+{
+ return d->error;
+}
+
+QString KDJob::errorString() const
+{
+ return d->errorString;
+}
+
+void KDJob::emitFinished()
+{
+ emit finished(this);
+}
+
+void KDJob::emitFinishedWithError(int error, const QString &errorString)
+{
+ d->error = error;
+ d->errorString = errorString;
+ emitFinished();
+}
+
+void KDJob::setError(int error)
+{
+ d->error = error;
+}
+
+void KDJob::setErrorString(const QString &errorString)
+{
+ d->errorString = errorString;
+}
+
+void KDJob::waitForStarted()
+{
+ d->waitForSignal(SIGNAL(started(KDJob*)));
+}
+
+void KDJob::waitForFinished()
+{
+ d->waitForSignal(SIGNAL(finished(KDJob*)));
+}
+
+KDJob::Capabilities KDJob::capabilities() const
+{
+ return d->caps;
+}
+
+bool KDJob::hasCapability(Capability c) const
+{
+ return d->caps.testFlag(c);
+}
+
+void KDJob::setCapabilities(Capabilities c)
+{
+ d->caps = c;
+}
+
+void KDJob::start()
+{
+ QMetaObject::invokeMethod(this, "delayedStart", Qt::QueuedConnection);
+}
+
+void KDJob::cancel()
+{
+ if (d->caps & Cancelable) {
+ doCancel();
+ if (error() == NoError) {
+ setError(Canceled);
+ setErrorString(tr("Canceled"));
+ }
+ emitFinished();
+ } else {
+ qDebug() << "The current job can not be canceled, missing \"Cancelable\" capability!";
+ }
+}
+
+quint64 KDJob::totalAmount() const
+{
+ return d->totalAmount;
+}
+
+quint64 KDJob::processedAmount() const
+{
+ return d->processedAmount;
+}
+
+void KDJob::setTotalAmount(quint64 amount)
+{
+ if (d->totalAmount == amount)
+ return;
+ d->totalAmount = amount;
+ emit progress(this, d->processedAmount, d->totalAmount);
+}
+
+/*!
+ Returns the timeout in milliseconds before the jobs cancel slot gets triggered. A return value of -1
+ means there is currently no timeout used for the job.
+*/
+int KDJob::timeout() const
+{
+ return d->m_timeout;
+}
+
+/*!
+ Sets the timeout in \a milliseconds before the jobs cancel slot gets triggered. \note Only jobs that
+ have the \bold KDJob::Cancelable capability can be canceled by an timeout. A value of -1 will stop the
+ timeout mechanism.
+*/
+void KDJob::setTimeout(int milliseconds)
+{
+ d->m_timeout = milliseconds;
+}
+
+void KDJob::setProcessedAmount(quint64 amount)
+{
+ if (d->processedAmount == amount)
+ return;
+ d->processedAmount = amount;
+ emit progress(this, d->processedAmount, d->totalAmount);
+}
+
+void KDJob::onFinished()
+{
+ d->m_timer.stop();
+ if (d->autoDelete)
+ deleteLater();
+}
+
+#include "moc_kdjob.cpp"
diff --git a/src/libs/kdtools/kdjob.h b/src/libs/kdtools/kdjob.h
new file mode 100644
index 000000000..37f441526
--- /dev/null
+++ b/src/libs/kdtools/kdjob.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_KDJOB_H
+#define KDTOOLS_KDJOB_H
+
+#include "kdtoolsglobal.h"
+
+#include <QtCore/QObject>
+
+class KDTOOLS_EXPORT KDJob : public QObject
+{
+ Q_OBJECT
+ class Private;
+
+ Q_PROPERTY(int timeout READ timeout WRITE setTimeout)
+ Q_PROPERTY(bool autoDelete READ autoDelete WRITE setAutoDelete)
+
+public:
+ explicit KDJob(QObject *parent = 0);
+ ~KDJob();
+
+ enum Error {
+ NoError = 0,
+ Canceled = 1,
+ UserDefinedError = 128
+ };
+
+ enum Capability {
+ NoCapabilities = 0x0,
+ Cancelable = 0x1
+ };
+
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ int error() const;
+ QString errorString() const;
+
+ bool autoDelete() const;
+ void setAutoDelete(bool autoDelete);
+
+ Capabilities capabilities() const;
+ bool hasCapability(Capability c) const;
+
+ void waitForStarted();
+ void waitForFinished();
+
+ quint64 totalAmount() const;
+ quint64 processedAmount() const;
+
+ int timeout() const;
+ void setTimeout(int milliseconds);
+
+public Q_SLOTS:
+ void start();
+ void cancel();
+
+Q_SIGNALS:
+ void started(KDJob *job);
+ void finished(KDJob *job);
+
+ void infoMessage(KDJob *job, const QString &message);
+ void progress(KDJob *job, quint64 processed, quint64 total);
+
+protected:
+ virtual void doStart() = 0;
+ virtual void doCancel() = 0;
+
+ void setCapabilities(Capabilities c);
+
+ void setTotalAmount(quint64 amount);
+ void setProcessedAmount(quint64 amount);
+
+ void setError(int error);
+ void setErrorString(const QString &errorString);
+
+ void emitFinished();
+ void emitFinishedWithError(int error, const QString &errorString);
+
+private Q_SLOTS:
+ void onFinished();
+
+private:
+ Private *d;
+ Q_PRIVATE_SLOT(d, void delayedStart())
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KDJob::Capabilities)
+
+#endif // KDTOOLS_KDJOB_H
diff --git a/src/libs/kdtools/kdlockfile.cpp b/src/libs/kdtools/kdlockfile.cpp
new file mode 100644
index 000000000..3a440b6b0
--- /dev/null
+++ b/src/libs/kdtools/kdlockfile.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdlockfile.h"
+
+#include "kdlockfile_p.h"
+
+KDLockFile::Private::Private(const QString &filename_)
+ : filename(filename_)
+ , handle(0)
+ , locked(false)
+{
+}
+
+KDLockFile::KDLockFile(const QString &name)
+ : d( new Private(name))
+{
+}
+
+KDLockFile::~KDLockFile()
+{
+ delete d;
+}
+
+bool KDLockFile::lock()
+{
+ return d->lock();
+}
+
+QString KDLockFile::errorString() const
+{
+ return d->errorString;
+}
+
+bool KDLockFile::unlock()
+{
+ return d->unlock();
+}
+
+
+#ifdef KDTOOLSCORE_UNITTESTS
+
+#include <KDUnitTest/Test>
+#include <QDebug>
+#include <QDir>
+
+KDAB_UNITTEST_SIMPLE( KDLockFile, "kdcoretools" ) {
+ {
+ KDLockFile f( QLatin1String("/jlksdfdsfjkldsf-doesnotexist/file") );
+ const bool locked = f.lock();
+ assertFalse( locked );
+ qDebug() << f.errorString();
+ assertTrue( !f.errorString().isEmpty() );
+ if ( !locked )
+ assertTrue( f.unlock() );
+ }
+ {
+ KDLockFile f( QDir::currentPath() + QLatin1String("/kdlockfile-test") );
+ const bool locked = f.lock();
+ assertTrue( locked );
+ if ( !locked )
+ qDebug() << f.errorString();
+ assertEqual( locked, f.errorString().isEmpty() );
+ const bool unlocked = f.unlock();
+ assertTrue( unlocked );
+ if ( !unlocked )
+ qDebug() << f.errorString();
+ assertEqual( unlocked, f.errorString().isEmpty() );
+ }
+}
+
+#endif // KDTOOLSCORE_UNITTESTS
diff --git a/src/libs/kdtools/kdlockfile.h b/src/libs/kdtools/kdlockfile.h
new file mode 100644
index 000000000..1674070bf
--- /dev/null
+++ b/src/libs/kdtools/kdlockfile.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_KDLOCKFILE_H
+#define KDTOOLS_KDLOCKFILE_H
+
+#include <kdtoolsglobal.h>
+
+class KDTOOLS_EXPORT KDLockFile
+{
+public:
+ explicit KDLockFile(const QString &name);
+ ~KDLockFile();
+
+ QString errorString() const;
+
+ bool lock();
+ bool unlock();
+
+private:
+ Q_DISABLE_COPY(KDLockFile)
+ class Private;
+ Private *d;
+};
+
+#endif // KDTOOLS_KDLOCKFILE_H
diff --git a/src/libs/kdtools/kdlockfile_p.h b/src/libs/kdtools/kdlockfile_p.h
new file mode 100644
index 000000000..da4c4dc4c
--- /dev/null
+++ b/src/libs/kdtools/kdlockfile_p.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef __KDTOOLSCORE_KDLOCKFILE_P_H__
+#define __KDTOOLSCORE_KDLOCKFILE_P_H__
+
+#include "kdlockfile.h"
+#include <QtCore/QString>
+#ifdef Q_OS_WIN
+#include <windows.h>
+#endif
+
+class KDLockFile::Private
+{
+public:
+ explicit Private( const QString& filename );
+ ~Private();
+ bool lock();
+ bool unlock();
+
+ QString errorString;
+
+private:
+ QString filename;
+#ifdef Q_OS_WIN
+ HANDLE handle;
+#else
+ int handle;
+#endif
+ bool locked;
+};
+
+#endif // LOCKFILE_P_H
diff --git a/src/libs/kdtools/kdlockfile_unix.cpp b/src/libs/kdtools/kdlockfile_unix.cpp
new file mode 100644
index 000000000..c40eb3b7d
--- /dev/null
+++ b/src/libs/kdtools/kdlockfile_unix.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdlockfile_p.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <cerrno>
+
+#include <sys/file.h>
+
+KDLockFile::Private::~Private()
+{
+ unlock();
+}
+
+bool KDLockFile::Private::lock()
+{
+ if (locked)
+ return true;
+
+ errorString.clear();
+ errno = 0;
+ handle = open(filename.toLatin1().constData(), O_CREAT | O_RDWR | O_NONBLOCK, 0600);
+ if (handle == -1) {
+ errorString = QObject::tr("Could not create lock file %1: %2").arg(filename, QLatin1String(strerror(errno)));
+ return false;
+ }
+ const QString pid = QString::number(qApp->applicationPid());
+ const QByteArray data = pid.toLatin1();
+ errno = 0;
+ qint64 written = 0;
+ while (written < data.size()) {
+ const qint64 n = write(handle, data.constData() + written, data.size() - written);
+ if (n < 0) {
+ errorString = QObject::tr("Could not write PID to lock file %1: %2").arg( filename, QLatin1String( strerror( errno ) ) );
+ return false;
+ }
+ written += n;
+ }
+ errno = 0;
+ locked = flock(handle, LOCK_NB | LOCK_EX) != -1;
+ if (!locked)
+ errorString = QObject::tr("Could not lock lock file %1: %2").arg(filename, QLatin1String(strerror(errno)));
+ return locked;
+}
+
+bool KDLockFile::Private::unlock()
+{
+ errorString.clear();
+ if (!locked)
+ return true;
+ errno = 0;
+ locked = flock(handle, LOCK_UN | LOCK_NB) == -1;
+ if (locked)
+ errorString = QObject::tr("Could not unlock lock file %1: %2").arg(filename, QLatin1String(strerror(errno)));
+ return !locked;
+}
diff --git a/src/libs/kdtools/kdlockfile_win.cpp b/src/libs/kdtools/kdlockfile_win.cpp
new file mode 100644
index 000000000..f139bd0d4
--- /dev/null
+++ b/src/libs/kdtools/kdlockfile_win.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdlockfile.h"
+#include "kdlockfile_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFileInfo>
+
+KDLockFile::Private::~Private()
+{
+ unlock();
+}
+
+bool KDLockFile::Private::lock()
+{
+ const QFileInfo fi(filename);
+ handle = CreateFile(filename.toStdWString().data(),
+ GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
+ NULL, fi.exists() ? OPEN_EXISTING : CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+
+ if (!handle)
+ return false;
+ QString pid = QString::number(qApp->applicationPid());
+ QByteArray data = pid.toLatin1();
+ DWORD bytesWritten;
+ const bool wrotePid = WriteFile(handle, data.data(), data.size(), &bytesWritten, NULL);
+ if (!wrotePid)
+ return false;
+ FlushFileBuffers(handle);
+
+ const bool locked = LockFile(handle, 0, 0, fi.size(), 0);
+
+ this->locked = locked;
+ return locked;
+}
+
+bool KDLockFile::Private::unlock()
+{
+ const QFileInfo fi(filename);
+ if (locked) {
+ const bool success = UnlockFile(handle, 0, 0, 0, fi.size());
+ this->locked = !success;
+ CloseHandle(handle);
+ return success;
+ }
+ return true;
+}
diff --git a/src/libs/kdtools/kdrunoncechecker.cpp b/src/libs/kdtools/kdrunoncechecker.cpp
new file mode 100644
index 000000000..8e0af3ddc
--- /dev/null
+++ b/src/libs/kdtools/kdrunoncechecker.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdrunoncechecker.h"
+#include "kdlockfile.h"
+#include "kdsysinfo.h"
+
+#include <QtCore/QList>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+
+#include <algorithm>
+
+using namespace KDUpdater;
+
+class KDRunOnceChecker::Private
+{
+public:
+ Private(const QString &filename);
+
+ KDLockFile m_lockfile;
+ bool m_hasLock;
+};
+
+KDRunOnceChecker::Private::Private(const QString &filename)
+ : m_lockfile(filename)
+ , m_hasLock(false)
+{}
+
+KDRunOnceChecker::KDRunOnceChecker(const QString &filename)
+ :d(new Private(filename))
+{}
+
+KDRunOnceChecker::~KDRunOnceChecker()
+{
+ delete d;
+}
+
+class ProcessnameEquals
+{
+public:
+ ProcessnameEquals(const QString &name): m_name(name) {}
+
+ bool operator()(const ProcessInfo &info)
+ {
+#ifndef Q_WS_WIN
+ if (info.name == m_name)
+ return true;
+ const QFileInfo fi(info.name);
+ if (fi.fileName() == m_name || fi.baseName() == m_name)
+ return true;
+ return false;
+#else
+ if (info.name.toLower() == m_name.toLower())
+ return true;
+ if (info.name.toLower() == QDir::toNativeSeparators(m_name.toLower()))
+ return true;
+ const QFileInfo fi(info.name);
+ if (fi.fileName().toLower() == m_name.toLower() || fi.baseName().toLower() == m_name.toLower())
+ return true;
+ return info.name == m_name;
+#endif
+ }
+
+private:
+ QString m_name;
+};
+
+bool KDRunOnceChecker::isRunning(Dependencies depends)
+{
+ bool running = false;
+ switch (depends) {
+ case Lockfile: {
+ const bool locked = d->m_hasLock || d->m_lockfile.lock();
+ if (locked)
+ d->m_hasLock = true;
+ running = running || ! locked;
+ }
+ break;
+ case ProcessList: {
+ const QList<ProcessInfo> allProcesses = runningProcesses();
+ const QString appName = qApp->applicationFilePath();
+ //QList< ProcessInfo >::const_iterator it = std::find_if(allProcesses.constBegin(), allProcesses.constEnd(), ProcessnameEquals(appName));
+ const int count = std::count_if(allProcesses.constBegin(), allProcesses.constEnd(), ProcessnameEquals(appName));
+ running = running || /*it != allProcesses.constEnd()*/count > 1;
+ }
+ break;
+ case Both: {
+ const QList<ProcessInfo> allProcesses = runningProcesses();
+ const QString appName = qApp->applicationFilePath();
+ //QList<ProcessInfo>::const_iterator it = std::find_if(allProcesses.constBegin(), allProcesses.constEnd(), ProcessnameEquals(appName));
+ const int count = std::count_if(allProcesses.constBegin(), allProcesses.constEnd(), ProcessnameEquals(appName));
+ const bool locked = d->m_hasLock || d->m_lockfile.lock();
+ if (locked)
+ d->m_hasLock = true;
+ running = running || ( /*it != allProcesses.constEnd()*/count > 1 && !locked);
+ }
+ break;
+ }
+
+ return running;
+}
diff --git a/src/libs/kdtools/kdrunoncechecker.h b/src/libs/kdtools/kdrunoncechecker.h
new file mode 100644
index 000000000..127102274
--- /dev/null
+++ b/src/libs/kdtools/kdrunoncechecker.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_RUNONCECHECKER_H
+#define KDTOOLS_RUNONCECHECKER_H
+
+#include <kdtoolsglobal.h>
+
+#include <QString>
+
+class KDTOOLS_EXPORT KDRunOnceChecker
+{
+public:
+ enum Dependencies { ProcessList, Lockfile, Both };
+
+ explicit KDRunOnceChecker(const QString &filename = QString());
+ ~KDRunOnceChecker();
+ bool isRunning(Dependencies depends);
+
+private:
+ Q_DISABLE_COPY(KDRunOnceChecker)
+ class Private;
+ Private *d;
+};
+
+#endif // KDTOOLS_RUNONCECHECKER_H
diff --git a/src/libs/kdtools/kdsavefile.cpp b/src/libs/kdtools/kdsavefile.cpp
new file mode 100644
index 000000000..d866968cf
--- /dev/null
+++ b/src/libs/kdtools/kdsavefile.cpp
@@ -0,0 +1,521 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdsavefile.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QPointer>
+#include <QtCore/QTemporaryFile>
+
+#ifdef Q_OS_WIN
+# include <io.h>
+#endif
+#include <memory>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+static int permissionsToMode(QFile::Permissions p, bool *ok)
+{
+ Q_ASSERT(ok);
+ int m = 0;
+#ifdef Q_OS_WIN
+ //following qfsfileengine_win.cpp: QFSFileEngine::setPermissions()
+ if (p & QFile::ReadOwner || p & QFile::ReadUser || p & QFile::ReadGroup || p & QFile::ReadOther)
+ m |= _S_IREAD;
+ if (p & QFile::WriteOwner || p & QFile::WriteUser || p & QFile::WriteGroup || p & QFile::WriteOther)
+ m |= _S_IWRITE;
+ *ok = m != 0;
+#else
+ if (p & QFile::ReadUser)
+ m |= S_IRUSR;
+ if (p & QFile::WriteUser)
+ m |= S_IWUSR;
+ if (p & QFile::ExeUser)
+ m |= S_IXUSR;
+ if (p & QFile::ReadGroup)
+ m |= S_IRGRP;
+ if (p & QFile::WriteGroup)
+ m |= S_IWGRP;
+ if (p & QFile::ExeGroup)
+ m |= S_IXGRP;
+ if (p & QFile::ReadOther)
+ m |= S_IROTH;
+ if (p & QFile::WriteOther)
+ m |= S_IWOTH;
+ if (p & QFile::ExeOther)
+ m |= S_IXOTH;
+ *ok = true;
+#endif
+ return m;
+}
+
+static bool sync(int fd)
+{
+#ifdef Q_OS_WIN
+ return _commit(fd) == 0;
+#else
+ return fsync(fd) == 0;
+#endif
+}
+
+static QString makeAbsolute(const QString &path)
+{
+ if (QDir::isAbsolutePath(path))
+ return path;
+ return QDir::currentPath() + QLatin1String("/") + path;
+}
+
+static int myOpen(const QString &path, int flags, int mode)
+{
+#ifdef Q_OS_WIN
+ int fd;
+ _wsopen_s(&fd, reinterpret_cast<const wchar_t *>(path.utf16()), flags, _SH_DENYRW, mode);
+ return fd;
+#else
+ return open(QFile::encodeName(path).constData(), flags, mode);
+#endif
+}
+
+static void myClose(int fd)
+{
+#ifdef Q_OS_WIN
+ _close(fd);
+#else
+ close(fd);
+#endif
+}
+
+static bool touchFile(const QString &path, QFile::Permissions p)
+{
+ bool ok;
+ const int mode = permissionsToMode(p, &ok);
+ if (!ok)
+ return false;
+ const int fd = myOpen(QDir::toNativeSeparators(path), O_WRONLY|O_CREAT, mode);
+ if (fd < 0) {
+ QFile file(path);
+ if (!file.open(QIODevice::WriteOnly))
+ return false;
+ if (!file.setPermissions(p)) {
+ QFile::remove(path);
+ return false;
+ }
+ return true;
+ }
+ sync(fd);
+ myClose(fd);
+ return true;
+}
+
+static QFile *createFile(const QString &path, QIODevice::OpenMode m, QFile::Permissions p, bool *openOk)
+{
+ Q_ASSERT(openOk);
+ if (!touchFile(path, p))
+ return 0;
+ std::auto_ptr<QFile> file(new QFile(path));
+ *openOk = file->open(m | QIODevice::Append);
+ if (!*openOk)
+ QFile::remove(path); // try to remove empty file
+ return file.release();
+}
+
+/*!
+ Generates a temporary file name based on template \a path
+ \internal
+ */
+static QString generateTempFileName(const QString &path)
+{
+ const QString tmp = path + QLatin1String("tmp.dsfdf.%1"); //TODO: use random suffix
+ int count = 1;
+ while (QFile::exists(tmp.arg(count)))
+ ++count;
+ return tmp.arg(count);
+}
+
+/*!
+ \class KDSaveFile KDSaveFile
+ \ingroup core
+ \brief Secure and robust writing to a file
+
+*/
+
+class KDSaveFile::Private
+{
+ KDSaveFile *const q;
+public:
+ explicit Private(const QString &fname, KDSaveFile *qq)
+ : q(qq),
+#ifdef Q_OS_WIN
+ backupExtension(QLatin1String(".bak")),
+#else
+ backupExtension(QLatin1String("~")),
+#endif
+ permissions(QFile::ReadUser|QFile::WriteUser), filename(fname), tmpFile()
+ {
+ //TODO respect umask instead of hardcoded default permissions
+ }
+
+ ~Private()
+ {
+ deleteTempFile();
+ }
+
+ bool deleteTempFile()
+ {
+ if (!tmpFile)
+ return true;
+ const QString name = tmpFile->fileName();
+ delete tmpFile;
+ //force a real close by deleting the object, before deleting the actual file. Needed on Windows
+ QFile tmp(name);
+ return tmp.remove();
+ }
+
+ bool recreateTemporaryFile(QIODevice::OpenMode mode)
+ {
+ deleteTempFile();
+ bool ok;
+ tmpFile = createFile(generateTempFileName( filename ), mode, permissions, &ok);
+ QObject::connect(tmpFile, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
+ QObject::connect(tmpFile, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
+ QObject::connect(tmpFile, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
+ QObject::connect(tmpFile, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
+ return ok;
+ }
+
+ QString generateBackupName() const
+ {
+ const QString bf = filename + backupExtension;
+ if (!QFile::exists(bf))
+ return bf;
+ int count = 1;
+ while (QFile::exists(bf + QString::number(count)))
+ ++count;
+ return bf + QString::number(count);
+ }
+
+ void propagateErrors()
+ {
+ if (!tmpFile)
+ return;
+ q->setErrorString(tmpFile->errorString());
+ }
+
+ QString backupExtension;
+ QFile::Permissions permissions;
+ QString filename;
+ QPointer<QFile> tmpFile;
+};
+
+
+KDSaveFile::KDSaveFile(const QString &filename, QObject *parent)
+ : QIODevice(parent), d( new Private(makeAbsolute(filename), this))
+{}
+
+KDSaveFile::~KDSaveFile()
+{
+ delete d;
+}
+
+void KDSaveFile::close()
+{
+ d->deleteTempFile();
+ QIODevice::close();
+}
+
+bool KDSaveFile::open(OpenMode mode)
+{
+ setOpenMode(QIODevice::NotOpen);
+ if (mode & QIODevice::Append) {
+ setErrorString(tr("Append mode not supported."));
+ return false;
+ }
+
+ if ((mode & QIODevice::WriteOnly) == 0){
+ setErrorString(tr("Read-only access not supported."));
+ return false;
+ }
+
+ // for some reason this seems to be problematic... let's remove it for now, will break in commit anyhow, if it's serious
+ //if ( QFile::exists( d->filename ) && !QFileInfo( d->filename ).isWritable() ) {
+ // setErrorString( tr("The target file %1 exists and is not writable").arg( d->filename ) );
+ // return false;
+ //}
+ const bool opened = d->recreateTemporaryFile(mode);
+ if (opened) {
+ setOpenMode(mode);
+ }
+
+ //if target file already exists, apply permissions of existing file to temp file
+ return opened;
+}
+
+bool KDSaveFile::atEnd()
+{
+ return d->tmpFile ? d->tmpFile->atEnd() : QIODevice::atEnd();
+}
+
+qint64 KDSaveFile::bytesAvailable() const
+{
+ return d->tmpFile ? d->tmpFile->bytesAvailable() : QIODevice::bytesAvailable();
+}
+
+qint64 KDSaveFile::bytesToWrite() const
+{
+ return d->tmpFile ? d->tmpFile->bytesToWrite() : QIODevice::bytesToWrite();
+}
+
+bool KDSaveFile::canReadLine() const
+{
+ return d->tmpFile ? d->tmpFile->canReadLine() : QIODevice::canReadLine();
+}
+
+bool KDSaveFile::isSequential() const
+{
+ return d->tmpFile ? d->tmpFile->isSequential() : QIODevice::isSequential();
+}
+
+qint64 KDSaveFile::pos() const
+{
+ return d->tmpFile ? d->tmpFile->pos() : QIODevice::pos();
+}
+
+bool KDSaveFile::reset()
+{
+ return d->tmpFile ? d->tmpFile->reset() : QIODevice::reset();
+}
+
+bool KDSaveFile::seek(qint64 pos)
+{
+ const bool ret = d->tmpFile ? d->tmpFile->seek(pos) : QIODevice::seek(pos);
+ if (!ret)
+ d->propagateErrors();
+ return ret;
+}
+
+qint64 KDSaveFile::size() const
+{
+ return d->tmpFile ? d->tmpFile->size() : QIODevice::size();
+}
+
+bool KDSaveFile::waitForBytesWritten(int msecs)
+{
+ return d->tmpFile ? d->tmpFile->waitForBytesWritten(msecs) : QIODevice::waitForBytesWritten(msecs);
+}
+
+bool KDSaveFile::waitForReadyRead(int msecs)
+{
+ return d->tmpFile ? d->tmpFile->waitForReadyRead(msecs) : QIODevice::waitForReadyRead(msecs);
+}
+
+bool KDSaveFile::commit(KDSaveFile::CommitMode mode)
+{
+ if (!d->tmpFile)
+ return false;
+ const QString tmpfname = d->tmpFile->fileName();
+ d->tmpFile->flush();
+ delete d->tmpFile;
+ QFile orig(d->filename);
+ QString backup;
+ if (orig.exists()) {
+ backup = d->generateBackupName();
+ if (!orig.rename(backup)) {
+ setErrorString(tr("Could not backup existing file %1: %2").arg( d->filename, orig.errorString()));
+ QFile tmp(tmpfname);
+ if (!tmp.remove()) // TODO how to report this error?
+ qWarning() << "Could not remove temp file" << tmpfname << tmp.errorString();
+ if (mode != OverwriteExistingFile)
+ return false;
+ }
+ }
+ QFile target(tmpfname);
+ if (!target.rename(d->filename)) {
+ setErrorString(target.errorString());
+ return false;
+ }
+ if (mode == OverwriteExistingFile) {
+ QFile tmp(backup);
+ const bool removed = !tmp.exists() || tmp.remove(backup);
+ if (!removed)
+ qWarning() << "Could not remove the backup: " << tmp.errorString();
+ }
+
+ return true;
+}
+
+QString KDSaveFile::fileName() const
+{
+ return d->filename;
+}
+
+void KDSaveFile::setFileName(const QString &filename)
+{
+ const QString fn = makeAbsolute(filename);
+ if (fn == d->filename)
+ return;
+ close();
+ delete d->tmpFile;
+ d->filename = fn;
+}
+
+qint64 KDSaveFile::readData(char *data, qint64 maxSize)
+{
+ if (!d->tmpFile) {
+ setErrorString(tr("TODO"));
+ return -1;
+ }
+ const qint64 ret = d->tmpFile->read(data, maxSize);
+ d->propagateErrors();
+ return ret;
+}
+
+qint64 KDSaveFile::readLineData(char *data, qint64 maxSize)
+{
+ if (!d->tmpFile) {
+ setErrorString(tr("TODO"));
+ return -1;
+ }
+ const qint64 ret = d->tmpFile->readLine(data, maxSize);
+ d->propagateErrors();
+ return ret;
+}
+
+qint64 KDSaveFile::writeData(const char *data, qint64 maxSize)
+{
+ if (!d->tmpFile) {
+ setErrorString(tr("TODO"));
+ return -1;
+ }
+ const qint64 ret = d->tmpFile->write(data, maxSize);
+ d->propagateErrors();
+ return ret;
+}
+
+bool KDSaveFile::flush()
+{
+ return d->tmpFile ? d->tmpFile->flush() : false;
+}
+
+bool KDSaveFile::resize(qint64 sz)
+{
+ return d->tmpFile ? d->tmpFile->resize(sz) : false;
+}
+
+int KDSaveFile::handle() const
+{
+ return d->tmpFile ? d->tmpFile->handle() : -1;
+}
+
+QFile::Permissions KDSaveFile::permissions() const
+{
+ return d->tmpFile ? d->tmpFile->permissions() : d->permissions;
+}
+
+bool KDSaveFile::setPermissions(QFile::Permissions p)
+{
+ d->permissions = p;
+ if (d->tmpFile)
+ return d->tmpFile->setPermissions(p);
+ return false;
+}
+
+void KDSaveFile::setBackupExtension(const QString &ext)
+{
+ d->backupExtension = ext;
+}
+
+QString KDSaveFile::backupExtension() const
+{
+ return d->backupExtension;
+}
+
+/**
+ * TODO
+ *
+ *
+ */
+
+#ifdef KDTOOLSCORE_UNITTESTS
+
+#include <KDUnitTest/Test>
+
+KDAB_UNITTEST_SIMPLE( KDSaveFile, "kdcoretools" ) {
+ //TODO test contents (needs blocking and checked write() )
+ {
+ const QString testfile1 = QLatin1String("kdsavefile-test1");
+ QByteArray testData("lalalala");
+ KDSaveFile saveFile( testfile1 );
+ assertTrue( saveFile.open( QIODevice::WriteOnly ) );
+ saveFile.write( testData.constData(), testData.size() );
+ assertTrue( saveFile.commit() );
+ assertTrue( QFile::exists( testfile1 ) );
+ assertTrue( QFile::remove( testfile1 ) );
+ }
+ {
+ const QString testfile1 = QLatin1String("kdsavefile-test1");
+ QByteArray testData("lalalala");
+ KDSaveFile saveFile( testfile1 );
+ assertTrue( saveFile.open( QIODevice::WriteOnly ) );
+ saveFile.write( testData.constData(), testData.size() );
+ saveFile.close();
+ assertFalse( QFile::exists( testfile1 ) );
+ }
+ {
+ const QString testfile1 = QLatin1String("kdsavefile-test1");
+ QByteArray testData("lalalala");
+ KDSaveFile saveFile( testfile1 );
+ assertTrue( saveFile.open( QIODevice::WriteOnly ) );
+ saveFile.write( testData.constData(), testData.size() );
+ assertTrue( saveFile.commit() );
+ assertTrue( QFile::exists( testfile1 ) );
+
+ KDSaveFile sf2( testfile1 );
+ sf2.setBackupExtension( QLatin1String(".bak") );
+ assertTrue( sf2.open( QIODevice::WriteOnly ) );
+ sf2.write( testData.constData(), testData.size() );
+ sf2.commit(); //commit in backup mode (default)
+ const QString backup = testfile1 + sf2.backupExtension();
+ assertTrue( QFile::exists( backup ) );
+ assertTrue( QFile::remove( backup ) );
+
+ KDSaveFile sf3( testfile1 );
+ sf3.setBackupExtension( QLatin1String(".bak") );
+ assertTrue( sf3.open( QIODevice::WriteOnly ) );
+ sf3.write( testData.constData(), testData.size() );
+ sf3.commit( KDSaveFile::OverwriteExistingFile );
+ const QString backup2 = testfile1 + sf3.backupExtension();
+ assertFalse( QFile::exists( backup2 ) );
+
+ assertTrue( QFile::remove( testfile1 ) );
+ }
+ {
+ const QString testfile1 = QLatin1String("kdsavefile-test1");
+ KDSaveFile sf( testfile1 );
+ assertFalse( sf.open( QIODevice::ReadOnly ) );
+ assertFalse( sf.open( QIODevice::WriteOnly|QIODevice::Append ) );
+ assertTrue( sf.open( QIODevice::ReadWrite ) );
+ }
+}
+
+#endif // KDTOOLSCORE_UNITTESTS
diff --git a/src/libs/kdtools/kdsavefile.h b/src/libs/kdtools/kdsavefile.h
new file mode 100644
index 000000000..dc42e1930
--- /dev/null
+++ b/src/libs/kdtools/kdsavefile.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_KDSAVEFILE_H
+#define KDTOOLS_KDSAVEFILE_H
+
+#include <kdtoolsglobal.h>
+
+#include <QtCore/QIODevice>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+class KDTOOLS_EXPORT KDSaveFile : public QIODevice
+{
+ Q_OBJECT
+
+public:
+ explicit KDSaveFile(const QString &filename = QString(), QObject *parent = 0);
+ ~KDSaveFile();
+
+ enum CommitMode {
+ BackupExistingFile = 1,
+ OverwriteExistingFile = 2
+ };
+
+ bool commit(CommitMode = BackupExistingFile);
+
+ QString fileName() const;
+ void setFileName(const QString &filename);
+
+ QFile::Permissions permissions() const;
+ bool setPermissions(QFile::Permissions);
+
+ QString backupExtension() const;
+ void setBackupExtension(const QString &extension);
+
+ bool flush();
+ bool resize(qint64 size);
+ int handle() const;
+
+ bool atEnd();
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+ bool canReadLine() const;
+ void close();
+ bool isSequential() const;
+ bool open(OpenMode mode = QIODevice::ReadWrite); //only valid: WriteOnly, ReadWrite
+ qint64 pos() const;
+ bool reset();
+ bool seek(qint64 pos);
+ qint64 size() const;
+ bool waitForBytesWritten(int msecs);
+ bool waitForReadyRead(int msecs);
+
+private:
+ qint64 readData(char *data, qint64 maxSize);
+ qint64 readLineData(char *data, qint64 maxSize);
+ qint64 writeData(const char *data, qint64 maxSize);
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif // KDTOOLS_KDSAVEFILE_H
diff --git a/src/libs/kdtools/kdselfrestarter.cpp b/src/libs/kdtools/kdselfrestarter.cpp
new file mode 100644
index 000000000..22457bd92
--- /dev/null
+++ b/src/libs/kdtools/kdselfrestarter.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdselfrestarter.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QProcess>
+
+class KDSelfRestarter::Private
+{
+public:
+ Private(int argc, char *argv[])
+ : restartOnQuit(false)
+ {
+ executable = QString::fromLocal8Bit(argv[0]);
+ workingPath = QDir::currentPath();
+ for (int i = 1; i < argc; ++i)
+ args << QString::fromLocal8Bit(argv[i]);
+ }
+
+ Private()
+ {
+ executable = qApp->applicationFilePath();
+ workingPath = QDir::currentPath();
+ args = qApp->arguments().mid(1);
+ }
+
+ ~Private()
+ {
+ if (restartOnQuit)
+ QProcess::startDetached(executable, args, workingPath);
+ }
+
+ QString executable;
+ QStringList args;
+ bool restartOnQuit;
+ QString workingPath;
+ static KDSelfRestarter *instance;
+};
+
+KDSelfRestarter *KDSelfRestarter::Private::instance = 0;
+
+KDSelfRestarter::KDSelfRestarter(int argc, char *argv[])
+ : d(new Private(argc, argv))
+{
+ Q_ASSERT_X(!Private::instance, Q_FUNC_INFO, "Cannot create more than one KDSelfRestarter instance");
+ Private::instance = this;
+}
+
+KDSelfRestarter::~KDSelfRestarter()
+{
+ Q_ASSERT_X(Private::instance == this, Q_FUNC_INFO, "Cannot create more than one KDSelfRestarter instance");
+ delete d;
+ Private::instance = 0;
+}
+
+void KDSelfRestarter::setRestartOnQuit(bool restart)
+{
+ Q_ASSERT_X(Private::instance, Q_FUNC_INFO, "KDSelfRestarter instance must be created in main()");
+ if (Private::instance)
+ Private::instance->d->restartOnQuit = restart;
+}
+
+bool KDSelfRestarter::restartOnQuit()
+{
+ return Private::instance ? Private::instance->d->restartOnQuit : false;
+}
diff --git a/src/libs/kdtools/kdselfrestarter.h b/src/libs/kdtools/kdselfrestarter.h
new file mode 100644
index 000000000..bbf46a2b9
--- /dev/null
+++ b/src/libs/kdtools/kdselfrestarter.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_KDSELFRESTARTER_H
+#define KDTOOLS_KDSELFRESTARTER_H
+
+#include "kdtoolsglobal.h"
+
+class KDTOOLS_EXPORT KDSelfRestarter
+{
+public:
+ KDSelfRestarter(int argc, char *argv[]);
+ ~KDSelfRestarter();
+
+ static bool restartOnQuit();
+ static void setRestartOnQuit(bool restart);
+
+private:
+ Q_DISABLE_COPY(KDSelfRestarter)
+ class Private;
+ Private *d;
+};
+
+#endif // KDTOOLS_KDSELFRESTARTER_H
diff --git a/src/libs/kdtools/kdsysinfo.cpp b/src/libs/kdtools/kdsysinfo.cpp
new file mode 100644
index 000000000..8e56d01ac
--- /dev/null
+++ b/src/libs/kdtools/kdsysinfo.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdsysinfo.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+
+using namespace KDUpdater;
+
+struct PathLongerThan
+{
+ bool operator()(const VolumeInfo &lhs, const VolumeInfo &rhs) const
+ {
+ return lhs.mountPath().length() > rhs.mountPath().length();
+ }
+};
+
+VolumeInfo::VolumeInfo()
+ : m_size(0)
+ , m_availableSize(0)
+{
+}
+
+VolumeInfo VolumeInfo::fromPath(const QString &path)
+{
+ QDir targetPath(QDir::cleanPath(path));
+ QList<VolumeInfo> volumes = mountedVolumes();
+
+ // sort by length to get the longest mount point (not just "/") first
+ qSort(volumes.begin(), volumes.end(), PathLongerThan());
+ foreach (const VolumeInfo &volume, volumes) {
+ const QDir volumePath(volume.mountPath());
+ if (targetPath == volumePath)
+ return volume;
+#ifdef Q_OS_WIN
+ if (QDir::toNativeSeparators(path).toLower().startsWith(volume.mountPath().toLower()))
+#else
+ // we need to take some care here, as canonical path might return an empty string if the target
+ // does not exist yet
+ if (targetPath.exists()) {
+ // the target exist, we can solve the path and if it fits return
+ if (targetPath.canonicalPath().startsWith(volume.mountPath()))
+ return volume;
+ continue;
+ }
+
+ // the target directory does not exist yet, we need to cd up till we find the first existing dir
+ QStringList parts = targetPath.absolutePath().split(QDir::separator(),QString::SkipEmptyParts);
+ while (targetPath.absolutePath() != QDir::rootPath()) {
+ if (targetPath.exists())
+ break;
+ parts.pop_back();
+ if (parts.isEmpty())
+ targetPath = QDir(QDir::rootPath());
+ else
+ targetPath = QDir(parts.join(QDir::separator()));
+ }
+
+ if (targetPath.canonicalPath().startsWith(volume.mountPath()))
+#endif
+ return volume;
+ }
+ return VolumeInfo();
+}
+
+QString VolumeInfo::mountPath() const
+{
+ return m_mountPath;
+}
+
+void VolumeInfo::setMountPath(const QString &path)
+{
+ m_mountPath = path;
+}
+
+QString VolumeInfo::fileSystemType() const
+{
+ return m_fileSystemType;
+}
+
+void VolumeInfo::setFileSystemType(const QString &type)
+{
+ m_fileSystemType = type;
+}
+
+QString VolumeInfo::volumeDescriptor() const
+{
+ return m_volumeDescriptor;
+}
+
+void VolumeInfo::setVolumeDescriptor(const QString &descriptor)
+{
+ m_volumeDescriptor = descriptor;
+}
+
+quint64 VolumeInfo::size() const
+{
+ return m_size;
+}
+
+void VolumeInfo::setSize(const quint64 &size)
+{
+ m_size = size;
+}
+
+quint64 VolumeInfo::availableSize() const
+{
+ return m_availableSize;
+}
+
+void VolumeInfo::setAvailableSize(const quint64 &available)
+{
+ m_availableSize = available;
+}
+
+bool VolumeInfo::operator==(const VolumeInfo &other) const
+{
+ return m_volumeDescriptor == other.m_volumeDescriptor;
+}
+
+QDebug operator<<(QDebug dbg, VolumeInfo volume)
+{
+ return dbg << "KDUpdater::Volume(" << volume.mountPath() << ")";
+}
+
+QDebug operator<<(QDebug dbg, ProcessInfo process)
+{
+ return dbg << "KDUpdater::ProcessInfo(" << process.id << ", " << process.name << ")";
+}
diff --git a/src/libs/kdtools/kdsysinfo.h b/src/libs/kdtools/kdsysinfo.h
new file mode 100644
index 000000000..6c8079fcd
--- /dev/null
+++ b/src/libs/kdtools/kdsysinfo.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDSYSINFO_H
+#define KDSYSINFO_H
+
+#include <kdtoolsglobal.h>
+
+#include <QtCore/QString>
+
+namespace KDUpdater {
+
+class KDTOOLS_EXPORT VolumeInfo
+{
+public:
+ VolumeInfo();
+ static VolumeInfo fromPath(const QString &path);
+
+ QString mountPath() const;
+ void setMountPath(const QString &path);
+
+ QString fileSystemType() const;
+ void setFileSystemType(const QString &type);
+
+ QString volumeDescriptor() const;
+ void setVolumeDescriptor(const QString &descriptor);
+
+ quint64 size() const;
+ void setSize(const quint64 &size);
+
+ quint64 availableSize() const;
+ void setAvailableSize(const quint64 &available);
+
+ bool operator==(const VolumeInfo &other) const;
+
+private:
+ QString m_mountPath;
+ QString m_fileSystemType;
+ QString m_volumeDescriptor;
+
+ quint64 m_size;
+ quint64 m_availableSize;
+};
+
+struct ProcessInfo
+{
+ quint32 id;
+ QString name;
+};
+
+quint64 installedMemory();
+QList<VolumeInfo> mountedVolumes();
+QList<ProcessInfo> runningProcesses();
+
+} // namespace KDUpdater
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
+
+QDebug operator<<(QDebug dbg, KDUpdater::VolumeInfo volume);
+QDebug operator<<(QDebug dbg, KDUpdater::ProcessInfo process);
+
+#endif // KDSYSINFO_H
diff --git a/src/libs/kdtools/kdsysinfo_mac.cpp b/src/libs/kdtools/kdsysinfo_mac.cpp
new file mode 100644
index 000000000..d039f8975
--- /dev/null
+++ b/src/libs/kdtools/kdsysinfo_mac.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdsysinfo.h"
+
+#include <Carbon/Carbon.h>
+
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <QtCore/QList>
+
+namespace KDUpdater {
+
+quint64 installedMemory()
+{
+ SInt32 mb = 0;
+ Gestalt(gestaltPhysicalRAMSizeInMegabytes, &mb);
+ return quint64(static_cast<quint64>(mb) * 1024LL * 1024LL);
+}
+
+QList<VolumeInfo> mountedVolumes()
+{
+ QList<VolumeInfo> result;
+ FSVolumeRefNum volume;
+ FSVolumeInfo info;
+ HFSUniStr255 volName;
+ FSRef ref;
+ int i = 0;
+
+ while (FSGetVolumeInfo(kFSInvalidVolumeRefNum, ++i, &volume, kFSVolInfoFSInfo, &info, &volName, &ref) == 0) {
+ UInt8 path[PATH_MAX + 1];
+ if (FSRefMakePath(&ref, path, PATH_MAX) == 0) {
+ FSGetVolumeInfo(volume, 0, 0, kFSVolInfoSizes, &info, 0, 0);
+
+ VolumeInfo v;
+ v.setSize(quint64(info.totalBytes));
+ v.setAvailableSize(quint64(info.freeBytes));
+ v.setMountPath(QString::fromLocal8Bit(reinterpret_cast< char* >(path)));
+
+ struct statfs data;
+ if (statfs(qPrintable(v.mountPath() + QLatin1String("/.")), &data) == 0) {
+ v.setFileSystemType(QLatin1String(data.f_fstypename));
+ v.setVolumeDescriptor(QLatin1String(data.f_mntfromname));
+ }
+ result.append(v);
+ }
+ }
+ return result;
+}
+
+QList<ProcessInfo> runningProcesses()
+{
+ int mib[4] = {
+ CTL_KERN,
+ KERN_ARGMAX,
+ 0,
+ 0
+ };
+
+ int argMax = 0;
+ size_t argMaxSize = sizeof(argMax);
+ // fetch the maximum process arguments size
+ sysctl(mib, 2, &argMax, &argMaxSize, NULL, 0);
+ char *processArguments = (char*) malloc(argMax);
+
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_ALL;
+ size_t processTableSize = 0;
+ // fetch the kernel process table size
+ sysctl(mib, 4, NULL, &processTableSize, NULL, 0);
+ struct kinfo_proc *processTable = (kinfo_proc*) malloc(processTableSize);
+
+ // fetch the process table
+ sysctl(mib, 4, processTable, &processTableSize, NULL, 0);
+
+ QList<ProcessInfo> processes;
+ for (size_t i = 0; i < (processTableSize / sizeof(struct kinfo_proc)); ++i) {
+ struct kinfo_proc *process = processTable + i;
+
+ ProcessInfo processInfo;
+ processInfo.id = process->kp_proc.p_pid;
+
+ mib[1] = KERN_PROCARGS2;
+ mib[2] = process->kp_proc.p_pid;
+ mib[3] = 0;
+
+ size_t size = argMax;
+ // fetch the process arguments
+ if (sysctl(mib, 3, processArguments, &size, NULL, 0) != -1) {
+ /*
+ * |-----------------| <-- data returned by sysctl()
+ * | argc |
+ * |-----------------|
+ * | executable path |
+ * |-----------------|
+ * | arguments |
+ * ~~~~~~~~~~~~~~~~~~~
+ * |-----------------|
+ */
+ processInfo.name = QString::fromLocal8Bit(processArguments + sizeof(int));
+ } else {
+ // if we fail, use the name from the process table
+ processInfo.name = QString::fromLocal8Bit(process->kp_proc.p_comm);
+ }
+ processes.append(processInfo);
+ }
+ free(processTable);
+ free(processArguments);
+
+ return processes;
+}
+
+} // namespace KDUpdater
diff --git a/src/libs/kdtools/kdsysinfo_win.cpp b/src/libs/kdtools/kdsysinfo_win.cpp
new file mode 100644
index 000000000..45342aee5
--- /dev/null
+++ b/src/libs/kdtools/kdsysinfo_win.cpp
@@ -0,0 +1,212 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdsysinfo.h"
+
+#include <windows.h>
+#include <Psapi.h>
+#include <Tlhelp32.h>
+
+#include <Winnetwk.h>
+#pragma comment(lib, "mpr.lib")
+
+#include <QtCore/QDir>
+#include <QtCore/QLibrary>
+
+const int KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
+
+namespace KDUpdater {
+
+quint64 installedMemory()
+{
+ MEMORYSTATUSEX status;
+ status.dwLength = sizeof(status);
+ GlobalMemoryStatusEx(&status);
+ return quint64(status.ullTotalPhys);
+}
+
+VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info)
+{
+ ULARGE_INTEGER bytesTotal;
+ ULARGE_INTEGER freeBytesPerUser;
+
+ VolumeInfo update = info;
+ if (GetDiskFreeSpaceExA(qPrintable(info.volumeDescriptor()), &freeBytesPerUser, &bytesTotal, NULL)) {
+ update.setSize(bytesTotal.QuadPart);
+ update.setAvailableSize(freeBytesPerUser.QuadPart);
+ }
+ return update;
+}
+
+/*!
+ Returns a list of volume info objects that are mounted as network drive shares.
+*/
+QList<VolumeInfo> networkVolumeInfosFromMountPoints()
+{
+ QList<VolumeInfo> volumes;
+ QFileInfoList drives = QDir::drives();
+ foreach (const QFileInfo &drive, drives) {
+ const QString driveLetter = QDir::toNativeSeparators(drive.canonicalPath());
+ const uint driveType = GetDriveTypeA(qPrintable(driveLetter));
+ switch (driveType) {
+ case DRIVE_REMOTE: {
+ char buffer[1024] = "";
+ DWORD bufferLength = 1024;
+ UNIVERSAL_NAME_INFOA *universalNameInfo = (UNIVERSAL_NAME_INFOA*) &buffer;
+ if (WNetGetUniversalNameA(qPrintable(driveLetter), UNIVERSAL_NAME_INFO_LEVEL,
+ LPVOID(universalNameInfo), &bufferLength) == NO_ERROR) {
+ VolumeInfo info;
+ info.setMountPath(driveLetter);
+ info.setVolumeDescriptor(QLatin1String(universalNameInfo->lpUniversalName));
+ volumes.append(info);
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+ return volumes;
+}
+
+/*!
+ Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted
+ volume folder paths. It does not return any network drive shares.
+*/
+QList<VolumeInfo> localVolumeInfosFromMountPoints(const QByteArray &volumeGUID)
+{
+ QList<VolumeInfo> volumes;
+ DWORD bufferSize;
+ char volumeNames[1024] = "";
+ if (GetVolumePathNamesForVolumeNameA(volumeGUID, volumeNames, ARRAYSIZE(volumeNames), &bufferSize)) {
+ QStringList mountedPaths = QString::fromLatin1(volumeNames, bufferSize).split(QLatin1Char(char(0)),
+ QString::SkipEmptyParts);
+ foreach (const QString &mountedPath, mountedPaths) {
+ VolumeInfo info;
+ info.setMountPath(mountedPath);
+ info.setVolumeDescriptor(QString::fromLatin1(volumeGUID));
+ volumes.append(info);
+ }
+ }
+ return volumes;
+}
+
+QList<VolumeInfo> mountedVolumes()
+{
+ QList<VolumeInfo> tmp;
+ char volumeGUID[MAX_PATH] = "";
+ HANDLE handle = FindFirstVolumeA(volumeGUID, ARRAYSIZE(volumeGUID));
+ if (handle != INVALID_HANDLE_VALUE) {
+ tmp += localVolumeInfosFromMountPoints(volumeGUID);
+ while (FindNextVolumeA(handle, volumeGUID, ARRAYSIZE(volumeGUID))) {
+ tmp += localVolumeInfosFromMountPoints(volumeGUID);
+ }
+ FindVolumeClose(handle);
+ }
+ tmp += networkVolumeInfosFromMountPoints();
+
+ QList<VolumeInfo> volumes;
+ while (!tmp.isEmpty()) // update volume size information
+ volumes.append(updateVolumeSizeInformation(tmp.takeFirst()));
+ return volumes;
+}
+
+struct EnumWindowsProcParam
+{
+ QList<ProcessInfo> processes;
+ QList<quint32> seenIDs;
+};
+
+typedef BOOL (WINAPI *QueryFullProcessImageNamePtr)(HANDLE, DWORD, char *, PDWORD);
+typedef DWORD (WINAPI *GetProcessImageFileNamePtr)(HANDLE, char *, DWORD);
+
+QList<ProcessInfo> runningProcesses()
+{
+ EnumWindowsProcParam param;
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (!snapshot)
+ return param.processes;
+ PROCESSENTRY32 processStruct;
+ processStruct.dwSize = sizeof(PROCESSENTRY32);
+ bool foundProcess = Process32First(snapshot, &processStruct);
+ const DWORD bufferSize = 1024;
+ char driveBuffer[bufferSize];
+ QStringList deviceList;
+ if (QSysInfo::windowsVersion() <= QSysInfo::WV_5_2) {
+ DWORD size = GetLogicalDriveStringsA(bufferSize, driveBuffer);
+ deviceList = QString::fromLatin1(driveBuffer, size).split(QLatin1Char(char(0)), QString::SkipEmptyParts);
+ }
+
+ QLibrary kernel32(QLatin1String("Kernel32.dll"));
+ kernel32.load();
+ void *pQueryFullProcessImageNameA = kernel32.resolve("QueryFullProcessImageNameA");
+
+ QLibrary psapi(QLatin1String("Psapi.dll"));
+ psapi.load();
+ void *pGetProcessImageFileNamePtr = psapi.resolve("GetProcessImageFileNameA");
+ QueryFullProcessImageNamePtr callPtr = (QueryFullProcessImageNamePtr) pQueryFullProcessImageNameA;
+ GetProcessImageFileNamePtr callPtrXp = (GetProcessImageFileNamePtr) pGetProcessImageFileNamePtr;
+
+ while (foundProcess) {
+ HANDLE procHandle = OpenProcess(QSysInfo::windowsVersion() > QSysInfo::WV_5_2
+ ? KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION
+ : PROCESS_QUERY_INFORMATION,
+ false,
+ processStruct.th32ProcessID);
+ char buffer[1024];
+ DWORD bufferSize = 1024;
+ bool succ = false;
+ QString executablePath;
+ ProcessInfo info;
+
+ if (QSysInfo::windowsVersion() > QSysInfo::WV_5_2) {
+ succ = callPtr(procHandle, 0, buffer, &bufferSize);
+ executablePath = QString::fromLatin1(buffer);
+ } else if (pGetProcessImageFileNamePtr) {
+ succ = callPtrXp(procHandle, buffer, bufferSize);
+ executablePath = QString::fromLatin1(buffer);
+ for (int i = 0; i < deviceList.count(); ++i) {
+ executablePath.replace(QString::fromLatin1( "\\Device\\HarddiskVolume%1\\" ).arg(i + 1),
+ deviceList.at(i));
+ }
+ }
+
+ if (succ) {
+ const quint32 pid = processStruct.th32ProcessID;
+ param.seenIDs.append(pid);
+ info.id = pid;
+ info.name = executablePath;
+ param.processes.append(info);
+ }
+
+ CloseHandle(procHandle);
+ foundProcess = Process32Next(snapshot, &processStruct);
+
+ }
+ if (snapshot)
+ CloseHandle(snapshot);
+
+ kernel32.unload();
+ return param.processes;
+}
+
+} // namespace KDUpdater
diff --git a/src/libs/kdtools/kdsysinfo_x11.cpp b/src/libs/kdtools/kdsysinfo_x11.cpp
new file mode 100644
index 000000000..f6e2c8ced
--- /dev/null
+++ b/src/libs/kdtools/kdsysinfo_x11.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdsysinfo.h"
+
+#include <sys/utsname.h>
+#include <sys/statvfs.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+
+namespace KDUpdater {
+
+quint64 installedMemory()
+{
+#ifdef Q_OS_LINUX
+ QFile f(QLatin1String("/proc/meminfo"));
+ f.open(QIODevice::ReadOnly);
+ QTextStream stream(&f);
+ while (true) {
+ const QString s = stream.readLine();
+ if( !s.startsWith(QLatin1String("MemTotal:" )))
+ continue;
+ else if (s.isEmpty())
+ return quint64();
+
+ const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ return quint64(parts.at(1).toInt() * 1024LL);
+ }
+#else
+ quint64 physmem;
+ size_t len = sizeof physmem;
+ static int mib[2] = { CTL_HW, HW_MEMSIZE };
+ sysctl(mib, 2, &physmem, &len, 0, 0);
+ return quint64(physmem);
+#endif
+ return 0;
+}
+
+QList<VolumeInfo> mountedVolumes()
+{
+ QList<VolumeInfo> result;
+
+ QFile f(QLatin1String("/etc/mtab"));
+ if (!f.open(QIODevice::ReadOnly)) {
+ qCritical("%s: Could not open %s: %s", Q_FUNC_INFO, qPrintable(f.fileName()), qPrintable(f.errorString()));
+ return result; //better error-handling?
+ }
+
+ QTextStream stream(&f);
+ while (true) {
+ const QString s = stream.readLine();
+ if (s.isNull())
+ return result;
+
+ if (!s.startsWith(QLatin1Char('/')))
+ continue;
+
+ const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
+
+ VolumeInfo v;
+ v.setMountPath(parts.at(1));
+ v.setVolumeDescriptor(parts.at(0));
+ v.setFileSystemType(parts.value(2));
+
+ struct statvfs data;
+ if (statvfs(qPrintable(v.mountPath() + QLatin1String("/.")), &data) == 0) {
+ v.setSize(quint64(static_cast<quint64>(data.f_blocks) * data.f_bsize));
+ v.setAvailableSize(quint64(static_cast<quint64>(data.f_bavail) * data.f_bsize));
+ }
+ result.append(v);
+ }
+ return result;
+}
+
+QList<ProcessInfo> runningProcesses()
+{
+ QList<ProcessInfo> processes;
+ QDir procDir(QLatin1String("/proc"));
+ const QFileInfoList procCont = procDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
+ QRegExp validator(QLatin1String("[0-9]+"));
+ Q_FOREACH (const QFileInfo &info, procCont) {
+ if (validator.exactMatch(info.fileName())) {
+ const QString linkPath = QDir(info.absoluteFilePath()).absoluteFilePath(QLatin1String("exe"));
+ const QFileInfo linkInfo(linkPath);
+ if (linkInfo.exists()) {
+ ProcessInfo processInfo;
+ processInfo.name = linkInfo.symLinkTarget();
+ processInfo.id = info.fileName().toInt();
+ processes.append(processInfo);
+ }
+ }
+ }
+ return processes;
+}
+
+} // namespace KDUpdater
diff --git a/src/libs/kdtools/kdtools.pri b/src/libs/kdtools/kdtools.pri
new file mode 100644
index 000000000..8b14def9a
--- /dev/null
+++ b/src/libs/kdtools/kdtools.pri
@@ -0,0 +1,60 @@
+DEPENDPATH += $$PWD
+INCLUDEPATH += $$PWD
+
+CONFIG(shared, static|shared) {
+ DEFINES += BUILD_SHARED_KDTOOLS
+}
+
+HEADERS += $$PWD/kdtoolsglobal.h \
+ $$PWD/kdjob.h \
+ $$PWD/kdgenericfactory.h \
+ $$PWD/kdselfrestarter.h \
+ $$PWD/kdsavefile.h \
+ $$PWD/kdrunoncechecker.h \
+ $$PWD/kdlockfile.h \
+ $$PWD/kdsysinfo.h
+
+SOURCES += $$PWD/kdjob.cpp \
+ $$PWD/kdgenericfactory.cpp \
+ $$PWD/kdselfrestarter.cpp \
+ $$PWD/kdsavefile.cpp \
+ $$PWD/kdrunoncechecker.cpp \
+ $$PWD/kdlockfile.cpp \
+ $$PWD/kdsysinfo.cpp
+
+
+HEADERS += $$PWD/kdupdater.h \
+ $$PWD/kdupdaterapplication.h \
+ $$PWD/kdupdaterfiledownloader.h \
+ $$PWD/kdupdaterfiledownloader_p.h \
+ $$PWD/kdupdaterfiledownloaderfactory.h \
+ $$PWD/kdupdaterpackagesinfo.h \
+ $$PWD/kdupdaterupdate.h \
+ $$PWD/kdupdaterupdateoperation.h \
+ $$PWD/kdupdaterupdateoperationfactory.h \
+ $$PWD/kdupdaterupdateoperations.h \
+ $$PWD/kdupdaterupdatesourcesinfo.h \
+ $$PWD/kdupdatertask.h \
+ $$PWD/kdupdaterupdatefinder.h \
+ $$PWD/kdupdaterupdatesinfo_p.h \
+ $$PWD/environment.h
+
+SOURCES += $$PWD/kdupdaterapplication.cpp \
+ $$PWD/kdupdaterfiledownloader.cpp \
+ $$PWD/kdupdaterfiledownloaderfactory.cpp \
+ $$PWD/kdupdaterpackagesinfo.cpp \
+ $$PWD/kdupdaterupdate.cpp \
+ $$PWD/kdupdaterupdateoperation.cpp \
+ $$PWD/kdupdaterupdateoperationfactory.cpp \
+ $$PWD/kdupdaterupdateoperations.cpp \
+ $$PWD/kdupdaterupdatesourcesinfo.cpp \
+ $$PWD/kdupdatertask.cpp \
+ $$PWD/kdupdaterupdatefinder.cpp \
+ $$PWD/kdupdaterupdatesinfo.cpp \
+ $$PWD/environment.cpp
+
+unix:SOURCES += $$PWD/kdlockfile_unix.cpp
+win32:SOURCES += $$PWD/kdlockfile_win.cpp
+win32:SOURCES += $$PWD/kdsysinfo_win.cpp
+macx:SOURCES += $$PWD/kdsysinfo_mac.cpp
+unix:!macx:SOURCES += $$PWD/kdsysinfo_x11.cpp
diff --git a/src/libs/kdtools/kdtoolsglobal.h b/src/libs/kdtools/kdtoolsglobal.h
new file mode 100644
index 000000000..8199724c4
--- /dev/null
+++ b/src/libs/kdtools/kdtoolsglobal.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDTOOLS_KDTOOLSGLOBAL_H
+#define KDTOOLS_KDTOOLSGLOBAL_H
+
+#include <QtCore/QtGlobal>
+
+#ifdef KDTOOLS_SHARED
+# ifdef BUILD_SHARED_KDTOOLS
+# define KDTOOLS_EXPORT Q_DECL_EXPORT
+# else
+# define KDTOOLS_EXPORT Q_DECL_IMPORT
+# endif
+#else // KDTOOLS_SHARED
+# define KDTOOLS_EXPORT
+#endif // KDTOOLS_SHARED
+
+#endif // KDTOOLS_KDTOOLSGLOBAL_H
+
diff --git a/src/libs/kdtools/kdupdater.h b/src/libs/kdtools/kdupdater.h
new file mode 100644
index 000000000..f28461d9b
--- /dev/null
+++ b/src/libs/kdtools/kdupdater.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_H
+#define KD_UPDATER_H
+
+#include <kdtoolsglobal.h>
+
+namespace KDUpdater
+{
+ enum Error
+ {
+ ENoError = 0,
+ ECannotStartTask,
+ ECannotPauseTask,
+ ECannotResumeTask,
+ ECannotStopTask,
+ EUnknown
+ };
+
+ enum UpdateType {
+ PackageUpdate = 0x1,
+ CompatUpdate = 0x2,
+ NewPackage = 0x4,
+ AllUpdate = PackageUpdate | CompatUpdate
+ };
+ Q_DECLARE_FLAGS( UpdateTypes, UpdateType )
+ Q_DECLARE_OPERATORS_FOR_FLAGS( UpdateTypes )
+
+ KDTOOLS_EXPORT int compareVersion(const QString &v1, const QString &v2);
+}
+
+#endif
diff --git a/src/libs/kdtools/kdupdaterapplication.cpp b/src/libs/kdtools/kdupdaterapplication.cpp
new file mode 100644
index 000000000..8506eca46
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterapplication.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterapplication.h"
+#include "kdupdaterpackagesinfo.h"
+#include "kdupdaterupdatesourcesinfo.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QSettings>
+
+using namespace KDUpdater;
+
+/*!
+ \defgroup kdupdater KD Updater
+ \since_l 2.1
+
+ "KD Updater" is a library from KDAB that helps in enabling automatic updates for your applications.
+ All classes belonging to the "KD Updater" library are defined in the \ref KDUpdater namespace.
+
+ TODO: this comes from the former mainpage:
+KD Updater is a tool to automatically detect, retrieve, install and activate updates to software
+applications and libraries. It is intended to be used with Qt based applications, and developed
+against the Qt 4 series. It is a library that users link to their application. It uses only accepted
+standard protocols, and does not require any other 3rd party libraries that are not shipped with
+Qt.
+
+KD Updater is generic in that it is not developed for one specific application. The first version is
+experimental. If it proves successful and useful, it will be integrated into KDAB's KD Tools
+package. It is part of KDAB's strategy to provide functionality missing in Qt that is required for
+medium-to-large scale software systems.
+*/
+
+/*!
+ \namespace KDUpdater
+*/
+
+ConfigurationInterface::~ConfigurationInterface()
+{
+}
+
+namespace {
+
+class DefaultConfigImpl : public ConfigurationInterface
+{
+public:
+ QVariant value(const QString &key) const
+ {
+ QSettings settings;
+ settings.beginGroup(QLatin1String("KDUpdater"));
+ return settings.value(key);
+ }
+
+ void setValue(const QString &key, const QVariant &value)
+ {
+ QSettings settings;
+ settings.beginGroup(QLatin1String("KDUpdater"));
+ settings.setValue(key, value);
+ }
+};
+
+} // namespace anon
+
+/*!
+ \class KDUpdater::Application kdupdaterapplication.h KDUpdaterApplication
+ \ingroup kdupdater
+ \brief This class represents an application that can be updated.
+
+ A KDUpdater application is an application that needs to interact with one or more update servers and
+ downloads/installs updates This class helps in describing an application in terms of:
+ \li application Directory
+ \li packages XML file name and its corresponding KDUpdater::PackagesInfo object
+ \li update Sources XML file name and its corresponding KDUpdater::UpdateSourcesInfo object
+
+ User can also retrieve some informations from this class:
+ \li application name
+ \li application version
+ \li compat level
+*/
+
+struct Application::ApplicationData
+{
+ explicit ApplicationData(ConfigurationInterface *config) :
+ packagesInfo(0),
+ updateSourcesInfo(0),
+ configurationInterface(config ? config : new DefaultConfigImpl)
+ {
+ const QStringList oldFiles = configurationInterface->value(QLatin1String("FilesForDelayedDeletion")).toStringList();
+ Q_FOREACH(const QString &i, oldFiles) { //TODO this should happen asnyc and report errors, I guess
+ QFile f(i);
+ if (f.exists() && !f.remove()) {
+ qWarning("Could not delete file %s: %s", qPrintable(i), qPrintable(f.errorString()));
+ filesForDelayedDeletion << i; // try again next time
+ }
+ }
+ configurationInterface->setValue(QLatin1String("FilesForDelayedDeletion"), filesForDelayedDeletion);
+ }
+
+ ~ApplicationData()
+ {
+ delete packagesInfo;
+ delete updateSourcesInfo;
+ delete configurationInterface;
+ }
+
+ static Application *instance;
+
+ QString applicationDirectory;
+ PackagesInfo *packagesInfo;
+ UpdateSourcesInfo *updateSourcesInfo;
+ QStringList filesForDelayedDeletion;
+ ConfigurationInterface *configurationInterface;
+};
+
+Application *Application::ApplicationData::instance = 0;
+
+/*!
+ Constructor of the Application class. The class will be constructed and configured to
+ assume the application directory to be the directory in which the application exists. The
+ application name is assumed to be QCoreApplication::applicationName()
+*/
+Application::Application(ConfigurationInterface* config, QObject* p) : QObject(p)
+{
+ d = new Application::ApplicationData( config );
+ d->packagesInfo = new PackagesInfo(this);
+ d->updateSourcesInfo = new UpdateSourcesInfo(this);
+
+ setApplicationDirectory( QCoreApplication::applicationDirPath() );
+
+ ApplicationData::instance = this;
+}
+
+/*!
+ Destructor
+*/
+Application::~Application()
+{
+ if (this == ApplicationData::instance)
+ ApplicationData::instance = 0;
+ delete d;
+}
+
+/*!
+ Returns a previousle created Application instance.
+ */
+Application *Application::instance()
+{
+ return ApplicationData::instance;
+}
+
+/*!
+ Changes the applicationDirPath directory to \c dir. Packages.xml and UpdateSources.xml found in the new
+ application directory will be used.
+*/
+void Application::setApplicationDirectory(const QString &dir)
+{
+ if (d->applicationDirectory == dir)
+ return;
+
+ QDir dirObj(dir);
+
+ // FIXME: Perhaps we should check whether dir exists on the local file system or not
+ d->applicationDirectory = dirObj.absolutePath();
+ setPackagesXMLFileName(QString::fromLatin1("%1/Packages.xml").arg(dir));
+ setUpdateSourcesXMLFileName(QString::fromLatin1("%1/UpdateSources.xml").arg(dir));
+}
+
+/*!
+ Returns path to the application directory.
+*/
+QString Application::applicationDirectory() const
+{
+ return d->applicationDirectory;
+}
+
+/*!
+ Returns the application name.
+*/
+QString Application::applicationName() const
+{
+ if (d->packagesInfo->isValid())
+ return d->packagesInfo->applicationName();
+
+ return QCoreApplication::applicationName();
+}
+
+/*!
+ Returns the application version.
+*/
+QString Application::applicationVersion() const
+{
+ if (d->packagesInfo->isValid())
+ return d->packagesInfo->applicationVersion();
+
+ return QString();
+}
+
+/*!
+ Returns the compat level that this application is in.
+*/
+int Application::compatLevel() const
+{
+ if (d->packagesInfo->isValid())
+ return d->packagesInfo->compatLevel();
+
+ return -1;
+}
+
+void Application::addUpdateSource(const QString &name, const QString &title,
+ const QString &description, const QUrl &url, int priority)
+{
+ UpdateSourceInfo info;
+ info.name = name;
+ info.title = title;
+ info.description = description;
+ info.url = url;
+ info.priority = priority;
+ d->updateSourcesInfo->addUpdateSourceInfo(info);
+}
+
+
+/*!
+ Sets the file name of the Package XML file for this application. By default this is assumed to be
+ Packages.xml in the application directory.
+
+ \sa KDUpdater::PackagesInfo::setFileName()
+*/
+void Application::setPackagesXMLFileName(const QString &fileName)
+{
+ d->packagesInfo->setFileName(fileName);
+}
+
+/*!
+ Returns the Package XML file name.
+*/
+QString Application::packagesXMLFileName() const
+{
+ return d->packagesInfo->fileName();
+}
+
+/*!
+ Returns the \ref PackagesInfo object associated with this application.
+*/
+PackagesInfo* Application::packagesInfo() const
+{
+ return d->packagesInfo;
+}
+
+/*!
+ Sets the file name of the Package XML file for this application. By default this is assumed to be
+ Packages.xml in the application directory.
+
+ \sa KDUpdater::UpdateSourcesInfo::setFileName()
+*/
+void Application::setUpdateSourcesXMLFileName(const QString &fileName)
+{
+ d->updateSourcesInfo->setFileName(fileName);
+}
+
+/*!
+ Returns the Update Sources XML file name.
+*/
+QString Application::updateSourcesXMLFileName() const
+{
+ return d->updateSourcesInfo->fileName();
+}
+
+/*!
+ Returns the \ref UpdateSourcesInfo object associated with this application.
+*/
+UpdateSourcesInfo* Application::updateSourcesInfo() const
+{
+ return d->updateSourcesInfo;
+}
+
+void Application::printError(int errorCode, const QString &error)
+{
+ qDebug() << errorCode << error;
+}
+
+QStringList Application::filesForDelayedDeletion() const
+{
+ return d->filesForDelayedDeletion;
+}
+
+void Application::addFilesForDelayedDeletion(const QStringList &files)
+{
+ d->filesForDelayedDeletion << files;
+ d->configurationInterface->setValue(QLatin1String("FilesForDelayedDeletion"), d->filesForDelayedDeletion);
+}
diff --git a/src/libs/kdtools/kdupdaterapplication.h b/src/libs/kdtools/kdupdaterapplication.h
new file mode 100644
index 000000000..f1ca9d612
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterapplication.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_APPLICATION_H
+#define KD_UPDATER_APPLICATION_H
+
+#include "kdupdater.h"
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QUrl;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+
+class PackagesInfo;
+class UpdateSourcesInfo;
+
+class ConfigurationInterface
+{
+public:
+ virtual ~ConfigurationInterface();
+ virtual QVariant value(const QString &key ) const = 0;
+ virtual void setValue(const QString &key, const QVariant &value) = 0;
+};
+
+class KDTOOLS_EXPORT Application : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit Application(ConfigurationInterface *config = 0, QObject *parent = 0);
+ ~Application();
+
+ static Application *instance();
+
+ void setApplicationDirectory(const QString &dir);
+ QString applicationDirectory() const;
+
+ QString applicationName() const;
+ QString applicationVersion() const;
+ int compatLevel() const;
+
+ void setPackagesXMLFileName(const QString &fileName);
+ QString packagesXMLFileName() const;
+ PackagesInfo *packagesInfo() const;
+
+ void addUpdateSource(const QString &name, const QString &title,
+ const QString &description, const QUrl &url, int priority = -1);
+
+ void setUpdateSourcesXMLFileName(const QString &fileName);
+ QString updateSourcesXMLFileName() const;
+ UpdateSourcesInfo *updateSourcesInfo() const;
+
+ QStringList filesForDelayedDeletion() const;
+ void addFilesForDelayedDeletion(const QStringList &files);
+
+public Q_SLOTS:
+ void printError(int errorCode, const QString &error);
+
+private:
+ struct ApplicationData;
+ ApplicationData *d;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_APPLICATION_H
diff --git a/src/libs/kdtools/kdupdaterfiledownloader.cpp b/src/libs/kdtools/kdupdaterfiledownloader.cpp
new file mode 100644
index 000000000..c36b78402
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterfiledownloader.cpp
@@ -0,0 +1,1294 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterfiledownloader_p.h"
+#include "kdupdaterfiledownloaderfactory.h"
+
+#include <QFile>
+#include <QFtp>
+#include <QNetworkAccessManager>
+#include <QNetworkProxyFactory>
+#include <QPointer>
+#include <QUrl>
+#include <QTemporaryFile>
+#include <QFileInfo>
+#include <QCryptographicHash>
+#include <QThreadPool>
+#include <QDebug>
+
+#include <QBasicTimer>
+#include <QTimerEvent>
+
+using namespace KDUpdater;
+
+static double calcProgress(qint32 done, qint32 total)
+{
+ return total ? (double(done) / double(total)) : 0;
+}
+
+static QString format(double data)
+{
+ if (data < 1024.0)
+ return KDUpdater::FileDownloader::tr("%L1 B").arg(data);
+ data /= 1024.0;
+ if (data < 1024.0)
+ return KDUpdater::FileDownloader::tr("%L1 KB").arg(data, 0, 'f', 2);
+ data /= 1024.0;
+ if (data < 1024.0)
+ return KDUpdater::FileDownloader::tr("%L1 MB").arg(data, 0, 'f', 2);
+ data /= 1024.0;
+ return KDUpdater::FileDownloader::tr("%L1 GB").arg(data, 0, 'f', 2);
+}
+
+QByteArray KDUpdater::calculateHash(QIODevice* device, QCryptographicHash::Algorithm algo)
+{
+ Q_ASSERT(device);
+ QCryptographicHash hash(algo);
+ QByteArray buffer;
+ buffer.resize(512 * 1024);
+ while (true) {
+ const qint64 numRead = device->read(buffer.data(), buffer.size());
+ if (numRead <= 0)
+ return hash.result();
+ hash.addData(buffer.constData(), numRead);
+ }
+ return QByteArray(); // never reached
+}
+
+QByteArray KDUpdater::calculateHash(const QString &path, QCryptographicHash::Algorithm algo)
+{
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly))
+ return QByteArray();
+ return calculateHash(&file, algo);
+}
+
+
+// -- HashVerificationJob
+
+class HashVerificationJob::Private
+{
+public:
+ Private()
+ : hash(QCryptographicHash::Sha1)
+ , error(HashVerificationJob::ReadError)
+ , timerId(-1) { }
+
+ QPointer<QIODevice> device;
+ QByteArray sha1Sum;
+ QCryptographicHash hash;
+ HashVerificationJob::Error error;
+ int timerId;
+};
+
+HashVerificationJob::HashVerificationJob(QObject* parent)
+ : QObject(parent)
+ , d(new Private)
+{
+}
+
+HashVerificationJob::~HashVerificationJob()
+{
+ delete d;
+}
+
+void HashVerificationJob::setDevice(QIODevice* dev)
+{
+ d->device = dev;
+}
+
+void HashVerificationJob::setSha1Sum(const QByteArray &sum)
+{
+ d->sha1Sum = sum;
+}
+
+int HashVerificationJob::error() const
+{
+ return d->error;
+}
+
+bool HashVerificationJob::hasError() const
+{
+ return d->error != NoError;
+}
+
+void HashVerificationJob::start()
+{
+ Q_ASSERT(d->device);
+ d->timerId = startTimer(0);
+}
+
+void HashVerificationJob::emitFinished()
+{
+ emit finished(this);
+ deleteLater();
+}
+
+void HashVerificationJob::timerEvent(QTimerEvent*)
+{
+ Q_ASSERT(d->timerId >= 0);
+ if (d->sha1Sum.isEmpty()) {
+ killTimer(d->timerId);
+ d->timerId = -1;
+ d->error = NoError;
+ d->device->close();
+ emitFinished();
+ return;
+ }
+
+ QByteArray buf;
+ buf.resize(128 * 1024);
+ const qint64 read = d->device->read(buf.data(), buf.size());
+ if (read > 0) {
+ d->hash.addData(buf.constData(), read);
+ return;
+ }
+
+ d->error = d->hash.result() == d->sha1Sum ? NoError : SumsDifferError;
+ killTimer(d->timerId);
+ d->timerId = -1;
+ emitFinished();
+}
+
+
+// -- KDUpdater::FileDownloader
+
+/*!
+ \internal
+ \ingroup kdupdater
+ \class KDUpdater::FileDownloader kdupdaterfiledownloader.h
+
+ Base class for file downloaders used in KDUpdater. File downloaders are used by
+ the KDUpdater::Update class to download update files. Each subclass of FileDownloader
+ can download file from a specific category of sources (e.g. local, ftp, http etc).
+
+ This is an internal class, not a part of the public API. Currently we have three
+ subclasses of FileDownloader
+ \li LocalFileDownloader - downloads from the local file system
+ \li FtpDownloader - downloads from a FTP site
+ \li HttpDownloader - downloads from a HTTP site
+
+ Usage
+
+ \code
+ KDUpdater::FileDownloader* downloader = new KDUpdater::(some subclass name)
+
+ downloader->setUrl(url);
+ downloader->download();
+
+ // wait for downloadCompleted() signal
+
+ QString downloadedFile = downloader->downloadedFileName();
+ \endcode
+*/
+
+struct KDUpdater::FileDownloader::Private
+{
+ Private()
+ : autoRemove(true)
+ , m_speedTimerInterval(100)
+ , m_bytesReceived(0)
+ , m_bytesToReceive(0)
+ , m_currentSpeedBin(0)
+ , m_sampleIndex(0)
+ , m_downloadSpeed(0)
+ , m_factory(0)
+ {
+ memset(m_samples, 0, sizeof(m_samples));
+ }
+
+ ~Private()
+ {
+ delete m_factory;
+ }
+
+ QUrl url;
+ QString scheme;
+ QByteArray sha1Sum;
+ QString errorString;
+ bool autoRemove;
+ bool followRedirect;
+
+ QBasicTimer m_timer;
+ int m_speedTimerInterval;
+
+ qint64 m_bytesReceived;
+ qint64 m_bytesToReceive;
+
+ mutable qint64 m_samples[50];
+ mutable qint64 m_currentSpeedBin;
+ mutable quint32 m_sampleIndex;
+ mutable qint64 m_downloadSpeed;
+
+ QAuthenticator m_authenticator;
+ FileDownloaderProxyFactory *m_factory;
+};
+
+KDUpdater::FileDownloader::FileDownloader(const QString &scheme, QObject *parent)
+ : QObject(parent)
+ , d(new Private)
+{
+ d->scheme = scheme;
+ d->followRedirect = false;
+}
+
+KDUpdater::FileDownloader::~FileDownloader()
+{
+ delete d;
+}
+
+void KDUpdater::FileDownloader::setUrl(const QUrl &url)
+{
+ d->url = url;
+}
+
+QUrl KDUpdater::FileDownloader::url() const
+{
+ return d->url;
+}
+
+void KDUpdater::FileDownloader::setSha1Sum(const QByteArray &sum)
+{
+ d->sha1Sum = sum;
+}
+
+QByteArray KDUpdater::FileDownloader::sha1Sum() const
+{
+ return d->sha1Sum;
+}
+
+QString FileDownloader::errorString() const
+{
+ return d->errorString;
+}
+
+void FileDownloader::setDownloadAborted(const QString &error)
+{
+ d->errorString = error;
+ emit downloadStatus(error);
+ emit downloadAborted(error);
+}
+
+void KDUpdater::FileDownloader::setDownloadCompleted(const QString &path)
+{
+ HashVerificationJob *job = new HashVerificationJob;
+ QFile *file = new QFile(path, job);
+ if (!file->open(QIODevice::ReadOnly)) {
+ emit downloadProgress(1);
+ onError();
+ setDownloadAborted(tr("Could not reopen downloaded file %1 for reading: %2").arg(path,
+ file->errorString()));
+ delete job;
+ return;
+ }
+
+ job->setDevice(file);
+ job->setSha1Sum(d->sha1Sum);
+ connect(job, SIGNAL(finished(KDUpdater::HashVerificationJob*)), this,
+ SLOT(sha1SumVerified(KDUpdater::HashVerificationJob*)));
+ job->start();
+}
+
+void KDUpdater::FileDownloader::setDownloadCanceled()
+{
+ emit downloadCanceled();
+ emit downloadStatus(tr("Download canceled."));
+}
+
+void KDUpdater::FileDownloader::sha1SumVerified(KDUpdater::HashVerificationJob *job)
+{
+ if (job->hasError()) {
+ onError();
+ setDownloadAborted(tr("Cryptographic hashes do not match."));
+ } else {
+ onSuccess();
+ emit downloadCompleted();
+ emit downloadStatus(tr("Download finished."));
+ }
+}
+
+QString KDUpdater::FileDownloader::scheme() const
+{
+ return d->scheme;
+}
+
+void KDUpdater::FileDownloader::setAutoRemoveDownloadedFile(bool val)
+{
+ d->autoRemove = val;
+}
+
+void KDUpdater::FileDownloader::setFollowRedirects(bool val)
+{
+ d->followRedirect = val;
+}
+
+bool KDUpdater::FileDownloader::followRedirects() const
+{
+ return d->followRedirect;
+}
+
+bool KDUpdater::FileDownloader::isAutoRemoveDownloadedFile() const
+{
+ return d->autoRemove;
+}
+
+void KDUpdater::FileDownloader::download()
+{
+ QMetaObject::invokeMethod(this, "doDownload", Qt::QueuedConnection);
+}
+
+void KDUpdater::FileDownloader::cancelDownload()
+{
+ // Do nothing
+}
+
+void KDUpdater::FileDownloader::runDownloadSpeedTimer()
+{
+ if (!d->m_timer.isActive())
+ d->m_timer.start(d->m_speedTimerInterval, this);
+}
+
+void KDUpdater::FileDownloader::stopDownloadSpeedTimer()
+{
+ d->m_timer.stop();
+}
+
+void KDUpdater::FileDownloader::addSample(qint64 sample)
+{
+ d->m_currentSpeedBin += sample;
+}
+
+int KDUpdater::FileDownloader::downloadSpeedTimerId() const
+{
+ return d->m_timer.timerId();
+}
+
+void KDUpdater::FileDownloader::setProgress(qint64 bytesReceived, qint64 bytesToReceive)
+{
+ d->m_bytesReceived = bytesReceived;
+ d->m_bytesToReceive = bytesToReceive;
+}
+
+void KDUpdater::FileDownloader::emitDownloadSpeed()
+{
+ unsigned int windowSize = sizeof(d->m_samples) / sizeof(qint64);
+
+ // add speed of last time bin to the window
+ d->m_samples[d->m_sampleIndex % windowSize] = d->m_currentSpeedBin;
+ d->m_currentSpeedBin = 0; // reset bin for next time interval
+
+ // advance the sample index
+ d->m_sampleIndex++;
+ d->m_downloadSpeed = 0;
+
+ // dynamic window size until the window is completely filled
+ if (d->m_sampleIndex < windowSize)
+ windowSize = d->m_sampleIndex;
+
+ for (unsigned int i = 0; i < windowSize; ++i)
+ d->m_downloadSpeed += d->m_samples[i];
+
+ d->m_downloadSpeed /= windowSize; // computer average
+ d->m_downloadSpeed *= 1000.0 / d->m_speedTimerInterval; // rescale to bytes/second
+
+ emit downloadSpeed(d->m_downloadSpeed);
+}
+
+void KDUpdater::FileDownloader::emitDownloadStatus()
+{
+ QString status;
+ if (d->m_bytesToReceive > 0) {
+ QString bytesReceived = format(d->m_bytesReceived);
+ const QString bytesToReceive = format(d->m_bytesToReceive);
+
+ const QString tmp = bytesToReceive.mid(bytesToReceive.indexOf(QLatin1Char(' ')));
+ if (bytesReceived.endsWith(tmp))
+ bytesReceived.chop(tmp.length());
+
+ status = bytesReceived + tr(" of ") + bytesToReceive;
+ } else {
+ if (d->m_bytesReceived > 0)
+ status = format(d->m_bytesReceived) + tr(" downloaded.");
+ }
+
+ status += QLatin1String(" (") + format(d->m_downloadSpeed) + tr("/sec") + QLatin1Char(')');
+ if (d->m_bytesToReceive > 0 && d->m_downloadSpeed > 0) {
+ const qint64 time = (d->m_bytesToReceive - d->m_bytesReceived) / d->m_downloadSpeed;
+
+ int s = time % 60;
+ const int d = time / 86400;
+ const int h = (time / 3600) - (d * 24);
+ const int m = (time / 60) - (d * 1440) - (h * 60);
+
+ QString days;
+ if (d > 0)
+ days = QString::number(d) + (d < 2 ? tr(" day") : tr(" days")) + QLatin1String(", ");
+
+ QString hours;
+ if (h > 0)
+ hours = QString::number(h) + (h < 2 ? tr(" hour") : tr(" hours")) + QLatin1String(", ");
+
+ QString minutes;
+ if (m > 0)
+ minutes = QString::number(m) + (m < 2 ? tr(" minute") : tr(" minutes"));
+
+ QString seconds;
+ if (s >= 0 && minutes.isEmpty()) {
+ s = (s <= 0 ? 1 : s);
+ seconds = QString::number(s) + (s < 2 ? tr(" second") : tr(" seconds"));
+ }
+ status += tr(" - ") + days + hours + minutes + seconds + tr(" remaining.");
+ } else {
+ status += tr(" - unknown time remaining.");
+ }
+
+ emit downloadStatus(status);
+}
+
+void KDUpdater::FileDownloader::emitDownloadProgress()
+{
+ emit downloadProgress(d->m_bytesReceived, d->m_bytesToReceive);
+}
+
+void KDUpdater::FileDownloader::emitEstimatedDownloadTime()
+{
+ if (d->m_bytesToReceive <= 0 || d->m_downloadSpeed <= 0) {
+ emit estimatedDownloadTime(-1);
+ return;
+ }
+ emit estimatedDownloadTime((d->m_bytesToReceive - d->m_bytesReceived) / d->m_downloadSpeed);
+}
+
+/*!
+ Returns a copy of the proxy factory that this FileDownloader object is using to determine the proxies to
+ be used for requests.
+*/
+FileDownloaderProxyFactory *KDUpdater::FileDownloader::proxyFactory() const
+{
+ if (d->m_factory)
+ return d->m_factory->clone();
+ return 0;
+}
+
+/*!
+ Sets the proxy factory for this class to be \a factory. A proxy factory is used to determine a more
+ specific list of proxies to be used for a given request, instead of trying to use the same proxy value
+ for all requests. This might only be of use for http or ftp requests.
+*/
+void KDUpdater::FileDownloader::setProxyFactory(FileDownloaderProxyFactory *factory)
+{
+ delete d->m_factory;
+ d->m_factory = factory;
+}
+
+/*!
+ Returns a copy of the authenticator that this FileDownloader object is using to set the username and
+ password for download request.
+*/
+QAuthenticator KDUpdater::FileDownloader::authenticator() const
+{
+ return d->m_authenticator;
+}
+
+/*!
+ Sets the authenticator object for this class to be \a authenticator. A authenticator is used to
+ pass on the required authentication information. This might only be of use for http or ftp requests.
+*/
+void KDUpdater::FileDownloader::setAuthenticator(const QAuthenticator &authenticator)
+{
+ d->m_authenticator = authenticator;
+}
+
+// -- KDUpdater::LocalFileDownloader
+
+/*
+ Even though QFile::copy() does the task of copying local files from one place
+ to another, I prefer to use the timer and copy one block of data per unit time.
+
+ This is because, it is possible that the user of KDUpdater is simultaneously
+ downloading several files. Sometimes in tandem with other file downloaders.
+ If the local file that is being downloaded takes a long time; then that will
+ hang the other downloads.
+
+ On the other hand, local downloads need not actually download the file. It can
+ simply pass on the source file as destination file. At this moment however,
+ I think the user of LocalFileDownloader will assume that the downloaded file
+ can be fiddled around with without worrying about whether it would mess up
+ the original source or not.
+*/
+
+struct KDUpdater::LocalFileDownloader::Private
+{
+ Private()
+ : source(0)
+ , destination(0)
+ , downloaded(false)
+ , timerId(-1)
+ {}
+
+ QFile *source;
+ QFile *destination;
+ QString destFileName;
+ bool downloaded;
+ int timerId;
+};
+
+KDUpdater::LocalFileDownloader::LocalFileDownloader(QObject *parent)
+ : KDUpdater::FileDownloader(QLatin1String("file"), parent)
+ , d (new Private)
+{
+}
+
+KDUpdater::LocalFileDownloader::~LocalFileDownloader()
+{
+ if (this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty())
+ QFile::remove(d->destFileName);
+
+ delete d;
+}
+
+bool KDUpdater::LocalFileDownloader::canDownload() const
+{
+ QFileInfo fi(url().toLocalFile());
+ return fi.exists() && fi.isReadable();
+}
+
+bool KDUpdater::LocalFileDownloader::isDownloaded() const
+{
+ return d->downloaded;
+}
+
+void KDUpdater::LocalFileDownloader::doDownload()
+{
+ // Already downloaded
+ if (d->downloaded)
+ return;
+
+ // Already started downloading
+ if (d->timerId >= 0)
+ return;
+
+ // Open source and destination files
+ QString localFile = this->url().toLocalFile();
+ d->source = new QFile(localFile, this);
+ if (!d->source->open(QFile::ReadOnly)) {
+ onError();
+ setDownloadAborted(tr("Cannot open source file '%1' for reading.").arg(QFileInfo(localFile)
+ .fileName()));
+ return;
+ }
+
+ if (d->destFileName.isEmpty()) {
+ QTemporaryFile *file = new QTemporaryFile(this);
+ file->open();
+ d->destination = file;
+ } else {
+ d->destination = new QFile(d->destFileName, this);
+ d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ }
+
+ if (!d->destination->isOpen()) {
+ onError();
+ setDownloadAborted(tr("Cannot open destination file '%1' for writing.")
+ .arg(QFileInfo(d->destination->fileName()).fileName()));
+ return;
+ }
+
+ runDownloadSpeedTimer();
+ // Start a timer and kickoff the copy process
+ d->timerId = startTimer(0); // as fast as possible
+
+ emit downloadStarted();
+ emit downloadProgress(0);
+}
+
+QString KDUpdater::LocalFileDownloader::downloadedFileName() const
+{
+ return d->destFileName;
+}
+
+void KDUpdater::LocalFileDownloader::setDownloadedFileName(const QString &name)
+{
+ d->destFileName = name;
+}
+
+KDUpdater::LocalFileDownloader *KDUpdater::LocalFileDownloader::clone(QObject *parent) const
+{
+ return new LocalFileDownloader(parent);
+}
+
+void KDUpdater::LocalFileDownloader::cancelDownload()
+{
+ if (d->timerId < 0)
+ return;
+
+ killTimer(d->timerId);
+ d->timerId = -1;
+
+ onError();
+ setDownloadCanceled();
+}
+
+void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == d->timerId) {
+ if (!d->source || !d->destination)
+ return;
+
+ const qint64 blockSize = 32768;
+ QByteArray buffer;
+ buffer.resize(blockSize);
+ const qint64 numRead = d->source->read(buffer.data(), buffer.size());
+ qint64 toWrite = numRead;
+ while (toWrite > 0) {
+ const qint64 numWritten = d->destination->write(buffer.constData() + numRead - toWrite, toWrite);
+ if (numWritten < 0) {
+ killTimer(d->timerId);
+ d->timerId = -1;
+ onError();
+ setDownloadAborted(tr("Writing to %1 failed: %2").arg(d->destination->fileName(),
+ d->destination->errorString()));
+ return;
+ }
+ toWrite -= numWritten;
+ }
+ addSample(numRead);
+
+ if (numRead > 0) {
+ setProgress(d->source->pos(), d->source->size());
+ emit downloadProgress(calcProgress(d->source->pos(), d->source->size()));
+ return;
+ }
+
+ d->destination->flush();
+
+ killTimer(d->timerId);
+ d->timerId = -1;
+
+ setDownloadCompleted(d->destination->fileName());
+ } else if (event->timerId() == downloadSpeedTimerId()) {
+ emitDownloadSpeed();
+ emitDownloadStatus();
+ emitDownloadProgress();
+ emitEstimatedDownloadTime();
+ }
+}
+
+void LocalFileDownloader::onSuccess()
+{
+ d->downloaded = true;
+ d->destFileName = d->destination->fileName();
+ if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination))
+ file->setAutoRemove(false);
+ d->destination->close();
+ delete d->destination;
+ d->destination = 0;
+ delete d->source;
+ d->source = 0;
+ stopDownloadSpeedTimer();
+}
+
+void LocalFileDownloader::onError()
+{
+ d->downloaded = false;
+ d->destFileName.clear();
+ delete d->destination;
+ d->destination = 0;
+ delete d->source;
+ d->source = 0;
+ stopDownloadSpeedTimer();
+}
+
+
+// -- ResourceFileDownloader
+
+struct KDUpdater::ResourceFileDownloader::Private
+{
+ Private()
+ : downloaded(false),
+ timerId(-1)
+ {}
+
+ QString destFileName;
+ bool downloaded;
+ int timerId;
+};
+
+KDUpdater::ResourceFileDownloader::ResourceFileDownloader(QObject *parent)
+ : KDUpdater::FileDownloader(QLatin1String("resource"), parent)
+ , d(new Private)
+{
+}
+
+KDUpdater::ResourceFileDownloader::~ResourceFileDownloader()
+{
+ delete d;
+}
+
+bool KDUpdater::ResourceFileDownloader::canDownload() const
+{
+ QUrl url = this->url();
+ url.setScheme(QString::fromLatin1("file"));
+ QString localFile = QString::fromLatin1(":%1").arg(url.toLocalFile());
+ QFileInfo fi(localFile);
+ return fi.exists() && fi.isReadable();
+}
+
+bool KDUpdater::ResourceFileDownloader::isDownloaded() const
+{
+ return d->downloaded;
+}
+
+void KDUpdater::ResourceFileDownloader::doDownload()
+{
+ // Already downloaded
+ if (d->downloaded)
+ return;
+
+ // Already started downloading
+ if (d->timerId >= 0)
+ return;
+
+ // Open source and destination files
+ QUrl url = this->url();
+ url.setScheme(QString::fromLatin1("file"));
+ d->destFileName = QString::fromLatin1(":%1").arg(url.toLocalFile());
+
+ // Start a timer and kickoff the copy process
+ d->timerId = startTimer(0); // as fast as possible
+ emit downloadStarted();
+ emit downloadProgress(0);
+}
+
+QString KDUpdater::ResourceFileDownloader::downloadedFileName() const
+{
+ return d->destFileName;
+}
+
+void KDUpdater::ResourceFileDownloader::setDownloadedFileName(const QString &/*name*/)
+{
+ Q_ASSERT_X(false, "KDUpdater::ResourceFileDownloader::setDownloadedFileName", "Not supported!");
+}
+
+KDUpdater::ResourceFileDownloader *KDUpdater::ResourceFileDownloader::clone(QObject *parent) const
+{
+ return new ResourceFileDownloader(parent);
+}
+
+void KDUpdater::ResourceFileDownloader::cancelDownload()
+{
+ if (d->timerId < 0)
+ return;
+
+ killTimer(d->timerId);
+ d->timerId = -1;
+
+ setDownloadCanceled();
+}
+
+void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *)
+{
+ killTimer(d->timerId);
+ d->timerId = -1;
+ setDownloadCompleted(d->destFileName);
+}
+
+void KDUpdater::ResourceFileDownloader::onSuccess()
+{
+ d->downloaded = true;
+}
+
+void KDUpdater::ResourceFileDownloader::onError()
+{
+ d->downloaded = false;
+}
+
+
+// -- KDUpdater::FtpFileDownloader
+
+struct KDUpdater::FtpDownloader::Private
+{
+ Private()
+ : ftp(0)
+ , destination(0)
+ , downloaded(false)
+ , ftpCmdId(-1)
+ , aborted(false)
+ {}
+
+ QFtp *ftp;
+ QFile *destination;
+ QString destFileName;
+ bool downloaded;
+ int ftpCmdId;
+ bool aborted;
+};
+
+KDUpdater::FtpDownloader::FtpDownloader(QObject *parent)
+ : KDUpdater::FileDownloader(QLatin1String("ftp"), parent)
+ , d(new Private)
+{
+}
+
+KDUpdater::FtpDownloader::~FtpDownloader()
+{
+ if (this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty())
+ QFile::remove(d->destFileName);
+
+ delete d;
+}
+
+bool KDUpdater::FtpDownloader::canDownload() const
+{
+ // TODO: Check whether the ftp file actually exists or not.
+ return true;
+}
+
+bool KDUpdater::FtpDownloader::isDownloaded() const
+{
+ return d->downloaded;
+}
+
+void KDUpdater::FtpDownloader::doDownload()
+{
+ if (d->downloaded)
+ return;
+
+ if (d->ftp)
+ return;
+
+ d->ftp = new QFtp(this);
+ connect(d->ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool)));
+ connect(d->ftp, SIGNAL(commandStarted(int)), this, SLOT(ftpCmdStarted(int)));
+ connect(d->ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(ftpCmdFinished(int, bool)));
+ connect(d->ftp, SIGNAL(stateChanged(int)), this, SLOT(ftpStateChanged(int)));
+ connect(d->ftp, SIGNAL(dataTransferProgress(qint64, qint64)), this,
+ SLOT(ftpDataTransferProgress(qint64, qint64)));
+ connect(d->ftp, SIGNAL(readyRead()), this, SLOT(ftpReadyRead()));
+
+ if (FileDownloaderProxyFactory *factory = proxyFactory()) {
+ const QList<QNetworkProxy> proxies = factory->queryProxy(QNetworkProxyQuery(url()));
+ if (!proxies.isEmpty())
+ d->ftp->setProxy(proxies.at(0).hostName(), proxies.at(0).port());
+ delete factory;
+ }
+
+ d->ftp->connectToHost(url().host(), url().port(21));
+ d->ftp->login(authenticator().user(), authenticator().password());
+}
+
+QString KDUpdater::FtpDownloader::downloadedFileName() const
+{
+ return d->destFileName;
+}
+
+void KDUpdater::FtpDownloader::setDownloadedFileName(const QString &name)
+{
+ d->destFileName = name;
+}
+
+KDUpdater::FtpDownloader *KDUpdater::FtpDownloader::clone(QObject *parent) const
+{
+ return new FtpDownloader(parent);
+}
+
+void KDUpdater::FtpDownloader::cancelDownload()
+{
+ if (d->ftp) {
+ d->aborted = true;
+ d->ftp->abort();
+ }
+}
+
+void KDUpdater::FtpDownloader::ftpDone(bool error)
+{
+ if (error) {
+ QString errorString;
+ if (d->ftp) {
+ errorString = d->ftp->errorString();
+ d->ftp->deleteLater();
+ d->ftp = 0;
+ d->ftpCmdId = -1;
+ }
+
+ onError();
+
+ if (d->aborted) {
+ d->aborted = false;
+ setDownloadCanceled();
+ } else {
+ setDownloadAborted(errorString);
+ }
+ }
+ //PENDING what about the non-error case??
+}
+
+void KDUpdater::FtpDownloader::ftpCmdStarted(int id)
+{
+ if (id != d->ftpCmdId)
+ return;
+
+ emit downloadStarted();
+ emit downloadProgress(0);
+}
+
+void KDUpdater::FtpDownloader::ftpCmdFinished(int id, bool error)
+{
+ if (id != d->ftpCmdId || error) // PENDING why error -> return??
+ return;
+
+ disconnect(d->ftp, 0, this, 0);
+ d->ftp->deleteLater();
+ d->ftp = 0;
+ d->ftpCmdId = -1;
+ d->destination->flush();
+
+ setDownloadCompleted(d->destination->fileName());
+}
+
+void FtpDownloader::onSuccess()
+{
+ d->downloaded = true;
+ d->destFileName = d->destination->fileName();
+ if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination))
+ file->setAutoRemove(false);
+ delete d->destination;
+ d->destination = 0;
+ stopDownloadSpeedTimer();
+
+}
+
+void FtpDownloader::onError()
+{
+ d->downloaded = false;
+ d->destFileName.clear();
+ delete d->destination;
+ d->destination = 0;
+ stopDownloadSpeedTimer();
+}
+
+void KDUpdater::FtpDownloader::ftpStateChanged(int state)
+{
+ switch(state) {
+ case QFtp::Connected: {
+ // begin the download
+ if (d->destFileName.isEmpty()) {
+ QTemporaryFile *file = new QTemporaryFile(this);
+ file->open(); //PENDING handle error
+ d->destination = file;
+ } else {
+ d->destination = new QFile(d->destFileName, this);
+ d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ }
+ runDownloadSpeedTimer();
+ d->ftpCmdId = d->ftp->get(url().path());
+ } break;
+
+ case QFtp::Unconnected: {
+ // download was unconditionally aborted
+ disconnect(d->ftp, 0, this, 0);
+ d->ftp->deleteLater();
+ d->ftp = 0;
+ d->ftpCmdId = -1;
+ onError();
+ setDownloadAborted(tr("Download was aborted due to network errors."));
+ } break;
+ }
+}
+
+void KDUpdater::FtpDownloader::ftpDataTransferProgress(qint64 done, qint64 total)
+{
+ setProgress(done, total);
+ emit downloadProgress(calcProgress(done, total));
+}
+
+void KDUpdater::FtpDownloader::ftpReadyRead()
+{
+ static QByteArray buffer(16384, '\0');
+ while (d->ftp->bytesAvailable()) {
+ const qint64 read = d->ftp->read(buffer.data(), buffer.size());
+ qint64 written = 0;
+ while (written < read) {
+ const qint64 numWritten = d->destination->write(buffer.data() + written, read - written);
+ if (numWritten < 0) {
+ onError();
+ setDownloadAborted(tr("Cannot download %1: Writing to temporary file failed: %2")
+ .arg(url().toString(), d->destination->errorString()));
+ return;
+ }
+ written += numWritten;
+ }
+ addSample(written);
+ }
+}
+
+void KDUpdater::FtpDownloader::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == downloadSpeedTimerId()) {
+ emitDownloadSpeed();
+ emitDownloadStatus();
+ emitDownloadProgress();
+ emitEstimatedDownloadTime();
+ }
+}
+
+
+// -- KDUpdater::HttpDownloader
+
+struct KDUpdater::HttpDownloader::Private
+{
+ explicit Private(HttpDownloader *qq)
+ : q(qq)
+ , http(0)
+ , destination(0)
+ , downloaded(false)
+ , aborted(false)
+ , retrying(false)
+ , m_authenticationDone(false)
+ {}
+
+ HttpDownloader *const q;
+ QNetworkAccessManager manager;
+ QNetworkReply *http;
+ QFile *destination;
+ QString destFileName;
+ bool downloaded;
+ bool aborted;
+ bool retrying;
+ bool m_authenticationDone;
+
+ void shutDown()
+ {
+ disconnect(http, SIGNAL(finished()), q, SLOT(httpReqFinished()));
+ http->deleteLater();
+ http = 0;
+ destination->close();
+ destination->deleteLater();
+ destination = 0;
+ }
+};
+
+KDUpdater::HttpDownloader::HttpDownloader(QObject *parent)
+ : KDUpdater::FileDownloader(QLatin1String("http"), parent)
+ , d(new Private(this))
+{
+ connect(&d->manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this,
+ SLOT(onAuthenticationRequired(QNetworkReply*, QAuthenticator*)));
+}
+
+KDUpdater::HttpDownloader::~HttpDownloader()
+{
+ if (this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty())
+ QFile::remove(d->destFileName);
+ delete d;
+}
+
+bool KDUpdater::HttpDownloader::canDownload() const
+{
+ // TODO: Check whether the http file actually exists or not.
+ return true;
+}
+
+bool KDUpdater::HttpDownloader::isDownloaded() const
+{
+ return d->downloaded;
+}
+
+void KDUpdater::HttpDownloader::doDownload()
+{
+ if (d->downloaded)
+ return;
+
+ if (d->http)
+ return;
+
+ startDownload(url());
+ runDownloadSpeedTimer();
+}
+
+QString KDUpdater::HttpDownloader::downloadedFileName() const
+{
+ return d->destFileName;
+}
+
+void KDUpdater::HttpDownloader::setDownloadedFileName(const QString &name)
+{
+ d->destFileName = name;
+}
+
+KDUpdater::HttpDownloader *KDUpdater::HttpDownloader::clone(QObject *parent) const
+{
+ return new HttpDownloader(parent);
+}
+
+void KDUpdater::HttpDownloader::httpReadyRead()
+{
+ static QByteArray buffer(16384, '\0');
+ while (d->http->bytesAvailable()) {
+ const qint64 read = d->http->read(buffer.data(), buffer.size());
+ qint64 written = 0;
+ while (written < read) {
+ const qint64 numWritten = d->destination->write(buffer.data() + written, read - written);
+ if (numWritten < 0) {
+ const QString err = d->destination->errorString();
+ d->shutDown();
+ setDownloadAborted(tr("Cannot download %1: Writing to temporary file failed: %2")
+ .arg(url().toString(), err));
+ return;
+ }
+ written += numWritten;
+ }
+ addSample(written);
+ }
+}
+
+void KDUpdater::HttpDownloader::httpError(QNetworkReply::NetworkError)
+{
+ if (!d->aborted)
+ httpDone(true);
+}
+
+void KDUpdater::HttpDownloader::cancelDownload()
+{
+ d->aborted = true;
+ if (d->http) {
+ d->http->abort();
+ httpDone(true);
+ }
+}
+
+void KDUpdater::HttpDownloader::httpDone(bool error)
+{
+ if (error) {
+ QString err;
+ if (d->http) {
+ err = d->http->errorString();
+ d->http->deleteLater();
+ d->http = 0;
+ onError();
+ }
+
+ if (d->aborted) {
+ d->aborted = false;
+ setDownloadCanceled();
+ } else {
+ setDownloadAborted(err);
+ }
+ }
+ //PENDING: what about the non-error case??
+}
+
+void KDUpdater::HttpDownloader::onError()
+{
+ d->downloaded = false;
+ d->destFileName.clear();
+ delete d->destination;
+ d->destination = 0;
+ stopDownloadSpeedTimer();
+}
+
+void KDUpdater::HttpDownloader::onSuccess()
+{
+ d->downloaded = true;
+ d->destFileName = d->destination->fileName();
+ if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination))
+ file->setAutoRemove(false);
+ delete d->destination;
+ d->destination = 0;
+ stopDownloadSpeedTimer();
+}
+
+void KDUpdater::HttpDownloader::httpReqFinished()
+{
+ const QVariant redirect = d->http == 0 ? QVariant()
+ : d->http->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+ const QUrl redirectUrl = redirect.toUrl();
+ if (followRedirects() && redirectUrl.isValid()) {
+ d->shutDown(); // clean the previous download
+ startDownload(redirectUrl);
+ } else {
+ if (d->http == 0)
+ return;
+
+ httpReadyRead();
+ d->destination->flush();
+ setDownloadCompleted(d->destination->fileName());
+ d->http->deleteLater();
+ d->http = 0;
+ }
+}
+
+void KDUpdater::HttpDownloader::httpReadProgress(qint64 done, qint64 total)
+{
+ setProgress(done, total);
+ emit downloadProgress(calcProgress(done, total));
+}
+
+void KDUpdater::HttpDownloader::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == downloadSpeedTimerId()) {
+ emitDownloadSpeed();
+ emitDownloadStatus();
+ emitDownloadProgress();
+ emitEstimatedDownloadTime();
+ }
+}
+
+void KDUpdater::HttpDownloader::startDownload(const QUrl &url)
+{
+ d->m_authenticationDone = false;
+ d->manager.setProxyFactory(proxyFactory());
+ d->http = d->manager.get(QNetworkRequest(url));
+
+ connect(d->http, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
+ connect(d->http, SIGNAL(downloadProgress(qint64, qint64)), this,
+ SLOT(httpReadProgress(qint64, qint64)));
+ connect(d->http, SIGNAL(finished()), this, SLOT(httpReqFinished()));
+ connect(d->http, SIGNAL(error(QNetworkReply::NetworkError)), this,
+ SLOT(httpError(QNetworkReply::NetworkError)));
+
+ if (d->destFileName.isEmpty()) {
+ QTemporaryFile *file = new QTemporaryFile(this);
+ file->open();
+ d->destination = file;
+ } else {
+ d->destination = new QFile(d->destFileName, this);
+ d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ }
+
+ if (!d->destination->isOpen()) {
+ d->shutDown();
+ setDownloadAborted(tr("Cannot download %1: Could not create temporary file: %2").arg(url.toString(),
+ d->destination->errorString()));
+ }
+}
+
+void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
+{
+ qDebug() << reply->readAll();
+ if (!d->m_authenticationDone) {
+ d->m_authenticationDone = true;
+ authenticator->setUser(this->authenticator().user());
+ authenticator->setPassword(this->authenticator().password());
+ }
+}
diff --git a/src/libs/kdtools/kdupdaterfiledownloader.h b/src/libs/kdtools/kdupdaterfiledownloader.h
new file mode 100644
index 000000000..bdba0a859
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterfiledownloader.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_FILE_DOWNLOADER_H
+#define KD_UPDATER_FILE_DOWNLOADER_H
+
+#include "kdupdater.h"
+#include "kdtoolsglobal.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+#include <QtCore/QCryptographicHash>
+
+#include <QtNetwork/QAuthenticator>
+
+namespace KDUpdater {
+
+KDTOOLS_EXPORT QByteArray calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo);
+KDTOOLS_EXPORT QByteArray calculateHash(const QString &path, QCryptographicHash::Algorithm algo);
+
+class HashVerificationJob;
+class FileDownloaderProxyFactory;
+
+class KDTOOLS_EXPORT FileDownloader : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool autoRemoveDownloadedFile READ isAutoRemoveDownloadedFile WRITE setAutoRemoveDownloadedFile)
+ Q_PROPERTY(QUrl url READ url WRITE setUrl)
+ Q_PROPERTY(QString scheme READ scheme)
+
+public:
+ explicit FileDownloader(const QString &scheme, QObject *parent = 0);
+ ~FileDownloader();
+
+ void setUrl(const QUrl &url);
+ QUrl url() const;
+
+ void setSha1Sum(const QByteArray &sha1);
+ QByteArray sha1Sum() const;
+
+ QString errorString() const;
+ QString scheme() const;
+
+ virtual bool canDownload() const = 0;
+ virtual bool isDownloaded() const = 0;
+ virtual QString downloadedFileName() const = 0;
+ virtual void setDownloadedFileName(const QString &name) = 0;
+ virtual FileDownloader *clone(QObject *parent=0) const = 0;
+
+ void download();
+
+ void setAutoRemoveDownloadedFile(bool val);
+ bool isAutoRemoveDownloadedFile() const;
+
+ void setFollowRedirects(bool val);
+ bool followRedirects() const;
+
+ FileDownloaderProxyFactory *proxyFactory() const;
+ void setProxyFactory(FileDownloaderProxyFactory *factory);
+
+ QAuthenticator authenticator() const;
+ void setAuthenticator(const QAuthenticator &authenticator);
+
+public Q_SLOTS:
+ virtual void cancelDownload();
+ void sha1SumVerified(KDUpdater::HashVerificationJob *job);
+
+protected:
+ virtual void onError() = 0;
+ virtual void onSuccess() = 0;
+
+Q_SIGNALS:
+ void downloadStarted();
+ void downloadCanceled();
+
+ void downloadProgress(double progress);
+ void estimatedDownloadTime(int seconds);
+ void downloadSpeed(qint64 bytesPerSecond);
+ void downloadStatus(const QString &status);
+ void downloadProgress(qint64 bytesReceived, qint64 bytesToReceive);
+
+#ifndef Q_MOC_RUN
+private:
+#endif
+ void downloadCompleted();
+ void downloadAborted(const QString &errorMessage);
+
+protected:
+ void setDownloadCanceled();
+ void setDownloadCompleted(const QString &filepath);
+ void setDownloadAborted(const QString &error);
+
+ void runDownloadSpeedTimer();
+ void stopDownloadSpeedTimer();
+
+ void addSample(qint64 sample);
+ int downloadSpeedTimerId() const;
+ void setProgress(qint64 bytesReceived, qint64 bytesToReceive);
+
+ void emitDownloadSpeed();
+ void emitDownloadStatus();
+ void emitDownloadProgress();
+ void emitEstimatedDownloadTime();
+
+private Q_SLOTS:
+ virtual void doDownload() = 0;
+
+private:
+ struct Private;
+ Private *d;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_FILE_DOWNLOADER_H
diff --git a/src/libs/kdtools/kdupdaterfiledownloader_p.h b/src/libs/kdtools/kdupdaterfiledownloader_p.h
new file mode 100644
index 000000000..127c3f5d9
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterfiledownloader_p.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_FILE_DOWNLOADER_P_H
+#define KD_UPDATER_FILE_DOWNLOADER_P_H
+
+#include "kdupdaterfiledownloader.h"
+
+#include <QtCore/QCryptographicHash>
+#include <QtNetwork/QNetworkReply>
+
+// these classes are not a part of the public API
+
+namespace KDUpdater {
+
+//TODO make it a KDJob once merged
+class HashVerificationJob : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Error {
+ NoError = 0,
+ ReadError = 128,
+ SumsDifferError
+ };
+
+ explicit HashVerificationJob(QObject *parent = 0);
+ ~HashVerificationJob();
+
+ void setDevice(QIODevice *dev);
+ void setSha1Sum(const QByteArray &data);
+
+ bool hasError() const;
+ int error() const;
+
+ void start();
+
+Q_SIGNALS:
+ void finished(KDUpdater::HashVerificationJob *);
+
+private:
+ void emitFinished();
+ void timerEvent(QTimerEvent *te);
+
+private:
+ class Private;
+ Private *d;
+};
+
+class LocalFileDownloader : public FileDownloader
+{
+ Q_OBJECT
+
+public:
+ explicit LocalFileDownloader(QObject *parent = 0);
+ ~LocalFileDownloader();
+
+ bool canDownload() const;
+ bool isDownloaded() const;
+ QString downloadedFileName() const;
+ void setDownloadedFileName(const QString &name);
+ LocalFileDownloader *clone(QObject *parent = 0) const;
+
+public Q_SLOTS:
+ void cancelDownload();
+
+protected:
+ void timerEvent(QTimerEvent *te);
+ void onError();
+ void onSuccess();
+
+private Q_SLOTS:
+ void doDownload();
+
+private:
+ struct Private;
+ Private *d;
+};
+
+class ResourceFileDownloader : public FileDownloader
+{
+ Q_OBJECT
+
+public:
+ explicit ResourceFileDownloader(QObject *parent = 0);
+ ~ResourceFileDownloader();
+
+ bool canDownload() const;
+ bool isDownloaded() const;
+ QString downloadedFileName() const;
+ void setDownloadedFileName(const QString &name);
+ ResourceFileDownloader *clone(QObject *parent = 0) const;
+
+public Q_SLOTS:
+ void cancelDownload();
+
+protected:
+ void timerEvent(QTimerEvent *te);
+ void onError();
+ void onSuccess();
+
+private Q_SLOTS:
+ void doDownload();
+
+private:
+ struct Private;
+ Private *d;
+};
+
+class FtpDownloader : public FileDownloader
+{
+ Q_OBJECT
+
+public:
+ explicit FtpDownloader(QObject *parent = 0);
+ ~FtpDownloader();
+
+ bool canDownload() const;
+ bool isDownloaded() const;
+ QString downloadedFileName() const;
+ void setDownloadedFileName(const QString &name);
+ FtpDownloader *clone(QObject *parent = 0) const;
+
+public Q_SLOTS:
+ void cancelDownload();
+
+protected:
+ void onError();
+ void onSuccess();
+ void timerEvent(QTimerEvent *event);
+
+private Q_SLOTS:
+ void doDownload();
+
+ void ftpDone(bool error);
+ void ftpCmdStarted(int id);
+ void ftpCmdFinished(int id, bool error);
+ void ftpStateChanged(int state);
+ void ftpDataTransferProgress(qint64 done, qint64 total);
+ void ftpReadyRead();
+
+private:
+ struct Private;
+ Private *d;
+};
+
+class HttpDownloader : public FileDownloader
+{
+ Q_OBJECT
+
+public:
+ explicit HttpDownloader(QObject *parent = 0);
+ ~HttpDownloader();
+
+ bool canDownload() const;
+ bool isDownloaded() const;
+ QString downloadedFileName() const;
+ void setDownloadedFileName(const QString &name);
+ HttpDownloader *clone(QObject *parent = 0) const;
+
+public Q_SLOTS:
+ void cancelDownload();
+
+protected:
+ void onError();
+ void onSuccess();
+ void timerEvent(QTimerEvent *event);
+
+private Q_SLOTS:
+ void doDownload();
+
+ void httpReadyRead();
+ void httpReadProgress(qint64 done, qint64 total);
+ void httpError(QNetworkReply::NetworkError);
+ void httpDone(bool error);
+ void httpReqFinished();
+ void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
+
+private:
+ void startDownload(const QUrl &url);
+
+private:
+ struct Private;
+ Private *d;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_FILE_DOWNLOADER_P_H
diff --git a/src/libs/kdtools/kdupdaterfiledownloaderfactory.cpp b/src/libs/kdtools/kdupdaterfiledownloaderfactory.cpp
new file mode 100644
index 000000000..8be508cc7
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterfiledownloaderfactory.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterfiledownloaderfactory.h"
+#include "kdupdaterfiledownloader_p.h"
+
+/*!
+ \internal
+ \ingroup kdupdater
+ \class KDUpdater::FileDownloaderFactory kdupdaterfiledownloaderfactory.h
+ \brief Factory for \ref KDUpdater::FileDownloader
+
+ This class acts as a factory for \ref KDUpdater::FileDownloader. You can register
+ one or more file downloaders with this factory and query them based on their scheme.
+
+ This class follows the singleton design pattern. Only one instance of this class can
+ be created and its reference can be fetched from the \ref instance() method.
+*/
+
+using namespace KDUpdater;
+
+struct FileDownloaderFactory::FileDownloaderFactoryData
+{
+ FileDownloaderFactoryData() : m_factory(0) {}
+ ~FileDownloaderFactoryData() { delete m_factory; }
+
+ bool m_followRedirects;
+ FileDownloaderProxyFactory *m_factory;
+};
+
+FileDownloaderFactory& FileDownloaderFactory::instance()
+{
+ static KDUpdater::FileDownloaderFactory theFactory;
+ return theFactory;
+}
+
+/*!
+ Constructor
+*/
+FileDownloaderFactory::FileDownloaderFactory()
+ : d (new FileDownloaderFactoryData)
+{
+ // Register the default file downloader set
+ registerFileDownloader<LocalFileDownloader>( QLatin1String("file"));
+ registerFileDownloader<FtpDownloader>(QLatin1String("ftp"));
+ registerFileDownloader<HttpDownloader>(QLatin1String("http"));
+ registerFileDownloader<ResourceFileDownloader >(QLatin1String("resource"));
+ d->m_followRedirects = false;
+}
+
+bool FileDownloaderFactory::followRedirects()
+{
+ return FileDownloaderFactory::instance().d->m_followRedirects;
+}
+
+void FileDownloaderFactory::setFollowRedirects(bool val)
+{
+ FileDownloaderFactory::instance().d->m_followRedirects = val;
+}
+
+void FileDownloaderFactory::setProxyFactory(FileDownloaderProxyFactory *factory)
+{
+ delete FileDownloaderFactory::instance().d->m_factory;
+ FileDownloaderFactory::instance().d->m_factory = factory;
+}
+
+FileDownloaderFactory::~FileDownloaderFactory()
+{
+ delete d;
+}
+
+/*!
+ Returns a new instance to the \ref KDUpdater::FileDownloader based whose scheme is equal to the string
+ passed as parameter to this function.
+ \note Ownership of this object remains to the programmer.
+*/
+FileDownloader *FileDownloaderFactory::create(const QString &scheme, QObject *parent) const
+{
+ FileDownloader *downloader = KDGenericFactory<FileDownloader>::create(scheme);
+ if (downloader != 0) {
+ downloader->setParent(parent);
+ downloader->setFollowRedirects(d->m_followRedirects);
+ if (d->m_factory)
+ downloader->setProxyFactory(d->m_factory->clone());
+ }
+ return downloader;
+}
+
+/*!
+ KDUpdater::FileDownloaderFactory::registerFileDownlooader
+ Registers a new file downloader with the factory. If there is already a downloader with the same scheme,
+ the downloader is replaced. The ownership of the downloader is transfered to the factory.
+*/
diff --git a/src/libs/kdtools/kdupdaterfiledownloaderfactory.h b/src/libs/kdtools/kdupdaterfiledownloaderfactory.h
new file mode 100644
index 000000000..e27cb6f7e
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterfiledownloaderfactory.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_FILE_DOWNLOADER_FACTORY_H
+#define KD_UPDATER_FILE_DOWNLOADER_FACTORY_H
+
+#include "kdupdater.h"
+#include <kdgenericfactory.h>
+
+#include <QtCore/QStringList>
+#include <QtCore/QUrl>
+
+#include <QtNetwork/QNetworkProxyFactory>
+
+QT_BEGIN_NAMESPACE
+class QObject;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+
+class FileDownloader;
+
+class KDTOOLS_EXPORT FileDownloaderProxyFactory : public QNetworkProxyFactory
+{
+ public:
+ virtual ~FileDownloaderProxyFactory() {}
+ virtual FileDownloaderProxyFactory *clone() const = 0;
+};
+
+class KDTOOLS_EXPORT FileDownloaderFactory : public KDGenericFactory<FileDownloader>
+{
+ Q_DISABLE_COPY(FileDownloaderFactory)
+
+public:
+ static FileDownloaderFactory &instance();
+ ~FileDownloaderFactory();
+
+ template<typename T>
+ void registerFileDownloader(const QString &scheme)
+ {
+ registerProduct<T>(scheme);
+ }
+ FileDownloader *create(const QString &scheme, QObject *parent = 0) const;
+
+ static bool followRedirects();
+ static void setFollowRedirects(bool val);
+
+ static void setProxyFactory(FileDownloaderProxyFactory *factory);
+
+private:
+ FileDownloaderFactory();
+
+private:
+ struct FileDownloaderFactoryData;
+ FileDownloaderFactoryData *d;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_FILE_DOWNLOADER_FACTORY_H
diff --git a/src/libs/kdtools/kdupdaterpackagesinfo.cpp b/src/libs/kdtools/kdupdaterpackagesinfo.cpp
new file mode 100644
index 000000000..5a23c7d1a
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterpackagesinfo.cpp
@@ -0,0 +1,581 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterpackagesinfo.h"
+#include "kdupdaterapplication.h"
+
+#include <QFileInfo>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QVector>
+
+using namespace KDUpdater;
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::PackagesInfo kdupdaterpackagesinfo.h KDUpdaterPackagesInfo
+ \brief Provides access to information about packages installed on the application side.
+
+ This class parses the XML package file specified via the setFileName() method and
+ provides access to the the information defined within the package file through an
+ easy to use API. You can:
+ \li get application name via the \ref applicationName() method
+ \li get application version via the \ref applicationVersion() method
+ \li get information about the number of packages installed and their meta-data via the
+ \ref packageInfoCount() and \ref packageInfo() methods.
+
+ Instances of this class cannot be created. Each instance of \ref KDUpdater::Application
+ has one instance of this class associated with it. You can fetch a pointer to an instance
+ of this class for an application via the \ref KDUpdater::Application::packagesInfo()
+ method.
+*/
+
+/*! \enum UpdatePackagesInfo::Error
+ * Error codes related to retrieving update sources
+ */
+
+/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NoError
+ * No error occurred
+ */
+
+/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NotYetReadError
+ * The package information was not parsed yet from the XML file
+ */
+
+/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::CouldNotReadPackageFileError
+ * the specified update source file could not be read (does not exist or not readable)
+ */
+
+/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidXmlError
+ * The source file contains invalid XML.
+ */
+
+/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidContentError
+ * The source file contains valid XML, but does not match the expected format for package descriptions
+ */
+
+struct PackagesInfo::PackagesInfoData
+{
+ PackagesInfoData() :
+ application(0),
+ error(PackagesInfo::NotYetReadError),
+ compatLevel(-1),
+ modified(false)
+ {}
+ Application *application;
+ QString errorMessage;
+ PackagesInfo::Error error;
+ QString fileName;
+ QString applicationName;
+ QString applicationVersion;
+ int compatLevel;
+ bool modified;
+
+ QVector<PackageInfo> packageInfoList;
+
+ void addPackageFrom(const QDomElement &packageE);
+ void setInvalidContentError(const QString &detail);
+};
+
+void PackagesInfo::PackagesInfoData::setInvalidContentError(const QString &detail)
+{
+ error = PackagesInfo::InvalidContentError;
+ errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail);
+}
+
+/*!
+ \internal
+*/
+PackagesInfo::PackagesInfo(Application *application)
+ : QObject(application),
+ d(new PackagesInfoData())
+{
+ d->application = application;
+}
+
+/*!
+ \internal
+*/
+PackagesInfo::~PackagesInfo()
+{
+ writeToDisk();
+ delete d;
+}
+
+/*!
+ Returns a pointer to the application, whose package information this class provides
+ access to.
+*/
+Application *PackagesInfo::application() const
+{
+ return d->application;
+}
+
+/*!
+ Returns true if the PackagesInfo are valid else false is returned in which case
+ the \a errorString() method can be used to receive a describing error message.
+*/
+bool PackagesInfo::isValid() const
+{
+ if (!d->fileName.isEmpty())
+ return d->error <= NotYetReadError;
+ return d->error == NoError;
+}
+
+/*!
+ Returns a human-readable error message.
+*/
+QString PackagesInfo::errorString() const
+{
+ return d->errorMessage;
+}
+
+PackagesInfo::Error PackagesInfo::error() const
+{
+ return d->error;
+}
+
+/*!
+ Sets the complete file name of the Packages.xml file. The function also issues a call to
+ \ref refresh() to reload package information from the XML file.
+
+ \sa KDUpdater::Application::setPackagesXMLFileName()
+*/
+void PackagesInfo::setFileName(const QString &fileName)
+{
+ if (d->fileName == fileName)
+ return;
+
+ d->fileName = fileName;
+ refresh();
+}
+
+/*!
+ Returns the name of the Packages.xml file that this class referred to.
+*/
+QString PackagesInfo::fileName() const
+{
+ return d->fileName;
+}
+
+/*!
+ Sets the application name. By default this is the name specified in
+ the ApplicationName XML element of the Packages.xml file.
+*/
+void PackagesInfo::setApplicationName(const QString &name)
+{
+ d->applicationName = name;
+ d->modified = true;
+}
+
+/*!
+ Returns the application name.
+*/
+QString PackagesInfo::applicationName() const
+{
+ return d->applicationName;
+}
+
+/*!
+ Sets the application version. By default this is the version specified
+ in the ApplicationVersion XML element of Packages.xml.
+*/
+void PackagesInfo::setApplicationVersion(const QString &version)
+{
+ d->applicationVersion = version;
+ d->modified = true;
+}
+
+/*!
+ Returns the application version.
+*/
+QString PackagesInfo::applicationVersion() const
+{
+ return d->applicationVersion;
+}
+
+/*!
+ Returns the number of \ref KDUpdater::PackageInfo objects contained in this class.
+*/
+int PackagesInfo::packageInfoCount() const
+{
+ return d->packageInfoList.count();
+}
+
+/*!
+ Returns the package info structure (\ref KDUpdater::PackageInfo) at index. If index is
+ out of range then an empty package info structure is returned.
+*/
+PackageInfo PackagesInfo::packageInfo(int index) const
+{
+ if (index < 0 || index >= d->packageInfoList.count())
+ return PackageInfo();
+
+ return d->packageInfoList.at(index);
+}
+
+/*!
+ Returns the compat level of the application.
+*/
+int PackagesInfo::compatLevel() const
+{
+ return d->compatLevel;
+}
+
+/*!
+ This function returns the index of the package whose name is \c pkgName. If no such
+ package was found, this function returns -1.
+*/
+int PackagesInfo::findPackageInfo(const QString &pkgName) const
+{
+ for (int i = 0; i < d->packageInfoList.count(); i++) {
+ if (d->packageInfoList[i].name == pkgName)
+ return i;
+ }
+
+ return -1;
+}
+
+/*!
+ Returns all package info structures.
+*/
+QVector<PackageInfo> PackagesInfo::packageInfos() const
+{
+ return d->packageInfoList;
+}
+
+/*!
+ This function re-reads the Packages.xml file and updates itself. Changes to \ref applicationName()
+ and \ref applicationVersion() are lost after this function returns. The function emits a reset()
+ signal after completion.
+*/
+void PackagesInfo::refresh()
+{
+ // First clear internal variables
+ d->applicationName.clear();
+ d->applicationVersion.clear();
+ d->packageInfoList.clear();
+ d->modified = false;
+
+ QFile file(d->fileName);
+
+ // if the file does not exist then we just skip the reading
+ if (!file.exists()) {
+ d->error = NotYetReadError;
+ d->errorMessage = tr("The file %1 does not exist.").arg(d->fileName);
+ emit reset();
+ return;
+ }
+
+ // Open Packages.xml
+ if (!file.open(QFile::ReadOnly)) {
+ d->error = CouldNotReadPackageFileError;
+ d->errorMessage = tr("Could not open %1.").arg(d->fileName);
+ emit reset();
+ return;
+ }
+
+ // Parse the XML document
+ QDomDocument doc;
+ QString parseErrorMessage;
+ int parseErrorLine;
+ int parseErrorColumn;
+ if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
+ d->error = InvalidXmlError;
+ d->errorMessage = tr("Parse error in %1 at %2, %3: %4")
+ .arg(d->fileName,
+ QString::number(parseErrorLine),
+ QString::number(parseErrorColumn),
+ parseErrorMessage);
+ emit reset();
+ return;
+ }
+ file.close();
+
+ // Now populate information from the XML file.
+ QDomElement rootE = doc.documentElement();
+ if (rootE.tagName() != QLatin1String("Packages")) {
+ d->setInvalidContentError(tr("Root element %1 unexpected, should be 'Packages'.").arg(rootE.tagName()));
+ emit reset();
+ return;
+ }
+
+ QDomNodeList childNodes = rootE.childNodes();
+ for (int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.item(i);
+ QDomElement childNodeE = childNode.toElement();
+ if (childNodeE.isNull())
+ continue;
+
+ if (childNodeE.tagName() == QLatin1String("ApplicationName"))
+ d->applicationName = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("ApplicationVersion"))
+ d->applicationVersion = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Package"))
+ d->addPackageFrom(childNodeE);
+ else if (childNodeE.tagName() == QLatin1String("CompatLevel"))
+ d->compatLevel = childNodeE.text().toInt();
+ }
+
+ d->error = NoError;
+ d->errorMessage.clear();
+ emit reset();
+}
+
+/*!
+ Sets the application compat level.
+*/
+void PackagesInfo::setCompatLevel(int level)
+{
+ d->compatLevel = level;
+ d->modified = true;
+}
+
+/*!
+ Marks the package with \a name as installed in \a version.
+ */
+bool PackagesInfo::installPackage(const QString &name, const QString &version,
+ const QString &title, const QString &description,
+ const QStringList &dependencies, bool forcedInstallation,
+ bool virtualComp, quint64 uncompressedSize,
+ const QString &inheritVersionFrom)
+{
+ if (findPackageInfo(name) != -1)
+ return updatePackage(name, version, QDate::currentDate());
+
+ PackageInfo info;
+ info.name = name;
+ info.version = version;
+ info.inheritVersionFrom = inheritVersionFrom;
+ info.installDate = QDate::currentDate();
+ info.title = title;
+ info.description = description;
+ info.dependencies = dependencies;
+ info.forcedInstallation = forcedInstallation;
+ info.virtualComp = virtualComp;
+ info.uncompressedSize = uncompressedSize;
+ d->packageInfoList.push_back(info);
+ d->modified = true;
+ return true;
+}
+
+/*!
+ Update the package.
+*/
+bool PackagesInfo::updatePackage(const QString &name,
+ const QString &version,
+ const QDate &date)
+{
+ int index = findPackageInfo(name);
+
+ if (index == -1)
+ return false;
+
+ d->packageInfoList[index].version = version;
+ d->packageInfoList[index].lastUpdateDate = date;
+ d->modified = true;
+ return true;
+}
+
+/*!
+ Remove the package with \a name.
+ */
+bool PackagesInfo::removePackage(const QString &name)
+{
+ const int index = findPackageInfo(name);
+ if (index == -1)
+ return false;
+
+ d->packageInfoList.remove(index);
+ d->modified = true;
+ return true;
+}
+
+static void addTextChildHelper(QDomNode *node,
+ const QString &tag,
+ const QString &text,
+ const QString &attributeName = QString(),
+ const QString &attributeValue = QString())
+{
+ QDomElement domElement = node->ownerDocument().createElement(tag);
+ QDomText domText = node->ownerDocument().createTextNode(text);
+
+ domElement.appendChild(domText);
+ if (!attributeName.isEmpty())
+ domElement.setAttribute(attributeName, attributeValue);
+ node->appendChild(domElement);
+}
+
+void PackagesInfo::writeToDisk()
+{
+ if (d->modified && (!d->packageInfoList.isEmpty() || QFile::exists(d->fileName))) {
+ QDomDocument doc;
+ QDomElement root = doc.createElement(QLatin1String("Packages")) ;
+ doc.appendChild(root);
+
+ addTextChildHelper(&root, QLatin1String("ApplicationName"), d->applicationName);
+ addTextChildHelper(&root, QLatin1String("ApplicationVersion"), d->applicationVersion);
+ if (d->compatLevel != -1)
+ addTextChildHelper(&root, QLatin1String( "CompatLevel" ), QString::number(d->compatLevel));
+
+ Q_FOREACH (const PackageInfo &info, d->packageInfoList) {
+ QDomElement package = doc.createElement(QLatin1String("Package"));
+
+ addTextChildHelper(&package, QLatin1String("Name"), info.name);
+ addTextChildHelper(&package, QLatin1String("Pixmap"), info.pixmap);
+ addTextChildHelper(&package, QLatin1String("Title"), info.title);
+ addTextChildHelper(&package, QLatin1String("Description"), info.description);
+ if (info.inheritVersionFrom.isEmpty())
+ addTextChildHelper(&package, QLatin1String("Version"), info.version);
+ else
+ addTextChildHelper(&package, QLatin1String("Version"), info.version,
+ QLatin1String("inheritVersionFrom"), info.inheritVersionFrom);
+ addTextChildHelper(&package, QLatin1String("LastUpdateDate"), info.lastUpdateDate.toString(Qt::ISODate));
+ addTextChildHelper(&package, QLatin1String("InstallDate"), info.installDate.toString(Qt::ISODate));
+ addTextChildHelper(&package, QLatin1String("Size"), QString::number(info.uncompressedSize));
+ QString assembledDependencies = QLatin1String("");
+ Q_FOREACH (const QString & val, info.dependencies) {
+ assembledDependencies += val + QLatin1String(",");
+ }
+ if (info.dependencies.count() > 0)
+ assembledDependencies.chop(1);
+ addTextChildHelper(&package, QLatin1String("Dependencies"), assembledDependencies);
+ if (info.forcedInstallation)
+ addTextChildHelper(&package, QLatin1String("ForcedInstallation"), QLatin1String("true"));
+ if (info.virtualComp)
+ addTextChildHelper(&package, QLatin1String("Virtual"), QLatin1String("true"));
+
+ root.appendChild(package);
+ }
+
+ // Open Packages.xml
+ QFile file(d->fileName);
+ if (!file.open(QFile::WriteOnly))
+ return;
+
+ file.write(doc.toByteArray(4));
+ file.close();
+ d->modified = false;
+ }
+}
+
+void PackagesInfo::PackagesInfoData::addPackageFrom(const QDomElement &packageE)
+{
+ if (packageE.isNull())
+ return;
+
+ QDomNodeList childNodes = packageE.childNodes();
+ if (childNodes.count() == 0)
+ return;
+
+ PackageInfo info;
+ info.forcedInstallation = false;
+ info.virtualComp = false;
+ for (int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.item(i);
+ QDomElement childNodeE = childNode.toElement();
+ if (childNodeE.isNull())
+ continue;
+
+ if (childNodeE.tagName() == QLatin1String("Name"))
+ info.name = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Pixmap"))
+ info.pixmap = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Title"))
+ info.title = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Description"))
+ info.description = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Version")) {
+ info.version = childNodeE.text();
+ info.inheritVersionFrom = childNodeE.attribute(QLatin1String("inheritVersionFrom"));
+ }
+ else if (childNodeE.tagName() == QLatin1String("Virtual"))
+ info.virtualComp = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
+ else if (childNodeE.tagName() == QLatin1String("Size"))
+ info.uncompressedSize = childNodeE.text().toULongLong();
+ else if (childNodeE.tagName() == QLatin1String("Dependencies"))
+ info.dependencies = childNodeE.text().split(QRegExp(QLatin1String("\\b(,|, )\\b")), QString::SkipEmptyParts);
+ else if (childNodeE.tagName() == QLatin1String("ForcedInstallation"))
+ info.forcedInstallation = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false;
+ else if (childNodeE.tagName() == QLatin1String("LastUpdateDate"))
+ info.lastUpdateDate = QDate::fromString(childNodeE.text(), Qt::ISODate);
+ else if (childNodeE.tagName() == QLatin1String("InstallDate"))
+ info.installDate = QDate::fromString(childNodeE.text(), Qt::ISODate);
+ }
+
+ this->packageInfoList.append(info);
+}
+
+/*!
+ Clears the installed package list.
+*/
+void PackagesInfo::clearPackageInfoList()
+{
+ d->packageInfoList.clear();
+ d->modified = true;
+ emit reset();
+}
+
+/*!
+ \fn void KDUpdater::PackagesInfo::reset()
+
+ This signal is emitted whenever the contents of this class is refreshed, usually from within
+ the \ref refresh() slot.
+*/
+
+/*!
+ \ingroup kdupdater
+ \struct KDUpdater::PackageInfo kdupdaterpackagesinfo.h KDUpdaterPackageInfo
+ \brief Describes a single installed package in the application.
+
+ This structure contains information about a single installed package in the application.
+ The information contained in this structure corresponds to the information described
+ by the Package XML element in Packages.xml
+*/
+
+/*!
+ \var QString KDUpdater::PackageInfo::name
+*/
+
+/*!
+ \var QString KDUpdater::PackageInfo::pixmap
+*/
+
+/*!
+ \var QString KDUpdater::PackageInfo::title
+*/
+
+/*!
+ \var QString KDUpdater::PackageInfo::description
+*/
+
+/*!
+ \var QString KDUpdater::PackageInfo::version
+*/
+
+/*!
+ \var QDate KDUpdater::PackageInfo::lastUpdateDate
+*/
+
+/*!
+ \var QDate KDUpdater::PackageInfo::installDate
+*/
diff --git a/src/libs/kdtools/kdupdaterpackagesinfo.h b/src/libs/kdtools/kdupdaterpackagesinfo.h
new file mode 100644
index 000000000..a70a70568
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterpackagesinfo.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_PACKAGES_INFO_H
+#define KD_UPDATER_PACKAGES_INFO_H
+
+#include "kdupdater.h"
+
+#include <QObject>
+#include <QDate>
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+namespace KDUpdater {
+
+class Application;
+class UpdateInstaller;
+
+struct KDTOOLS_EXPORT PackageInfo
+{
+ QString name;
+ QString pixmap;
+ QString title;
+ QString description;
+ QString version;
+ QString inheritVersionFrom;
+ QStringList dependencies;
+ QStringList translations;
+ QDate lastUpdateDate;
+ QDate installDate;
+ bool forcedInstallation;
+ bool virtualComp;
+ quint64 uncompressedSize;
+};
+
+class KDTOOLS_EXPORT PackagesInfo : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~PackagesInfo();
+
+ enum Error
+ {
+ NoError = 0,
+ NotYetReadError,
+ CouldNotReadPackageFileError,
+ InvalidXmlError,
+ InvalidContentError
+ };
+
+ Application *application() const;
+
+ bool isValid() const;
+ QString errorString() const;
+ Error error() const;
+ void clearPackageInfoList();
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ void setApplicationName(const QString &name);
+ QString applicationName() const;
+
+ void setApplicationVersion(const QString &version);
+ QString applicationVersion() const;
+
+ int packageInfoCount() const;
+ PackageInfo packageInfo(int index) const;
+ int findPackageInfo(const QString &pkgName) const;
+ QVector<KDUpdater::PackageInfo> packageInfos() const;
+ void writeToDisk();
+
+ int compatLevel() const;
+ void setCompatLevel(int level);
+
+ bool installPackage(const QString &pkgName, const QString &version, const QString &title = QString(),
+ const QString &description = QString(), const QStringList &dependencies = QStringList(),
+ bool forcedInstallation = false, bool virtualComp = false, quint64 uncompressedSize = 0,
+ const QString &inheritVersionFrom = QString());
+
+ bool updatePackage(const QString &pkgName, const QString &version, const QDate &date);
+ bool removePackage(const QString &pkgName);
+
+public Q_SLOTS:
+ void refresh();
+
+Q_SIGNALS:
+ void reset();
+
+protected:
+ explicit PackagesInfo(Application *application = 0);
+
+private:
+ friend class Application;
+ friend class UpdateInstaller;
+ struct PackagesInfoData;
+ PackagesInfoData *d;
+};
+
+} // KDUpdater
+
+#endif // KD_UPDATER_PACKAGES_INFO_H
diff --git a/src/libs/kdtools/kdupdatersignatureverificationrunnable.cpp b/src/libs/kdtools/kdupdatersignatureverificationrunnable.cpp
new file mode 100644
index 000000000..1e00ca43f
--- /dev/null
+++ b/src/libs/kdtools/kdupdatersignatureverificationrunnable.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdatersignatureverificationrunnable.h"
+#include "kdupdatersignatureverifier.h"
+#include "kdupdatersignatureverificationresult.h"
+
+#include <QByteArray>
+#include <QIODevice>
+#include <QMetaObject>
+#include <QObject>
+#include <QPointer>
+#include <QThreadPool>
+#include <QVariant>
+#include <QVector>
+
+#include <cassert>
+
+using namespace KDUpdater;
+
+class Runnable::Private {
+public:
+ QVector<QObject*> receivers;
+ QVector<QByteArray> methods;
+};
+
+Runnable::Runnable() : QRunnable(), d( new Private ) {
+}
+
+Runnable::~Runnable() {
+ delete d;
+}
+
+
+void Runnable::addResultListener( QObject* receiver, const char* method ) {
+ d->receivers.push_back( receiver );
+ d->methods.push_back( QByteArray( method ) );
+}
+
+void Runnable::emitResult( const QGenericArgument& arg0,
+ const QGenericArgument& arg1,
+ const QGenericArgument& arg2,
+ const QGenericArgument& arg3,
+ const QGenericArgument& arg4,
+ const QGenericArgument& arg5,
+ const QGenericArgument& arg6,
+ const QGenericArgument& arg7,
+ const QGenericArgument& arg8,
+ const QGenericArgument& arg9 ) {
+ assert( d->receivers.size() == d->methods.size() );
+ for ( int i = 0; i < d->receivers.size(); ++i ) {
+ QMetaObject::invokeMethod( d->receivers[i],
+ d->methods[i].constData(),
+ Qt::QueuedConnection,
+ arg0,
+ arg1,
+ arg2,
+ arg3,
+ arg4,
+ arg5,
+ arg6,
+ arg7,
+ arg8,
+ arg9 );
+ }
+}
+
+class SignatureVerificationRunnable::Private {
+public:
+ Private() : verifier( 0 ) {}
+ const SignatureVerifier* verifier;
+ QPointer<QIODevice> device;
+ QByteArray signature;
+};
+
+SignatureVerificationRunnable::SignatureVerificationRunnable() : Runnable(), d( new Private ) {
+}
+
+SignatureVerificationRunnable::~SignatureVerificationRunnable() {
+ delete d;
+}
+
+const SignatureVerifier* SignatureVerificationRunnable::verifier() const {
+ return d->verifier;
+}
+
+void SignatureVerificationRunnable::setVerifier( const SignatureVerifier* verifier ) {
+ delete d->verifier;
+ d->verifier = verifier ? verifier->clone() : 0;
+}
+
+QByteArray SignatureVerificationRunnable::signature() const {
+ return d->signature;
+}
+
+void SignatureVerificationRunnable::setSignature( const QByteArray& sig ) {
+ d->signature = sig;
+}
+
+QIODevice* SignatureVerificationRunnable::data() const {
+ return d->device;
+}
+
+void SignatureVerificationRunnable::setData( QIODevice* device ) {
+ d->device = device;
+}
+
+
+void SignatureVerificationRunnable::run() {
+ QThreadPool::globalInstance()->releaseThread();
+ const SignatureVerificationResult result = d->verifier->verify( d->device->readAll(), d->signature );
+ QThreadPool::globalInstance()->reserveThread();
+ delete d->verifier;
+ delete d->device;
+ emitResult( Q_ARG( KDUpdater::SignatureVerificationResult, result ) );
+}
+
+
diff --git a/src/libs/kdtools/kdupdatersignatureverificationrunnable.h b/src/libs/kdtools/kdupdatersignatureverificationrunnable.h
new file mode 100644
index 000000000..901689253
--- /dev/null
+++ b/src/libs/kdtools/kdupdatersignatureverificationrunnable.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KDUPDATERSIGNATUREVERIFICATIONJOB_H
+#define KDUPDATERSIGNATUREVERIFICATIONJOB_H
+
+#include <kdtoolsglobal.h>
+
+#include <QtCore/QGenericArgument>
+#include <QtCore/QRunnable>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+class QIODevice;
+class QObject;
+template <typename T> class QVector;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+
+class SignatureVerifier;
+class SignatureVerificationResult;
+
+class Runnable : public QRunnable
+{
+public:
+ Runnable();
+ ~Runnable();
+
+ void addResultListener(QObject *receiver, const char *method);
+
+protected:
+ void emitResult(const QGenericArgument &arg0 = QGenericArgument(0),
+ const QGenericArgument &arg1 = QGenericArgument(),
+ const QGenericArgument &arg2 = QGenericArgument(),
+ const QGenericArgument &arg3 = QGenericArgument(),
+ const QGenericArgument &arg4 = QGenericArgument(),
+ const QGenericArgument &arg5 = QGenericArgument(),
+ const QGenericArgument &arg6 = QGenericArgument(),
+ const QGenericArgument &arg7 = QGenericArgument(),
+ const QGenericArgument &arg8 = QGenericArgument(),
+ const QGenericArgument &arg9 = QGenericArgument());
+
+private:
+ class Private;
+ Private *d;
+};
+
+class SignatureVerificationRunnable : public Runnable
+{
+public:
+ explicit SignatureVerificationRunnable();
+ ~SignatureVerificationRunnable();
+
+ const SignatureVerifier *verifier() const;
+ void setVerifier(const SignatureVerifier *verifier);
+
+ QByteArray signature() const;
+ void setSignature(const QByteArray &sig);
+
+ QIODevice *data() const;
+ void setData(QIODevice *device);
+
+ void run();
+
+private:
+ class Private;
+ Private *d;
+};
+
+} // namespace KDUpdater
+
+#endif // KDUPDATERSIGNATUREVERIFICATIONJOB_H
diff --git a/src/libs/kdtools/kdupdatertask.cpp b/src/libs/kdtools/kdupdatertask.cpp
new file mode 100644
index 000000000..fb8118fbd
--- /dev/null
+++ b/src/libs/kdtools/kdupdatertask.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdatertask.h"
+
+using namespace KDUpdater;
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::Task kdupdatertask.h KDUpdaterTask
+ \brief Base class for all task classes in KDUpdater
+
+ This class is the base class for all task classes in KDUpdater. Task is an activity that
+ occupies certain amount of execution time. It can be started, stopped (or canceled), paused and
+ resumed. Tasks can report progress and error messages which an application can show in any
+ sort of UI. The KDUpdater::Task class provides a common interface for dealing with all kinds of
+ tasks in KDUpdater. The class diagram show in this class documentation will help in pointing out
+ the task classes in KDUpdater.
+
+ User should be carefull of these points:
+ \li Instances of this class cannot be created. Only instance of the subclasses can be created
+ \li Task classes can be started only once.
+*/
+
+/*!
+ \internal
+*/
+KDUpdater::Task::Task(const QString &name, int caps, QObject *parent)
+ : QObject(parent)
+ , m_caps(caps)
+ , m_name(name)
+ , m_errorCode(0)
+ , m_started(false)
+ , m_finished(false)
+ , m_paused(false)
+ , m_stopped(false)
+ , m_progressPc(0)
+ , m_autoDelete(true)
+{
+}
+
+/*!
+ \internal
+*/
+Task::~Task()
+{}
+
+/*!
+ Returns the name of the task.
+*/
+QString Task::name() const
+{
+ return m_name;
+}
+
+/*!
+ Returns the capabilities of the task. It is a combination of one or more
+ Capability flags. Defined as follows
+ \code
+ enum Task::Capability
+ {
+ NoCapability = 0,
+ Pausable = 1,
+ Stoppable = 2
+ };
+ \endcode
+*/
+int Task::capabilities() const
+{
+ return m_caps;
+}
+
+/*!
+ Returns the last reported error code.
+*/
+int Task::error() const
+{
+ return m_errorCode;
+}
+
+/*!
+ Returns the last reported error message text.
+*/
+QString Task::errorString() const
+{
+ return m_errorText;
+}
+
+/*!
+ Returns whether the task has started and is running or not.
+*/
+bool Task::isRunning() const
+{
+ return m_started;
+}
+
+/*!
+ Returns whether the task has finished or not.
+
+ \note Stopped (or canceled) tasks are not finished tasks.
+*/
+bool Task::isFinished() const
+{
+ return m_finished;
+}
+
+/*!
+ Returns whether the task is paused or not.
+*/
+bool Task::isPaused() const
+{
+ return m_paused;
+}
+
+/*!
+ Returns whether the task is stopped or not.
+
+ \note Finished tasks are not stopped classes.
+*/
+bool Task::isStopped() const
+{
+ return m_stopped;
+}
+
+/*!
+ Returns the progress in percentage made by this task.
+*/
+int Task::progressPercent() const
+{
+ return m_progressPc;
+}
+
+/*!
+ Returns a string that describes the progress made by this task as a string.
+*/
+QString Task::progressText() const
+{
+ return m_progressText;
+}
+
+/*!
+ Starts the task.
+*/
+void Task::run()
+{
+ if (m_started) {
+ qDebug("Trying to start an already started task");
+ return;
+ }
+
+ if (m_finished || m_stopped) {
+ qDebug("Trying to start a finished or canceled task");
+ return;
+ }
+
+ m_stopped = false;
+ m_finished = false; // for the sake of completeness
+ m_started = true;
+ emit started();
+ reportProgress(0, tr("%1 started").arg(m_name));
+
+ doRun();
+}
+
+/*!
+ Stops the task, provided the task has \ref Stoppable capability.
+
+ \note Once the task is stopped, it cannot be restarted.
+*/
+void Task::stop()
+{
+ if (!(m_caps & Stoppable)) {
+ const QString errorMsg = tr("%1 cannot be stopped").arg(m_name);
+ reportError(ECannotStopTask, errorMsg);
+ return;
+ }
+
+ if (!m_started) {
+ qDebug("Trying to stop an unstarted task");
+ return;
+ }
+
+ if(m_finished || m_stopped)
+ {
+ qDebug("Trying to stop a finished or canceled task");
+ return;
+ }
+
+ m_stopped = doStop();
+ if (!m_stopped) {
+ const QString errorMsg = tr("Cannot stop task %1").arg(m_name);
+ reportError(ECannotStopTask, errorMsg);
+ return;
+ }
+
+ m_started = false; // the task is not running
+ m_finished = false; // the task is not finished, but was canceled half-way through
+
+ emit stopped();
+ if (m_autoDelete)
+ deleteLater();
+}
+
+/*!
+ Paused the task, provided the task has \ref Pausable capability.
+*/
+void Task::pause()
+{
+ if (!(m_caps & Pausable)) {
+ const QString errorMsg = tr("%1 cannot be paused").arg(m_name);
+ reportError(ECannotPauseTask, errorMsg);
+ return;
+ }
+
+ if (!m_started) {
+ qDebug("Trying to pause an unstarted task");
+ return;
+ }
+
+ if (m_finished || m_stopped) {
+ qDebug("Trying to pause a finished or canceled task");
+ return;
+ }
+
+ m_paused = doPause();
+
+ if (!m_paused) {
+ const QString errorMsg = tr("Cannot pause task %1").arg(m_name);
+ reportError(ECannotPauseTask, errorMsg);
+ return;
+ }
+
+ // The task state has to be started, paused but not finished or stopped.
+ // We need not set the flags below, but just in case.
+ // Perhaps we should do Q_ASSERT() ???
+ m_started = true;
+ m_finished = false;
+ m_stopped = false;
+
+ emit paused();
+}
+
+/*!
+ Resumes the task if it was paused.
+*/
+void Task::resume()
+{
+ if (!m_paused) {
+ qDebug("Trying to resume an unpaused task");
+ return;
+ }
+
+ const bool val = doResume();
+
+ if (!val) {
+ const QString errorMsg = tr("Cannot resume task %1").arg(m_name);
+ reportError(ECannotResumeTask, errorMsg);
+ return;
+ }
+
+ // The task state should be started, but not paused, finished or stopped.
+ // We need not set the flags below, but just in case.
+ // Perhaps we should do Q_ASSERT() ???
+ m_started = true;
+ m_paused = false;
+ m_finished = false;
+ m_stopped = false;
+
+ emit resumed();
+}
+
+/*!
+ \internal
+*/
+void Task::reportProgress(int percent, const QString &text)
+{
+ if (m_progressPc == percent)
+ return;
+
+ m_progressPc = percent;
+ m_progressText = text;
+ emit progressValue(m_progressPc);
+ emit progressText(m_progressText);
+}
+
+/*!
+ \internal
+*/
+void Task::reportError(int errorCode, const QString &errorText)
+{
+ m_errorCode = errorCode;
+ m_errorText = errorText;
+
+ emit error(m_errorCode, m_errorText);
+ if (m_autoDelete)
+ deleteLater();
+}
+
+/*!
+ \internal
+*/
+void Task::reportError(const QString &errorText)
+{
+ reportError(EUnknown, errorText);
+}
+
+/*!
+ \internal
+*/
+void Task::reportDone()
+{
+ QString msg = tr("%1 done");
+ reportProgress(100, msg);
+
+ // State should be finished, but not started, paused or stopped.
+ m_finished = true;
+ m_started = false;
+ m_paused = false;
+ m_stopped = false;
+ m_errorCode = 0;
+ m_errorText.clear();
+
+ emit finished();
+ if (m_autoDelete)
+ deleteLater();
+}
+
+bool Task::autoDelete() const
+{
+ return m_autoDelete;
+}
+
+void Task::setAutoDelete(bool autoDelete)
+{
+ m_autoDelete = autoDelete;
+}
+
+/*!
+ \fn virtual bool KDUpdater::Task::doStart() = 0;
+*/
+
+/*!
+ \fn virtual bool KDUpdater::Task::doStop() = 0;
+*/
+
+/*!
+ \fn virtual bool KDUpdater::Task::doPause() = 0;
+*/
+
+/*!
+ \fn virtual bool KDUpdater::Task::doResume() = 0;
+*/
+
+/*!
+ \signal void KDUpdater::Task::error(int code, const QString& errorText)
+
+ This signal is emitted to notify an error during the execution of this task.
+ \param code Error code
+ \param errorText A string describing the error.
+
+ Error codes are just integers, there are however built in errors represented
+ by the KDUpdater::Error enumeration
+ \code
+ enum Error
+ {
+ ECannotStartTask,
+ ECannotPauseTask,
+ ECannotResumeTask,
+ ECannotStopTask,
+ EUnknown
+ };
+ \endcode
+*/
+
+/*!
+ \signal void KDUpdater::Task::progress(int percent, const QString& progressText)
+
+ This signal is emitted to nofity progress made by the task.
+
+ \param percent Percentage of progress made
+ \param progressText A string describing the progress made
+*/
+
+/*!
+ \signal void KDUpdater::Task::started()
+
+ This signal is emitted when the task has started.
+*/
+
+/*!
+ \signal void KDUpdater::Task::paused()
+
+ This signal is emitted when the task has paused.
+*/
+
+/*!
+ \signal void KDUpdater::Task::resumed()
+
+ This signal is emitted when the task has resumed.
+*/
+
+/*!
+ \signal void KDUpdater::Task::stopped()
+
+ This signal is emitted when the task has stopped (or canceled).
+*/
+
+/*!
+ \signal void KDUpdater::Task::finished()
+
+ This signal is emitted when the task has finished.
+*/
diff --git a/src/libs/kdtools/kdupdatertask.h b/src/libs/kdtools/kdupdatertask.h
new file mode 100644
index 000000000..4b2b2e4f5
--- /dev/null
+++ b/src/libs/kdtools/kdupdatertask.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_TASK_H
+#define KD_UPDATER_TASK_H
+
+#include "kdupdater.h"
+
+#include <QObject>
+
+namespace KDUpdater {
+
+class KDTOOLS_EXPORT Task : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Capability
+ {
+ NoCapability = 0,
+ Pausable = 1,
+ Stoppable = 2
+ };
+
+ virtual ~Task();
+
+ QString name() const;
+ int capabilities() const;
+
+ int error() const;
+ QString errorString() const;
+
+ bool isRunning() const;
+ bool isFinished() const;
+ bool isPaused() const;
+ bool isStopped() const;
+
+ int progressPercent() const;
+ QString progressText() const;
+
+ bool autoDelete() const;
+ void setAutoDelete(bool autoDelete);
+
+public Q_SLOTS:
+ void run();
+ void stop();
+ void pause();
+ void resume();
+
+Q_SIGNALS:
+ void error(int code, const QString &errorText);
+ void progressValue(int percent);
+ void progressText(const QString &progressText);
+ void started();
+ void paused();
+ void resumed();
+ void stopped();
+ void finished();
+
+protected:
+ explicit Task(const QString &name, int caps = NoCapability, QObject *parent = 0);
+ void reportProgress(int percent, const QString &progressText);
+ void reportError(int errorCode, const QString &errorText);
+ void reportError(const QString &errorText);
+ void reportDone();
+
+ // Task interface
+ virtual void doRun() = 0;
+ virtual bool doStop() = 0;
+ virtual bool doPause() = 0;
+ virtual bool doResume() = 0;
+
+private:
+ int m_caps;
+ QString m_name;
+ int m_errorCode;
+ QString m_errorText;
+ bool m_started;
+ bool m_finished;
+ bool m_paused;
+ bool m_stopped;
+ int m_progressPc;
+ QString m_progressText;
+ bool m_autoDelete;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_TASK_H
diff --git a/src/libs/kdtools/kdupdaterupdate.cpp b/src/libs/kdtools/kdupdaterupdate.cpp
new file mode 100644
index 000000000..02d7fbc9a
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdate.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdate.h"
+#include "kdupdaterapplication.h"
+#include "kdupdaterupdatesourcesinfo.h"
+#include "kdupdaterfiledownloader_p.h"
+#include "kdupdaterfiledownloaderfactory.h"
+#include "kdupdaterupdateoperations.h"
+#include "kdupdaterupdateoperationfactory.h"
+
+#include <QFile>
+
+using namespace KDUpdater;
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::Update kdupdaterupdate.h KDUpdaterUpdate
+ \brief Represents a single update
+
+ The KDUpdater::Update class contains information and mechanisms to download one update. It is
+ created by KDUpdater::UpdateFinder and is used by KDUpdater::UpdateInstaller to download the UpdateFile
+ corresponding to the update.
+
+ The class makes use of appropriate network protocols (HTTP, HTTPS, FTP, or Local File Copy) to
+ download the UpdateFile.
+
+ The constructor of the KDUpdater::Update class is made protected, because it can be instantiated only by
+ KDUpdater::UpdateFinder (which is a friend class). The destructor however is public.
+*/
+
+struct Update::UpdateData
+{
+ UpdateData(Update *qq) :
+ q(qq),
+ application(0),
+ compressedSize(0),
+ uncompressedSize(0)
+ {}
+
+ Update *q;
+ Application *application;
+ UpdateSourceInfo sourceInfo;
+ QMap<QString, QVariant> data;
+ QUrl updateUrl;
+ UpdateType type;
+ QList<UpdateOperation *> operations;
+ QByteArray sha1sum;
+
+ quint64 compressedSize;
+ quint64 uncompressedSize;
+
+ FileDownloader *fileDownloader;
+};
+
+
+/*!
+ \internal
+*/
+Update::Update(Application *application, const UpdateSourceInfo &sourceInfo,
+ UpdateType type, const QUrl &updateUrl, const QMap<QString, QVariant> &data,
+ quint64 compressedSize, quint64 uncompressedSize, const QByteArray &sha1sum)
+ : Task(QLatin1String("Update"), Stoppable, application),
+ d(new UpdateData(this))
+{
+ d->application = application;
+ d->sourceInfo = sourceInfo;
+ d->data = data;
+ d->updateUrl = updateUrl;
+ d->type = type;
+
+ d->compressedSize = compressedSize;
+ d->uncompressedSize = uncompressedSize;
+ d->sha1sum = sha1sum;
+
+ d->fileDownloader = FileDownloaderFactory::instance().create(updateUrl.scheme(), this);
+ if (d->fileDownloader) {
+ d->fileDownloader->setUrl(d->updateUrl);
+ d->fileDownloader->setSha1Sum(d->sha1sum);
+ connect(d->fileDownloader, SIGNAL(downloadProgress(double)), this, SLOT(downloadProgress(double)));
+ connect(d->fileDownloader, SIGNAL(downloadCanceled()), this, SIGNAL(stopped()));
+ connect(d->fileDownloader, SIGNAL(downloadCompleted()), this, SIGNAL(finished()));
+ }
+
+ switch (type) {
+ case NewPackage:
+ case PackageUpdate: {
+ UpdateOperation *packageOperation = UpdateOperationFactory::instance().create(QLatin1String("UpdatePackage"));
+ QStringList args;
+ args << data.value(QLatin1String("Name")).toString()
+ << data.value(QLatin1String("Version")).toString()
+ << data.value(QLatin1String("ReleaseDate")).toString();
+ packageOperation->setArguments(args);
+ packageOperation->setApplication(application);
+ d->operations.append(packageOperation);
+ break;
+ }
+ case CompatUpdate: {
+ UpdateOperation *compatOperation = UpdateOperationFactory::instance().create(QLatin1String("UpdateCompatLevel"));
+ QStringList args;
+ args << data.value(QLatin1String("CompatLevel")).toString();
+ compatOperation->setArguments(args);
+ compatOperation->setApplication(application);
+ d->operations.append(compatOperation);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ Destructor
+*/
+Update::~Update()
+{
+ const QString fileName = this->downloadedFileName();
+ if (!fileName.isEmpty())
+ QFile::remove(fileName);
+ qDeleteAll(d->operations);
+ d->operations.clear();
+ delete d;
+}
+
+/*!
+ Returns the application for which this class is downloading the UpdateFile
+*/
+Application *Update::application() const
+{
+ return d->application;
+}
+
+/*!
+ Returns the release date of the update downloaded by this class
+*/
+QDate Update::releaseDate() const
+{
+ return d->data.value(QLatin1String("ReleaseDate")).toDate();
+}
+
+/*!
+ Returns data whose name is given in parameter, or an invalid QVariant if the data doesn't exist.
+*/
+QVariant Update::data(const QString &name, const QVariant &defaultValue) const
+{
+ if (d->data.contains(name))
+ return d->data.value(name);
+ return defaultValue;
+}
+
+/*!
+ Returns the complete URL of the UpdateFile downloaded by this class.
+*/
+QUrl Update::updateUrl() const
+{
+ return d->updateUrl;
+}
+
+/*!
+ Returns the update source info on which this update was created.
+*/
+UpdateSourceInfo Update::sourceInfo() const
+{
+ return d->sourceInfo;
+}
+
+/*!
+ * Returns the type of update
+ */
+UpdateType Update::type() const
+{
+ return d->type;
+}
+
+/*!
+ Returns true of the update can be downloaded, false otherwise. The function
+ returns false if the URL scheme is not supported by this class.
+*/
+bool Update::canDownload() const
+{
+ return d->fileDownloader && d->fileDownloader->canDownload();
+}
+
+/*!
+ Returns true of the update has been downloaded. If this function returns true
+ the you can use the \ref downloadedFileName() method to get the complete name
+ of the downloaded UpdateFile.
+
+ \note: The downloaded UpdateFile will be deleted when this class is destroyed
+*/
+bool Update::isDownloaded() const
+{
+ return d->fileDownloader && d->fileDownloader->isDownloaded();
+}
+
+/*!
+ Returns the name of the downloaded UpdateFile after the download is complete, ie
+ when \ref isDownloaded() returns true.
+*/
+QString Update::downloadedFileName() const
+{
+ if (d->fileDownloader)
+ return d->fileDownloader->downloadedFileName();
+
+ return QString();
+}
+
+/*!
+ \internal
+*/
+void Update::downloadProgress(double value)
+{
+ Q_ASSERT(value <= 1);
+ reportProgress(value * 100, tr("Downloading update..."));
+}
+
+/*!
+ \internal
+*/
+void Update::downloadCompleted()
+{
+ reportProgress(100, tr("Update downloaded"));
+ reportDone();
+}
+
+/*!
+ \internal
+*/
+void Update::downloadAborted(const QString &msg)
+{
+ reportError(msg);
+}
+
+/*!
+ \internal
+*/
+void Update::doRun()
+{
+ if (d->fileDownloader)
+ d->fileDownloader->download();
+}
+
+/*!
+ \internal
+*/
+bool Update::doStop()
+{
+ if (d->fileDownloader)
+ d->fileDownloader->cancelDownload();
+ return true;
+}
+
+/*!
+ \internal
+*/
+bool Update::doPause()
+{
+ return false;
+}
+
+/*!
+ \internal
+*/
+bool Update::doResume()
+{
+ return false;
+}
+
+/*!
+ Returns a list of operations needed by this update. For example, package update needs to change
+ the package version, compat update needs to change the compat level...
+ */
+QList<UpdateOperation *> Update::operations() const
+{
+ return d->operations;
+}
+
+/*!
+ * Returns the compressed size of this update's data file.
+ */
+quint64 Update::compressedSize() const
+{
+ return d->compressedSize;
+}
+
+/*!
+ * Returns the uncompressed size of this update's data file.
+ */
+quint64 Update::uncompressedSize() const
+{
+ return d->uncompressedSize;
+}
diff --git a/src/libs/kdtools/kdupdaterupdate.h b/src/libs/kdtools/kdupdaterupdate.h
new file mode 100644
index 000000000..0cf614f56
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdate.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_H
+#define KD_UPDATER_UPDATE_H
+
+#include "kdupdater.h"
+#include "kdupdatertask.h"
+
+#include <QUrl>
+#include <QDate>
+#include <QMap>
+#include <QVariant>
+#include <QList>
+
+namespace KDUpdater {
+
+class Application;
+struct UpdateSourceInfo;
+class UpdateFinder;
+class UpdateOperation;
+
+class KDTOOLS_EXPORT Update : public Task
+{
+ Q_OBJECT
+
+public:
+ ~Update();
+
+ Application *application() const;
+
+ UpdateType type() const;
+ QUrl updateUrl() const;
+ QDate releaseDate() const;
+ QVariant data(const QString &m_name, const QVariant &defaultValue = QVariant()) const;
+ UpdateSourceInfo sourceInfo() const;
+
+ bool canDownload() const;
+ bool isDownloaded() const;
+ void download() { run(); }
+ QString downloadedFileName() const;
+
+ QList<UpdateOperation *> operations() const;
+
+ quint64 compressedSize() const;
+ quint64 uncompressedSize() const;
+
+private Q_SLOTS:
+ void downloadProgress(double);
+ void downloadAborted(const QString &msg);
+ void downloadCompleted();
+
+private:
+ friend class UpdateFinder;
+ struct UpdateData;
+ UpdateData *d;
+
+ void doRun();
+ bool doStop();
+ bool doPause();
+ bool doResume();
+
+ Update(Application *application, const UpdateSourceInfo &sourceInfo,
+ UpdateType type, const QUrl &updateUrl, const QMap<QString, QVariant> &data,
+ quint64 compressedSize, quint64 uncompressedSize, const QByteArray &sha1sum);
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_H
diff --git a/src/libs/kdtools/kdupdaterupdatefinder.cpp b/src/libs/kdtools/kdupdaterupdatefinder.cpp
new file mode 100644
index 000000000..63631a8e5
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatefinder.cpp
@@ -0,0 +1,842 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdatefinder.h"
+#include "kdupdaterapplication.h"
+#include "kdupdaterupdatesourcesinfo.h"
+#include "kdupdaterpackagesinfo.h"
+#include "kdupdaterupdate.h"
+#include "kdupdaterfiledownloader_p.h"
+#include "kdupdaterfiledownloaderfactory.h"
+#include "kdupdaterupdatesinfo_p.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+using namespace KDUpdater;
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::UpdateFinder kdupdaterupdatefinder KDUpdaterUpdateFinder
+ \brief Finds updates applicable for a \ref KDUpdater::Application
+
+ The KDUpdater::UpdateFinder class helps in searching for updates and installing them on the application. The
+ class basically processes the application's \ref KDUpdater::PackagesInfo and the UpdateXMLs it aggregates
+ from all the update sources described in KDUpdater::UpdateSourcesInfo and populates a list of
+ \ref KDUpdater::Update objects. This list can then be passed to \ref KDUpdater::UpdateInstaller for
+ actually downloading and installing the updates.
+
+
+ Usage:
+ \code
+ KDUpdater::UpdateFinder updateFinder( application );
+ QProgressDialog finderProgressDlg;
+
+ QObject::connect( &updateFinder, SIGNAL(progressValue(int)),
+ &finderProgressDlg, SLOT(setValue(int)));
+ QObject::connect( &updateFinder, SIGNAL(computeUpdatesCompleted()),
+ &finderProgressDlg, SLOT(accept()));
+ QObject::connect( &updateFinder, SIGNAL(computeUpdatesCanceled()),
+ &finderProgressDlg, SLOT(reject()));
+
+ QObject::connect( &finderProgressDlg, SIGNAL(canceled()),
+ &updateFinder, SLOT(cancelComputeUpdates()));
+
+ updateFinder.run();
+ finderProgressDlg.exec();
+
+ // Control comes here after update finding is done or canceled.
+
+ QList<KDUpdater::Update*> updates = updateFinder.updates();
+ KDUpdater::UpdateInstaller updateInstaller;
+ updateInstaller.installUpdates( updates );
+
+\endcode
+*/
+
+
+//
+// Private
+//
+class UpdateFinder::Private
+{
+public:
+ Private(UpdateFinder *qq) :
+ q(qq),
+ application(0),
+ updateType(PackageUpdate)
+ {}
+
+ ~Private()
+ {
+ qDeleteAll(updates);
+ qDeleteAll(updatesInfoList);
+ qDeleteAll(updateXmlFDList);
+ }
+
+ UpdateFinder *q;
+ Application *application;
+ QList<Update *> updates;
+ UpdateTypes updateType;
+
+ // Temporary structure that notes down information about updates.
+ bool cancel;
+ int downloadCompleteCount;
+ QList<UpdateSourceInfo> updateSourceInfoList;
+ QList<UpdatesInfo *> updatesInfoList;
+ QList<FileDownloader *> updateXmlFDList;
+
+ void clear();
+ void computeUpdates();
+ void cancelComputeUpdates();
+ bool downloadUpdateXMLFiles();
+ bool computeApplicableUpdates();
+
+ QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo,
+ bool addNewPackages = false);
+ void createUpdateObjects(const UpdateSourceInfo &sourceInfo,
+ const QList<UpdateInfo> &updateInfoList);
+ bool checkForUpdatePriority(const UpdateSourceInfo &sourceInfo,
+ const UpdateInfo &updateInfo);
+ int pickUpdateFileInfo(const QList<UpdateFileInfo> &updateFiles);
+ void slotDownloadDone();
+};
+
+
+static int computeProgressPercentage(int min, int max, int percent)
+{
+ return min + qint64(max-min) * percent / 100;
+}
+
+static int computePercent(int done, int total)
+{
+ return total ? done * Q_INT64_C(100) / total : 0 ;
+}
+
+/*!
+ \internal
+
+ Releases all internal resources consumed while downloading and computing updates.
+*/
+void UpdateFinder::Private::clear()
+{
+ qDeleteAll(updates);
+ updates.clear();
+ qDeleteAll(updatesInfoList);
+ updatesInfoList.clear();
+ qDeleteAll(updateXmlFDList);
+ updateXmlFDList.clear();
+ updateSourceInfoList.clear();
+ downloadCompleteCount = 0;
+}
+
+/*!
+ \internal
+
+ This method computes the updates that can be applied on the application by
+ studying the application's \ref KDUpdater::PackagesInfo object and the UpdateXML files
+ from each of the update sources described in \ref KDUpdater::UpdateSourcesInfo.
+
+ This function can take a long time to complete. The following signals are emitted
+ during the execution of this function
+
+ The function creates \ref KDUpdater::Update objects on the stack. All KDUpdater::Update objects
+ are made children of the application associated with this finder.
+
+ The update sources are fetched from the \ref KDUpdater::UpdateSourcesInfo object associated with
+ the application. Package information is extracted from the \ref KDUpdater::PackagesInfo object
+ associated with the application.
+
+ \note Each time this function is called, all the previously computed updates are discarded
+ and its resources are freed.
+*/
+void UpdateFinder::Private::computeUpdates()
+{
+ // Computing updates is done in two stages
+ // 1. Downloading Update XML files from all the update sources
+ // 2. Matching updates with Package XML and figuring out available updates
+
+ cancel = false;
+ clear();
+
+ // First do some quick sanity checks on the packages info
+ PackagesInfo *packages = application->packagesInfo();
+ if (!packages) {
+ q->reportError(tr("Could not access the package information of this application."));
+ return;
+ }
+ if (!packages->isValid()) {
+ q->reportError(packages->errorString());
+ return;
+ }
+
+ // Now do some quick sanity checks on the update sources info
+ UpdateSourcesInfo *sources = application->updateSourcesInfo();
+ if (!sources) {
+ q->reportError(tr("Could not access the update sources information of this application."));
+ return;
+ }
+ if (!sources->isValid()) {
+ q->reportError(sources->errorString());
+ return;
+ }
+
+ // Now we can start...
+
+ // Step 1: 0 - 49 percent
+ if (!downloadUpdateXMLFiles() || cancel) {
+ clear();
+ return;
+ }
+
+ // Step 2: 50 - 100 percent
+ if (!computeApplicableUpdates() || cancel) {
+ clear();
+ return;
+ }
+
+ // All done
+ q->reportProgress(100, tr("%1 updates found.").arg(updates.count()));
+ q->reportDone();
+}
+
+/*!
+ \internal
+
+ Cancels the computation of updates.
+
+ \sa \ref computeUpdates()
+*/
+void UpdateFinder::Private::cancelComputeUpdates()
+{
+ cancel = true;
+}
+
+/*!
+ \internal
+
+ This function downloads Updates.xml from all the update sources. A single application can potentially
+ have several update sources, hence we need to be asynchronous in downloading updates from different
+ sources.
+
+ The function basically does this for each update source:
+ a) Create a KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update
+ b) Triggers the download of Updates.xml from each file downloader.
+ c) The downloadCompleted(), downloadCanceled() and downloadAborted() signals are connected
+ in each of the downloaders. Once all the downloads are complete and/or aborted, the next stage
+ would be done.
+
+ The function gets into an event loop until all the downloads are complete.
+*/
+bool UpdateFinder::Private::downloadUpdateXMLFiles()
+{
+ if (!application)
+ return false;
+
+ UpdateSourcesInfo *updateSources = application->updateSourcesInfo();
+ if (!updateSources )
+ return false;
+
+ // Create FileDownloader and UpdatesInfo for each update
+ for (int i = 0; i < updateSources->updateSourceInfoCount(); i++) {
+ UpdateSourceInfo info = updateSources->updateSourceInfo(i);
+ QUrl updateXmlUrl = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString());
+
+ FileDownloader *downloader = FileDownloaderFactory::instance().create(updateXmlUrl.scheme(), q);
+ if (!downloader)
+ continue;
+
+ downloader->setUrl(updateXmlUrl);
+ downloader->setAutoRemoveDownloadedFile(true);
+
+ UpdatesInfo *updatesInfo = new UpdatesInfo;
+ updateSourceInfoList.append(info);
+ updateXmlFDList.append(downloader);
+ updatesInfoList.append(updatesInfo);
+
+ connect(downloader, SIGNAL(downloadCompleted()),
+ q, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCanceled()),
+ q, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadAborted(QString)),
+ q, SLOT(slotDownloadDone()));
+ }
+
+ // Trigger download of Updates.xml file
+ downloadCompleteCount = 0;
+ for (int i = 0; i < updateXmlFDList.count(); i++) {
+ FileDownloader *downloader = updateXmlFDList.at(i);
+ downloader->download();
+ }
+
+ // Wait until all downloaders have completed their downloads.
+ while (true) {
+ QCoreApplication::processEvents();
+ if (cancel)
+ return false;
+ if (downloadCompleteCount == updateXmlFDList.count())
+ break;
+
+ int pc = computePercent(downloadCompleteCount, updateXmlFDList.count());
+ q->reportProgress(pc, tr("Downloading Updates.xml from update sources."));
+ }
+
+ // All the downloaders have now either downloaded or aborted the
+ // download of update XML files.
+
+ // Let's now get rid of update sources whose Updates.xml could not be downloaded
+ for (int i = 0; i < updateXmlFDList.count(); i++) {
+ FileDownloader *downloader = updateXmlFDList.at(i);
+ if (downloader->isDownloaded())
+ continue;
+
+ UpdateSourceInfo info = updateSourceInfoList.at(i);
+ QString msg = tr("Could not download updates from %1 ('%2')").arg(info.name, info.url.toString());
+ q->reportError(msg);
+
+ delete updatesInfoList[i];
+ delete downloader;
+ updateXmlFDList.removeAt(i);
+ updatesInfoList.removeAt(i);
+ updateSourceInfoList.removeAt(i);
+ --i;
+ }
+
+ if (updatesInfoList.isEmpty())
+ return false;
+
+ // Lets parse the downloaded update XML files and get rid of the downloaders.
+ for (int i = 0; i < updateXmlFDList.count(); i++) {
+ FileDownloader *downloader = updateXmlFDList.at(i);
+ UpdatesInfo *updatesInfo = updatesInfoList.at(i);
+
+ updatesInfo->setFileName(downloader->downloadedFileName());
+
+ if (!updatesInfo->isValid()) {
+ QString msg = updatesInfo->errorString();
+ q->reportError(msg);
+
+ delete updatesInfoList[i];
+ delete downloader;
+ updateXmlFDList.removeAt(i);
+ updatesInfoList.removeAt(i);
+ --i;
+ }
+ }
+ qDeleteAll(updateXmlFDList);
+ updateXmlFDList.clear();
+
+ if (updatesInfoList.isEmpty())
+ return false;
+
+ q->reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
+ return true;
+}
+
+/*!
+ \internal
+
+ This function runs through all the KDUpdater::UpdatesInfo objects created during
+ the downloadUpdateXMLFiles() method and compares it with the data contained in
+ KDUpdater::PackagesInfo. Thereby figures out whether an update is applicable for
+ this application or not.
+*/
+bool UpdateFinder::Private::computeApplicableUpdates()
+{
+ if (updateType & CompatUpdate) {
+ UpdateInfo compatUpdateInfo;
+ UpdateSourceInfo compatUpdateSourceInfo;
+
+ // Required compat level
+ int reqCompatLevel = application->compatLevel() + 1;
+
+ q->reportProgress(60, tr("Looking for compatibility update..."));
+
+ // We are only interested in compat updates.
+ for (int i = 0; i < updatesInfoList.count(); i++) {
+ UpdatesInfo *info = updatesInfoList.at(i);
+ UpdateSourceInfo updateSource = updateSourceInfoList.at(i);
+
+ // If we already have a compat update, just check if the source currently being
+ // considered has a higher priority or not.
+ if (compatUpdateInfo.data.contains(QLatin1String("CompatLevel")) && updateSource.priority < compatUpdateSourceInfo.priority)
+ continue;
+
+ // Let's look for compat updates that provide compat level one-higher than
+ // the application's current compat level.
+ QList<UpdateInfo> updatesInfo = info->updatesInfo(CompatUpdate, reqCompatLevel);
+
+ if (updatesInfo.count() == 0)
+ continue;
+
+ compatUpdateInfo = updatesInfo.at(0);
+ compatUpdateSourceInfo = updateSource;
+ }
+
+ bool found = compatUpdateInfo.data.contains(QLatin1String("CompatLevel"));
+ if (found) {
+ q->reportProgress(80, tr("Found compatibility update."));
+
+ // Create an update for this compat update.
+ // Pick a update file based on arch and OS.
+ int pickUpdateFileIndex = pickUpdateFileInfo(compatUpdateInfo.updateFiles);
+ if (pickUpdateFileIndex < 0) {
+ q->reportError(tr("Compatibility update for the required architecture and hardware configuration was "
+ "not found."));
+ q->reportProgress(100, tr("Compatibility update not found."));
+ return false;
+ }
+
+ UpdateFileInfo fileInfo = compatUpdateInfo.updateFiles.at(pickUpdateFileIndex);
+
+ // Create an update for this entry
+ QUrl url = QString::fromLatin1("%1/%2").arg( compatUpdateSourceInfo.url.toString(), fileInfo.fileName);
+ Update *update = q->constructUpdate(application, compatUpdateSourceInfo, CompatUpdate,
+ url, compatUpdateInfo.data, fileInfo.compressedSize,
+ fileInfo.uncompressedSize, fileInfo.sha1sum);
+
+ // Register the update
+ updates.append(update);
+
+ // Done
+ q->reportProgress(100, tr("Compatibility update found."));
+ } else {
+ q->reportProgress(100, tr("No compatibility updates found."));
+ }
+ }
+ if (updateType & PackageUpdate) {
+ // We are looking for normal updates, not compat ones.
+ for (int i = 0; i < updatesInfoList.count(); i++) {
+ // Fetch updates applicable to this application.
+ UpdatesInfo *info = updatesInfoList.at(i);
+ QList<UpdateInfo> updates = applicableUpdates(info , updateType & NewPackage);
+ if (!updates.count())
+ continue;
+
+ if (cancel)
+ return false;
+ UpdateSourceInfo updateSource = updateSourceInfoList.at(i);
+
+ // Create Update objects for updates that have a valid
+ // UpdateFile
+ createUpdateObjects(updateSource, updates);
+ if (cancel)
+ return false;
+
+ // Report progress
+ int pc = computePercent(i, updatesInfoList.count());
+ pc = computeProgressPercentage(51, 100, pc);
+ q->reportProgress(pc, tr("Computing applicable updates."));
+ }
+ }
+
+ q->reportProgress(99, tr("Application updates computed."));
+ return true;
+}
+
+QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesInfo, bool addNewPackages)
+{
+ QList<UpdateInfo> retList;
+
+ if (!updatesInfo || updatesInfo->updateInfoCount( PackageUpdate ) == 0)
+ return retList;
+
+ PackagesInfo *packages = this->application->packagesInfo();
+ if (!packages)
+ return retList;
+
+ // Check to see if the updates info contains updates for any application
+ bool anyApp = updatesInfo->applicationName() == QLatin1String("{AnyApplication}");
+ int appNameIndex = -1;
+
+ if (!anyApp) {
+ // updatesInfo->applicationName() describes one application or a series of
+ // application names separated by commas.
+ QString appName = updatesInfo->applicationName();
+ appName = appName.replace(QLatin1String( ", " ), QLatin1String( "," ));
+ appName = appName.replace(QLatin1String( " ," ), QLatin1String( "," ));
+
+ // Catch hold of app names contained updatesInfo->applicationName()
+ QStringList apps = appName.split(QRegExp(QLatin1String("\\b(,|, )\\b")), QString::SkipEmptyParts);
+ appNameIndex = apps.indexOf(this->application->applicationName());
+
+ // If the application appName isn't one of the app names, then
+ // the updates are not applicable.
+ if (appNameIndex < 0)
+ return retList;
+ }
+
+ // Check to see if version numbers match. This means that the version
+ // number of the update should be greater than the version number of
+ // the package that is currently installed.
+ QList<UpdateInfo> updateList = updatesInfo->updatesInfo(PackageUpdate);
+ for (int i = 0; i < updatesInfo->updateInfoCount(PackageUpdate); i++) {
+ UpdateInfo updateInfo = updateList.at(i);
+ if (!addNewPackages) {
+ int pkgInfoIdx = packages->findPackageInfo( updateInfo.data.value(QLatin1String("Name")).toString());
+ if (pkgInfoIdx < 0)
+ continue;
+
+ PackageInfo pkgInfo = packages->packageInfo(pkgInfoIdx);
+
+ // First check to see if the update version is higher than package version
+ QString updateVersion = updateInfo.data.value(QLatin1String("Version")).toString();
+ QString pkgVersion = pkgInfo.version;
+ if (KDUpdater::compareVersion(updateVersion, pkgVersion) <= 0)
+ continue;
+
+ // It is quite possible that we may have already installed the update.
+ // Lets check the last update date of the package and the release date
+ // of the update. This way we can compare and figure out if the update
+ // has been installed or not.
+ QDate pkgDate = pkgInfo.lastUpdateDate;
+ QDate updateDate = updateInfo.data.value(QLatin1String("ReleaseDate")).toDate();
+ if (pkgDate > updateDate)
+ continue;
+ }
+
+ // Bingo, we found an update :-)
+ retList.append(updateInfo);
+ }
+
+ return retList;
+}
+
+void UpdateFinder::Private::createUpdateObjects(const UpdateSourceInfo &sourceInfo, const QList<UpdateInfo> &updateInfoList)
+{
+ for (int i = 0; i < updateInfoList.count(); i++) {
+ UpdateInfo info = updateInfoList.at(i);
+ // Compat level checks
+ if (info.data.contains(QLatin1String("RequiredCompatLevel")) &&
+ info.data.value(QLatin1String("RequiredCompatLevel")).toInt() != application->compatLevel())
+ {
+ qDebug().nospace() << "Update \"" << info.data.value( QLatin1String( "Name" ) ).toString()
+ << "\" at \"" << sourceInfo.name << "\"(\"" << sourceInfo.url.toString()
+ << "\") requires a different compat level";
+ continue; // Compatibility level mismatch
+ }
+
+ // If another update of the same name exists, then use the update coming from
+ // a higher priority.
+ if (!checkForUpdatePriority(sourceInfo, info)) {
+ qDebug().nospace() << "Skipping Update \""
+ << info.data.value(QLatin1String("Name")).toString()
+ << "\" from \""
+ << sourceInfo.name
+ << "\"(\""
+ << sourceInfo.url.toString()
+ << "\") because an update with the same name was found from a higher priority location";
+
+ continue;
+ }
+
+ // Pick a update file based on arch and OS.
+ int pickUpdateFileIndex = this->pickUpdateFileInfo(info.updateFiles);
+ if (pickUpdateFileIndex < 0)
+ continue;
+
+ UpdateFileInfo fileInfo = info.updateFiles.at(pickUpdateFileIndex);
+
+ // Create an update for this entry
+ QUrl url(QString::fromLatin1("%1/%2").arg( sourceInfo.url.toString(), fileInfo.fileName));
+ Update *update = q->constructUpdate(application, sourceInfo, PackageUpdate, url, info.data, fileInfo.compressedSize, fileInfo.uncompressedSize, fileInfo.sha1sum);
+
+ // Register the update
+ this->updates.append(update);
+ }
+}
+
+bool UpdateFinder::Private::checkForUpdatePriority(const UpdateSourceInfo &sourceInfo, const UpdateInfo &updateInfo)
+{
+ for (int i = 0; i < this->updates.count(); i++){
+ Update *update = this->updates.at(i);
+ if (update->data(QLatin1String("Name")).toString() != updateInfo.data.value(QLatin1String("Name")).toString())
+ continue;
+
+ // Bingo, update was previously found elsewhere.
+
+ // If the existing update comes from a higher priority server, then cool :)
+ if (update->sourceInfo().priority > sourceInfo.priority)
+ return false;
+
+ // If the existing update has a higher version number, keep it
+ if (KDUpdater::compareVersion(update->data(QLatin1String("Version")).toString(),
+ updateInfo.data.value(QLatin1String("Version")).toString()) > 0)
+ return false;
+
+ // Otherwise the old update must be deleted.
+ this->updates.removeAll(update);
+ delete update;
+
+ return true;
+ }
+
+ // No update by that name was found, so what we have is a priority update.
+ return true;
+}
+
+int UpdateFinder::Private::pickUpdateFileInfo(const QList<UpdateFileInfo> &updateFiles)
+{
+#ifdef Q_WS_MAC
+ QString os = QLatin1String( "MacOSX" );
+#endif
+#ifdef Q_WS_WIN
+ QString os = QLatin1String( "Windows" );
+#endif
+#ifdef Q_WS_X11
+ QString os = QLatin1String( "Linux" );
+#endif
+
+ QString arch = QLatin1String( "i386" ); // only one architecture considered for now.
+
+ for (int i = 0; i < updateFiles.count(); i++) {
+ UpdateFileInfo fileInfo = updateFiles.at(i);
+
+ if (fileInfo.arch != arch)
+ continue;
+
+ if (fileInfo.os != QLatin1String("Any") && fileInfo.os != os)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+
+
+//
+// UpdateFinder
+//
+
+/*!
+ Constructs a update finder for a given \ref KDUpdater::Application.
+*/
+UpdateFinder::UpdateFinder(Application *application)
+ : Task(QLatin1String("UpdateFinder"), Stoppable, application),
+ d(new Private(this))
+{
+ d->application = application;
+}
+
+/*!
+ Destructor
+*/
+UpdateFinder::~UpdateFinder()
+{
+ delete d;
+}
+
+/*!
+ Returns a pointer to the update application for which this function computes all
+ the updates.
+*/
+Application *UpdateFinder::application() const
+{
+ return d->application;
+}
+
+/*!
+ Returns a list of KDUpdater::Update objects. The update objects returned in this list
+ are made children of the \ref KDUpdater::Application object associated with this class.
+*/
+QList<Update *> UpdateFinder::updates() const
+{
+ return d->updates;
+}
+
+/*!
+ Looks only for a certain type of update. By default, only package update
+*/
+void UpdateFinder::setUpdateType(UpdateTypes type)
+{
+ d->updateType = type;
+}
+
+/*!
+ Returns the type of updates searched
+*/
+UpdateTypes UpdateFinder::updateType() const
+{
+ return d->updateType;
+}
+
+/*!
+ \internal
+
+ Implemented from \ref KDUpdater::Task::doStart().
+*/
+void UpdateFinder::doRun()
+{
+ d->computeUpdates();
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool UpdateFinder::doStop()
+{
+ d->cancelComputeUpdates();
+
+ // Wait until the cancel has actually happened, and then return.
+ // Thinking of using QMutex for this. Frank/Till any suggestions?
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool UpdateFinder::doPause()
+{
+ // Not a pausable task
+ return false;
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool UpdateFinder::doResume()
+{
+ // Not a pausable task, hence it is not resumable as well
+ return false;
+}
+
+/*!
+ \internal
+*/
+void UpdateFinder::Private::slotDownloadDone()
+{
+ ++downloadCompleteCount;
+
+ int pc = computePercent(downloadCompleteCount, updateXmlFDList.count());
+ pc = computeProgressPercentage(0, 45, pc);
+ q->reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
+}
+
+/*!
+ \internal
+ */
+Update *UpdateFinder::constructUpdate(Application *application, const UpdateSourceInfo &sourceInfo,
+ UpdateType type, const QUrl &updateUrl, const QMap<QString, QVariant> &data,
+ quint64 compressedSize, quint64 uncompressedSize, const QByteArray &sha1sum )
+{
+ return new Update(application, sourceInfo, type, updateUrl, data, compressedSize, uncompressedSize, sha1sum);
+}
+
+
+/*!
+ \ingroup kdupdater
+
+ This function compares two version strings \c v1 and \c v2 and returns
+ -1, 0 or +1 based on the following rule
+
+ \li Returns 0 if v1 == v2
+ \li Returns -1 if v1 < v2
+ \li Returns +1 if v1 > v2
+
+ The function is very similar to \c strcmp(), except that it works on version strings.
+
+ Example:
+ \code
+
+ KDUpdater::compareVersion("2.0", "2.1"); // Returns -1
+ KDUpdater::compareVersion("2.1", "2.0"); // Returns +1
+ KDUpdater::compareVersion("2.0", "2.0"); // Returns 0
+ KDUpdater::compareVersion("2.1", "2.1"); // Returns 0
+
+ KDUpdater::compareVersion("2.0", "2.x"); // Returns 0
+ KDUpdater::compareVersion("2.x", "2.0"); // Returns 0
+
+ KDUpdater::compareVersion("2.0.12.4", "2.1.10.4"); // Returns -1
+ KDUpdater::compareVersion("2.0.12.x", "2.0.x"); // Returns 0
+ KDUpdater::compareVersion("2.1.12.x", "2.0.x"); // Returns +1
+ KDUpdater::compareVersion("2.1.12.x", "2.x"); // Returns 0
+ KDUpdater::compareVersion("2.x", "2.1.12.x"); // Returns 0
+
+ \endcode
+*/
+int KDUpdater::compareVersion(const QString &v1, const QString &v2)
+{
+ // For tests refer VersionCompareFnTest testcase.
+
+ // Check for equality
+ if (v1 == v2)
+ return 0;
+
+ // Split version numbers across "."
+ const QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-")));
+ const QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-")));
+
+ // Check each component of the version
+ int index = 0;
+ while (true) {
+ if (index == v1_comps.count() && index < v2_comps.count())
+ return -1;
+ if (index < v1_comps.count() && index == v2_comps.count())
+ return +1;
+ if (index >= v1_comps.count() || index >= v2_comps.count())
+ break;
+
+ bool v1_ok, v2_ok;
+ int v1_comp = v1_comps[index].toInt(&v1_ok);
+ int v2_comp = v2_comps[index].toInt(&v2_ok);
+
+ if (!v1_ok) {
+ if (v1_comps[index] == QLatin1String("x"))
+ return 0;
+ }
+ if (!v2_ok) {
+ if (v2_comps[index] == QLatin1String("x"))
+ return 0;
+ }
+ if (!v1_ok && !v2_ok)
+ return v1_comps[index].compare(v2_comps[index]);
+
+ if (v1_comp < v2_comp)
+ return -1;
+
+ if (v1_comp > v2_comp)
+ return +1;
+
+ // v1_comp == v2_comp
+ ++index;
+ }
+
+ if (index < v2_comps.count())
+ return +1;
+
+ if (index < v1_comps.count())
+ return -1;
+
+ // Controversial return. I hope this never happens.
+ return 0;
+}
+
+#include "moc_kdupdaterupdatefinder.cpp"
diff --git a/src/libs/kdtools/kdupdaterupdatefinder.h b/src/libs/kdtools/kdupdaterupdatefinder.h
new file mode 100644
index 000000000..4faa97dab
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatefinder.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_FINDER_H
+#define KD_UPDATER_UPDATE_FINDER_H
+
+#include "kdupdater.h"
+#include "kdupdatertask.h"
+
+#include <QList>
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+class QUrl;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+
+class Application;
+class Update;
+struct UpdateSourceInfo;
+
+class KDTOOLS_EXPORT UpdateFinder : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit UpdateFinder(Application *application);
+ ~UpdateFinder();
+
+ Application *application() const;
+ QList<Update *> updates() const;
+
+ void setUpdateType(UpdateTypes type);
+ UpdateTypes updateType() const;
+
+private:
+ void doRun();
+ bool doStop();
+ bool doPause();
+ bool doResume();
+
+ Update *constructUpdate(Application *application, const UpdateSourceInfo &sourceInfo,
+ UpdateType type, const QUrl &updateUrl, const QMap<QString, QVariant> &data,
+ quint64 compressedSize, quint64 uncompressedSize, const QByteArray &sha1sum);
+
+private:
+ class Private;
+ Private *d;
+ Q_PRIVATE_SLOT(d, void slotDownloadDone())
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_FINDER_H
diff --git a/src/libs/kdtools/kdupdaterupdateoperation.cpp b/src/libs/kdtools/kdupdaterupdateoperation.cpp
new file mode 100644
index 000000000..2c4af4b03
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperation.cpp
@@ -0,0 +1,388 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdateoperation.h"
+
+#include "kdupdaterapplication.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QTemporaryFile>
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::UpdateOperation kdupdaterupdateoperation.h KDUpdaterUpdateOperation
+ \brief Abstract base class for update operations.
+
+ The \ref KDUpdater::UpdateOperation is an abstract class that specifies an interface for
+ update operations. Concrete implementations of this class must perform a single update
+ operation like copy, move, delete etc.
+
+ \note Two seperate threads cannot be using a single instance of KDUpdater::UpdateOperation
+ at the same time.
+*/
+
+/*
+ * \internal
+ * Returns a filename for a temporary file based on \a templateName
+ */
+static QString backupFileName(const QString &templateName)
+{
+ const QFileInfo templ(templateName);
+ QTemporaryFile file( QDir::temp().absoluteFilePath(templ.fileName()));
+ file.open();
+ const QString name = file.fileName();
+ file.close();
+ file.remove();
+ return name;
+}
+
+using namespace KDUpdater;
+
+/*!
+ Constructor
+*/
+UpdateOperation::UpdateOperation()
+ : m_error(0), m_application(0)
+{}
+
+/*!
+ Destructor
+*/
+UpdateOperation::~UpdateOperation()
+{
+ if (Application *app = Application::instance())
+ app->addFilesForDelayedDeletion(filesForDelayedDeletion());
+}
+
+/*!
+ Returns the update operation name.
+
+ \sa setName()
+*/
+QString UpdateOperation::name() const
+{
+ return m_name;
+}
+
+/*!
+ Returns a command line string that describes the update operation. The returned
+ string would be of the form
+
+ <name> <arg1> <arg2> <arg3> ....
+*/
+QString UpdateOperation::operationCommand() const
+{
+ QString argsStr = m_arguments.join(QLatin1String( " " ));
+ return QString::fromLatin1( "%1 %2" ).arg(m_name, argsStr);
+}
+
+/*!
+ Returns true if there exists a setting called \a name. Otherwise returns false.
+*/
+bool UpdateOperation::hasValue(const QString &name) const
+{
+ return m_values.contains(name);
+}
+
+/*!
+ Clears the value of setting \a name and removes it.
+ \post hasValue( \a name ) returns false.
+*/
+void UpdateOperation::clearValue(const QString &name)
+{
+ m_values.remove(name);
+}
+
+/*!
+ Returns the value of setting \a name. If the setting does not exists,
+ this returns an empty QVariant.
+*/
+QVariant UpdateOperation::value(const QString &name) const
+{
+ return hasValue(name) ? m_values[name] : QVariant();
+}
+
+/*!
+ Sets the value of setting \a name to \a value.
+*/
+void UpdateOperation::setValue(const QString &name, const QVariant &value)
+{
+ m_values[name] = value;
+}
+
+/*!
+ Sets a update operation name. Subclasses will have to provide a unique
+ name to describe this operation.
+*/
+void UpdateOperation::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/*!
+ Through this function, arguments to the update operation can be specified
+ to the update operation.
+*/
+void UpdateOperation::setArguments(const QStringList &args)
+{
+ m_arguments = args;
+}
+
+/*!
+ Sets the Application for this operation.
+ This may be used by some operations
+*/
+void UpdateOperation::setApplication(Application *application)
+{
+ m_application = application;
+}
+
+/*!
+ Returns the last set function arguments.
+*/
+QStringList UpdateOperation::arguments() const
+{
+ return m_arguments;
+}
+
+/*!
+ Returns error details in case performOperation() failed.
+*/
+QString UpdateOperation::errorString() const
+{
+ return m_errorString;
+}
+
+/*!
+ * Can be used by subclasses to report more detailed error codes (optional).
+ * To check if an operation was successful, use the return value of performOperation().
+ */
+int UpdateOperation::error() const
+{
+ return m_error;
+}
+
+/*!
+ * Used by subclasses to set the error string.
+ */
+void UpdateOperation::setErrorString(const QString &str)
+{
+ m_errorString = str;
+}
+
+/*!
+ * Used by subclasses to set the error code.
+ */
+void UpdateOperation::setError(int error, const QString &errorString)
+{
+ m_error = error;
+ if (!errorString.isNull())
+ m_errorString = errorString;
+}
+
+/*!
+ Clears the previously set argument list and application
+*/
+void UpdateOperation::clear()
+{
+ m_arguments.clear();
+ m_application = 0;
+}
+
+QStringList UpdateOperation::filesForDelayedDeletion() const
+{
+ return m_delayedDeletionFiles;
+}
+
+/*!
+ Registers a file to be deleted later, once the application was restarted
+ (and the file isn't used anymore for sure).
+ @param files the files to be registered
+*/
+void UpdateOperation::registerForDelayedDeletion(const QStringList &files)
+{
+ m_delayedDeletionFiles << files;
+}
+
+/*!
+ Tries to delete \a file. If \a file can't be deleted, it gets registered for delayed deletion.
+*/
+bool UpdateOperation::deleteFileNowOrLater(const QString &file, QString *errorString)
+{
+ if (file.isEmpty() || QFile::remove(file))
+ return true;
+
+ if (!QFile::exists(file))
+ return true;
+
+ const QString backup = backupFileName(file);
+ QFile f(file);
+ if (!f.rename(backup)) {
+ if (errorString)
+ *errorString = f.errorString();
+ return false;
+ }
+ registerForDelayedDeletion(QStringList(backup));
+ return true;
+}
+
+/*!
+ Returns a pointer to the current Application
+*/
+Application *UpdateOperation::application() const
+{
+ return m_application;
+}
+
+/*!
+ \fn virtual void KDUpdater::UpdateOperation::backup() = 0;
+
+ Subclasses must implement this function to backup any data before performing the action.
+*/
+
+/*!
+ \fn virtual bool KDUpdater::UpdateOperation::performOperation() = 0;
+
+ Subclasses must implement this function to perform the update operation
+*/
+
+/*!
+ \fn virtual bool KDUpdater::UpdateOperation::undoOperation() = 0;
+
+ Subclasses must implement this function to perform the reverse of the operation.
+*/
+
+/*!
+ \fn virtual bool KDUpdater::UpdateOperation::testOperation() = 0;
+
+ Subclasses must implement this function to perform the test operation.
+*/
+
+/*!
+ \fn virtual bool KDUpdater::UpdateOperation::clone() = 0;
+
+ Subclasses must implement this function to clone the current operation.
+*/
+
+/*!
+ Saves this UpdateOperation in XML. You can override this method to store your own extra-data.
+ The default implementation is taking care of arguments and values set via setValue.
+*/
+QDomDocument UpdateOperation::toXml() const
+{
+ QDomDocument doc;
+ QDomElement root = doc.createElement(QLatin1String("operation"));
+ doc.appendChild(root);
+ QDomElement args = doc.createElement(QLatin1String("arguments"));
+ Q_FOREACH (const QString &s, arguments()) {
+ QDomElement arg = doc.createElement(QLatin1String("argument"));
+ arg.appendChild(doc.createTextNode(s));
+ args.appendChild(arg);
+ }
+ root.appendChild(args);
+ if (m_values.isEmpty())
+ return doc;
+
+ // append all values set with setValue
+ QDomElement values = doc.createElement(QLatin1String("values"));
+ for (QVariantMap::const_iterator it = m_values.begin(); it != m_values.end(); ++it) {
+ QDomElement value = doc.createElement(QLatin1String("value"));
+ const QVariant& variant = it.value();
+ value.setAttribute(QLatin1String("name"), it.key());
+ value.setAttribute(QLatin1String("type"), QLatin1String( QVariant::typeToName( variant.type())));
+
+ if (variant.type() != QVariant::List && variant.type() != QVariant::StringList && qVariantCanConvert<QString>(variant)) {
+ // it can convert to string? great!
+ value.appendChild( doc.createTextNode(variant.toString()));
+ } else {
+ // no? then we have to go the hard way...
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << variant;
+ value.appendChild(doc.createTextNode(QLatin1String( data.toBase64().data())));
+ }
+ values.appendChild(value);
+ }
+ root.appendChild(values);
+ return doc;
+}
+
+/*!
+ Restores UpdateOperation's arguments and values from the XML document \a doc.
+ Returns true on success, otherwise false.
+*/
+bool UpdateOperation::fromXml(const QDomDocument &doc)
+{
+ QStringList args;
+ const QDomElement root = doc.documentElement();
+ const QDomElement argsElem = root.firstChildElement(QLatin1String("arguments"));
+ Q_ASSERT(! argsElem.isNull());
+ for (QDomNode n = argsElem.firstChild(); ! n.isNull(); n = n.nextSibling()) {
+ const QDomElement e = n.toElement();
+ if (!e.isNull() && e.tagName() == QLatin1String("argument"))
+ args << e.text();
+ }
+ setArguments(args);
+
+ m_values.clear();
+ const QDomElement values = root.firstChildElement(QLatin1String("values"));
+ for (QDomNode n = values.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ const QDomElement v = n.toElement();
+ if (v.isNull() || v.tagName() != QLatin1String("value"))
+ continue;
+
+ const QString name = v.attribute(QLatin1String("name"));
+ const QString type = v.attribute(QLatin1String("type"));
+ const QString value = v.text();
+
+ const QVariant::Type t = QVariant::nameToType(type.toLatin1().data());
+ QVariant var = qVariantFromValue(value);
+ if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) {
+ QDataStream stream(QByteArray::fromBase64( value.toLatin1()));
+ stream >> var;
+ }
+
+ m_values[name] = var;
+ }
+
+ return true;
+}
+
+/*!
+ Restores UpdateOperation's arguments and values from the XML document at path \a xml.
+ Returns true on success, otherwise false.
+ \overload
+*/
+bool UpdateOperation::fromXml(const QString &xml)
+{
+ QDomDocument doc;
+ QString errorMsg;
+ int errorLine;
+ int errorColumn;
+ if (!doc.setContent( xml, &errorMsg, &errorLine, &errorColumn)) {
+ qWarning() << "Error parsing xml error=" << errorMsg << "line=" << errorLine << "column=" << errorColumn;
+ return false;
+ }
+ return fromXml(doc);
+}
diff --git a/src/libs/kdtools/kdupdaterupdateoperation.h b/src/libs/kdtools/kdupdaterupdateoperation.h
new file mode 100644
index 000000000..04f1c4858
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperation.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_OPERATION_H
+#define KD_UPDATER_UPDATE_OPERATION_H
+
+#include "kdupdater.h"
+
+#include <QCoreApplication>
+#include <QStringList>
+#include <QVariant>
+#include <QtXml/QDomDocument>
+
+namespace KDUpdater {
+
+class Application;
+
+class KDTOOLS_EXPORT UpdateOperation
+{
+ Q_DECLARE_TR_FUNCTIONS(UpdateOperation)
+
+public:
+ enum Error {
+ NoError = 0,
+ InvalidArguments = 1,
+ UserDefinedError = 128
+ };
+
+ UpdateOperation();
+ virtual ~UpdateOperation();
+
+ QString name() const;
+ QString operationCommand() const;
+
+ bool hasValue(const QString &name) const;
+ void clearValue(const QString &name);
+ QVariant value(const QString &name) const;
+ void setValue(const QString &name, const QVariant &value);
+
+ void setArguments(const QStringList &args);
+ void setApplication(Application *application);
+ QStringList arguments() const;
+ void clear();
+ QString errorString() const;
+ int error() const;
+ QStringList filesForDelayedDeletion() const;
+
+ virtual void backup() = 0;
+ virtual bool performOperation() = 0;
+ virtual bool undoOperation() = 0;
+ virtual bool testOperation() = 0;
+ virtual UpdateOperation *clone() const = 0;
+
+ virtual QDomDocument toXml() const;
+ virtual bool fromXml(const QString &xml);
+ virtual bool fromXml(const QDomDocument &doc);
+
+protected:
+ void setName(const QString &name);
+ Application *application() const;
+ void setErrorString(const QString &errorString);
+ void setError(int error, const QString &errorString = QString());
+ void registerForDelayedDeletion(const QStringList &files);
+ bool deleteFileNowOrLater(const QString &file, QString *errorString = 0);
+
+private:
+ QString m_name;
+ QStringList m_arguments;
+ QString m_errorString;
+ int m_error;
+ Application *m_application;
+ QVariantMap m_values;
+ QStringList m_delayedDeletionFiles;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_OPERATION_H
diff --git a/src/libs/kdtools/kdupdaterupdateoperationfactory.cpp b/src/libs/kdtools/kdupdaterupdateoperationfactory.cpp
new file mode 100644
index 000000000..aab8f6c2d
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperationfactory.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdateoperationfactory.h"
+#include "kdupdaterupdateoperations.h"
+
+#include <QHash>
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::UpdateOperationFactory kdupdaterupdateoperationfactory.h KDUpdaterUpdateOperationFactory
+ \brief Factory for \ref KDUpdater::UpdateOperation
+
+ This class acts as a factory for \ref KDUpdater::UpdateOperation. You can register
+ one or more update operations with this factory and query operations based on their name.
+
+ This class follows the singleton design pattern. Only one instance of this class can
+ be created and its reference can be fetched from the \ref instance() method.
+*/
+
+/*!
+ \fn KDUpdater::UpdateOperationFactory::registerUpdateOperation( const QString& name )
+
+ Registers T as new UpdateOperation with \a name. When create() is called with that \a name,
+ T is constructed using its default constructor.
+*/
+
+using namespace KDUpdater;
+
+/*!
+ Returns the UpdateOperationFactory instance. The instance is created if needed.
+*/
+UpdateOperationFactory &UpdateOperationFactory::instance()
+{
+ static UpdateOperationFactory theFactory;
+ return theFactory;
+}
+
+/*!
+ Constructor
+*/
+UpdateOperationFactory::UpdateOperationFactory()
+{
+ // Register the default update operation set
+ registerUpdateOperation<CopyOperation>(QLatin1String("Copy"));
+ registerUpdateOperation<MoveOperation>(QLatin1String("Move"));
+ registerUpdateOperation<DeleteOperation>(QLatin1String("Delete"));
+ registerUpdateOperation<MkdirOperation>(QLatin1String("Mkdir"));
+ registerUpdateOperation<RmdirOperation>(QLatin1String("Rmdir"));
+ registerUpdateOperation<AppendFileOperation>(QLatin1String("AppendFile"));
+ registerUpdateOperation<PrependFileOperation>(QLatin1String("PrependFile"));
+ registerUpdateOperation<ExecuteOperation>(QLatin1String("Execute"));
+ registerUpdateOperation<UpdatePackageOperation>(QLatin1String("UpdatePackage"));
+ registerUpdateOperation<UpdateCompatOperation>(QLatin1String("UpdateCompat"));
+}
diff --git a/src/libs/kdtools/kdupdaterupdateoperationfactory.h b/src/libs/kdtools/kdupdaterupdateoperationfactory.h
new file mode 100644
index 000000000..c21727f78
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperationfactory.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_OPERATION_FACTORY_H
+#define KD_UPDATER_UPDATE_OPERATION_FACTORY_H
+
+#include <kdgenericfactory.h>
+
+#include "kdupdater.h"
+
+namespace KDUpdater {
+
+class UpdateOperation;
+
+typedef KDGenericFactory<UpdateOperation>::FactoryFunction UpdateOperationFactoryFunction;
+
+class KDTOOLS_EXPORT UpdateOperationFactory : public KDGenericFactory<UpdateOperation>
+{
+ Q_DISABLE_COPY(UpdateOperationFactory)
+
+public:
+ static UpdateOperationFactory &instance();
+
+ template <class T>
+ void registerUpdateOperation(const QString &name)
+ {
+ registerProduct<T>(name);
+ }
+
+protected:
+ UpdateOperationFactory();
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_OPERATION_FACTORY_H
diff --git a/src/libs/kdtools/kdupdaterupdateoperations.cpp b/src/libs/kdtools/kdupdaterupdateoperations.cpp
new file mode 100644
index 000000000..681631542
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperations.cpp
@@ -0,0 +1,1058 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdateoperations.h"
+#include "kdupdaterapplication.h"
+#include "kdupdaterpackagesinfo.h"
+#include "environment.h"
+
+#include <QFile>
+#include <QDir>
+#include <QDirIterator>
+#include <QProcess>
+#include <QTextStream>
+#include <QDebug>
+#include <QTemporaryFile>
+
+
+#include <cerrno>
+
+#define SUPPORT_DETACHED_PROCESS_EXECUTION
+
+#ifdef SUPPORT_DETACHED_PROCESS_EXECUTION
+#ifdef Q_WS_WIN
+#include <windows.h>
+#endif
+#endif
+
+using namespace KDUpdater;
+
+static bool removeDirectory(const QString &path, QString *errorString)
+{
+ Q_ASSERT(errorString);
+ const QFileInfoList entries = QDir(path).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
+ for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) {
+ if (it->isDir() && !it->isSymLink()) {
+ removeDirectory(it->filePath(), errorString);
+ } else {
+ QFile f(it->filePath());
+ if (!f.remove())
+ return false;
+ }
+ }
+
+ errno = 0;
+ const bool success = QDir().rmdir(path);
+ if (errno)
+ *errorString = QLatin1String(strerror(errno));
+ return success;
+}
+/*
+ * \internal
+ * Returns a filename for a temporary file based on \a templateName
+ */
+static QString backupFileName(const QString &templateName)
+{
+ QTemporaryFile file(templateName);
+ file.open();
+ const QString name = file.fileName();
+ file.close();
+ file.remove();
+ return name;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::CopyOperation
+////////////////////////////////////////////////////////////////////////////
+
+CopyOperation::CopyOperation()
+{
+ setName(QLatin1String("Copy"));
+}
+
+CopyOperation::~CopyOperation()
+{
+ deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString());
+}
+
+void CopyOperation::backup()
+{
+ const QString dest = arguments().last();
+ if (!QFile::exists(dest)) {
+ clearValue(QLatin1String("backupOfExistingDestination"));
+ return;
+ }
+
+ setValue(QLatin1String("backupOfExistingDestination"), backupFileName(dest));
+
+ // race condition: The backup file could get created
+ // by another process right now. But this is the same
+ // in QFile::copy...
+ const bool success = QFile::rename(dest, value(QLatin1String("backupOfExistingDestination")).toString());
+ if (!success)
+ setError(UserDefinedError, tr("Could not backup file %1.").arg(dest));
+}
+
+bool CopyOperation::performOperation()
+{
+ // We need two args to complete the copy operation.
+ // First arg provides the complete file name of source
+ // Second arg provides the complete file name of dest
+ QStringList args = this->arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count()));
+ return false;
+ }
+ QString source = args.first();
+ QString dest = args.last();
+
+ // If destination file exists, then we cannot use QFile::copy()
+ // because it does not overwrite an existing file. So we remove
+ // the destination file.
+ if (QFile::exists(dest)) {
+ QFile file(dest);
+ if (!file.remove()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not remove destination file %1: %2").arg(dest, file.errorString()));
+ return false;
+ }
+ }
+
+ QFile file(source);
+ const bool copied = file.copy(dest);
+ if (!copied) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not copy %1 to %2: %3").arg(source, dest, file.errorString()));
+ }
+ return copied;
+}
+
+bool CopyOperation::undoOperation()
+{
+ const QString dest = arguments().last();
+
+ QFile destF(dest);
+ // first remove the dest
+ if (!destF.remove()) {
+ setError(UserDefinedError, tr("Could not delete file %1: %2").arg(dest, destF.errorString()));
+ return false;
+ }
+
+ // no backup was done:
+ // the copy destination file wasn't existing yet - that's no error
+ if (!hasValue(QLatin1String("backupOfExistingDestination")))
+ return true;
+
+ QFile backupF(value(QLatin1String("backupOfExistingDestination")).toString());
+ // otherwise we have to copy the backup back:
+ const bool success = backupF.rename(dest);
+ if (!success)
+ setError(UserDefinedError, tr("Could not restore backup file into %1: %2").arg(dest, backupF.errorString()));
+ return success;
+}
+
+/*!
+ \reimp
+ */
+QDomDocument CopyOperation::toXml() const
+{
+ // we don't want to save the backupOfExistingDestination
+ if (!hasValue(QLatin1String("backupOfExistingDestination")))
+ return UpdateOperation::toXml();
+
+ CopyOperation *const me = const_cast<CopyOperation *>(this);
+
+ const QVariant v = value(QLatin1String("backupOfExistingDestination"));
+ me->clearValue(QLatin1String("backupOfExistingDestination"));
+ const QDomDocument xml = UpdateOperation::toXml();
+ me->setValue(QLatin1String("backupOfExistingDestination"), v);
+ return xml;
+}
+
+bool CopyOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+CopyOperation *CopyOperation::clone() const
+{
+ return new CopyOperation();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::MoveOperation
+////////////////////////////////////////////////////////////////////////////
+
+MoveOperation::MoveOperation()
+{
+ setName(QLatin1String("Move"));
+}
+
+MoveOperation::~MoveOperation()
+{
+ deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString());
+}
+
+void MoveOperation::backup()
+{
+ const QString dest = arguments().last();
+ if (!QFile::exists(dest)) {
+ clearValue(QLatin1String("backupOfExistingDestination"));
+ return;
+ }
+
+ setValue(QLatin1String("backupOfExistingDestination"), backupFileName(dest));
+
+ // race condition: The backup file could get created
+ // by another process right now. But this is the same
+ // in QFile::copy...
+ const bool success = QFile::rename(dest, value(QLatin1String("backupOfExistingDestination")).toString());
+ if (!success)
+ setError(UserDefinedError, tr("Could not backup file %1.").arg(dest));
+}
+
+bool MoveOperation::performOperation()
+{
+ // We need two args to complete the copy operation.
+ // First arg provides the complete file name of source
+ // Second arg provides the complete file name of dest
+ QStringList args = this->arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count()));
+ return false;
+ }
+
+ QString source = args.first();
+ QString dest = args.last();
+
+ // If destination file exists, then we cannot use QFile::copy()
+ // because it does not overwrite an existing file. So we remove
+ // the destination file.
+ if (QFile::exists(dest)) {
+ QFile file(dest);
+ if (!file.remove(dest)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not remove destination file %1: %2").arg(dest, file.errorString()));
+ return false;
+ }
+ }
+
+ // Copy source to destination.
+ QFile file(source);
+ const bool copied = file.copy(source, dest);
+ if (!copied) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not copy %1 to %2: %3").arg(source, dest, file.errorString()));
+ return false;
+ }
+
+ return deleteFileNowOrLater(source);
+}
+
+bool MoveOperation::undoOperation()
+{
+ const QStringList args = arguments();
+ const QString& source = args.first();
+ const QString& dest = args.last();
+
+ // first: copy back the destination to source
+ QFile destF(dest);
+ if (!destF.copy(source)) {
+ setError(UserDefinedError, tr("Cannot copy %1 to %2: %3").arg(dest, source, destF.errorString()));
+ return false;
+ }
+
+ // second: delete the move destination
+ if (!deleteFileNowOrLater(dest)) {
+ setError(UserDefinedError, tr("Cannot remove file %1."));
+ return false;
+ }
+
+ // no backup was done:
+ // the move destination file wasn't existing yet - that's no error
+ if (!hasValue(QLatin1String("backupOfExistingDestination")))
+ return true;
+
+ // otherwise we have to copy the backup back:
+ QFile backupF(value(QLatin1String("backupOfExistingDestination")).toString());
+ const bool success = backupF.rename(dest);
+ if (!success)
+ setError(UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(dest, backupF.errorString()));
+
+ return success;
+}
+
+bool MoveOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+MoveOperation *MoveOperation::clone() const
+{
+ return new MoveOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::DeleteOperation
+////////////////////////////////////////////////////////////////////////////
+
+DeleteOperation::DeleteOperation()
+{
+ setName(QLatin1String("Delete"));
+}
+
+DeleteOperation::~DeleteOperation()
+{
+ deleteFileNowOrLater(value(QLatin1String("backupOfExistingFile")).toString());
+}
+
+void DeleteOperation::backup()
+{
+ const QString fileName = arguments().first();
+ setValue(QLatin1String("backupOfExistingFile"), backupFileName(fileName));
+ QFile file(fileName);
+ const bool success = file.copy(value(QLatin1String("backupOfExistingFile")).toString());
+ if (!success)
+ setError(UserDefinedError, tr("Cannot create backup of %1: %2").arg(fileName, file.errorString()));
+}
+
+bool DeleteOperation::performOperation()
+{
+ // Requires only one parameter. That is the name of
+ // the file to remove.
+ QStringList args = this->arguments();
+ if (args.count() != 1) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 1 expected.").arg(args.count()));
+ return false;
+ }
+
+ const QString fName = args.first();
+ return deleteFileNowOrLater(fName);
+}
+
+bool DeleteOperation::undoOperation()
+{
+ if (!hasValue(QLatin1String("backupOfExistingFile")))
+ return true;
+
+ const QString fileName = arguments().first();
+ QFile backupF(value(QLatin1String("backupOfExistingFile")).toString());
+ const bool success = backupF.copy(fileName) && deleteFileNowOrLater(backupF.fileName());
+ if (!success)
+ setError(UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(fileName, backupF.errorString()));
+
+ return success;
+}
+
+bool DeleteOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+DeleteOperation *DeleteOperation::clone() const
+{
+ return new DeleteOperation;
+}
+
+/*!
+ \reimp
+ */
+QDomDocument DeleteOperation::toXml() const
+{
+ // we don't want to save the backupOfExistingFile
+ if (!hasValue(QLatin1String("backupOfExistingFile")))
+ return UpdateOperation::toXml();
+
+ DeleteOperation *const me = const_cast<DeleteOperation *>(this);
+
+ const QVariant v = value(QLatin1String("backupOfExistingFile"));
+ me->clearValue(QLatin1String("backupOfExistingFile"));
+ const QDomDocument xml = UpdateOperation::toXml();
+ me->setValue(QLatin1String("backupOfExistingFile"), v);
+ return xml;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::MkdirOperation
+////////////////////////////////////////////////////////////////////////////
+
+MkdirOperation::MkdirOperation()
+{
+ setName(QLatin1String("Mkdir"));
+}
+
+void MkdirOperation::backup()
+{
+ static const QRegExp re(QLatin1String("\\\\|/"));
+ static const QLatin1String sep("/");
+
+ QString path = arguments().first();
+ path.replace(re, sep);
+
+ QDir createdDir = QDir::root();
+
+ // find out, which part of the path is the first one we actually need to create
+ int end = 0;
+ while (true) {
+ QString p = path.section(sep, 0, ++end);
+ createdDir = QDir(p);
+ if (!createdDir.exists())
+ break;
+ if (p == path) {
+ // everything did already exist -> nothing to do for us (nothing to revert then, either)
+ createdDir = QDir::root();
+ break;
+ }
+ }
+
+ setValue(QLatin1String("createddir"), createdDir.absolutePath());
+}
+
+bool MkdirOperation::performOperation()
+{
+ // Requires only one parameter. That is the name of
+ // the file to remove.
+ QStringList args = this->arguments();
+ if (args.count() != 1) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 1 expected.").arg(args.count()));
+ return false;
+ }
+ QString dirName = args.first();
+ const bool created = QDir::root().mkpath(dirName);
+ if (!created) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not create folder %1: Unknown error.").arg(dirName));
+ }
+ return created;
+}
+
+bool MkdirOperation::undoOperation()
+{
+ Q_ASSERT(arguments().count() == 1);
+
+ QDir createdDir = QDir(value(QLatin1String("createddir")).toString());
+ const bool forceremoval = QVariant(value(QLatin1String("forceremoval"))).toBool();
+
+ // Since refactoring we know the mkdir operation which is creating the target path. If we do a full
+ // uninstall prevent removing the full path including target, instead remove the target only. (QTIFW-46)
+ if (hasValue(QLatin1String("uninstall-only")) && value(QLatin1String("uninstall-only")).toBool())
+ createdDir = QDir(arguments().first());
+
+ if (createdDir == QDir::root())
+ return true;
+
+ if (!createdDir.exists())
+ return true;
+
+ QString errorString;
+ if (forceremoval)
+ return removeDirectory(createdDir.path(), &errorString);
+
+ // even remove some hidden, OS-created files in there
+#if defined Q_WS_MAC
+ QFile::remove(createdDir.path() + QLatin1String("/.DS_Store"));
+#elif defined Q_WS_WIN
+ QFile::remove(createdDir.path() + QLatin1String("/Thumbs.db"));
+#endif
+
+ errno = 0;
+ const bool result = QDir::root().rmdir(createdDir.path());
+ if (!result) {
+ if (errorString.isEmpty())
+ setError(UserDefinedError, tr("Cannot remove directory %1: %2").arg(createdDir.path(), errorString));
+ else
+ setError(UserDefinedError, tr("Cannot remove directory %1: %2").arg(createdDir.path(), QLatin1String(strerror(errno))));
+ }
+ return result;
+}
+
+bool KDUpdater::MkdirOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+MkdirOperation *MkdirOperation::clone() const
+{
+ return new MkdirOperation;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::RmdirOperation
+////////////////////////////////////////////////////////////////////////////
+
+RmdirOperation::RmdirOperation()
+{
+ setValue(QLatin1String("removed"), false);
+ setName(QLatin1String("Rmdir"));
+}
+
+void RmdirOperation::backup()
+{
+ // nothing to backup - rollback will just create the directory
+}
+
+bool RmdirOperation::performOperation()
+{
+ // Requires only one parameter. That is the name of
+ // the file to remove.
+ QStringList args = this->arguments();
+ if (args.count() != 1) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 1 expected.").arg(args.count()));
+ return false;
+ }
+
+ QString dirName = args.first();
+ QDir dir(dirName);
+ if (!dir.exists()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not remove folder %1: The folder does not exist.").arg(dirName));
+ return false;
+ }
+
+ errno = 0;
+ const bool removed = dir.rmdir(dirName);
+ setValue(QLatin1String("removed"), removed);
+ if (!removed) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not remove folder %1: %2").arg(dirName, QLatin1String(strerror(errno))));
+ }
+ return removed;
+}
+
+bool RmdirOperation::undoOperation()
+{
+ if (!value(QLatin1String("removed")).toBool())
+ return true;
+
+ const QFileInfo fi(arguments().first());
+ errno = 0;
+ const bool success = fi.dir().mkdir(fi.fileName());
+ if( !success)
+ setError(UserDefinedError, tr("Cannot recreate directory %1: %2").arg(fi.fileName(), QLatin1String(strerror(errno))));
+
+ return success;
+}
+
+bool RmdirOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+RmdirOperation *RmdirOperation::clone() const
+{
+ return new RmdirOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::AppendFileOperation
+////////////////////////////////////////////////////////////////////////////
+
+AppendFileOperation::AppendFileOperation()
+{
+ setName(QLatin1String("AppendFile"));
+}
+
+void AppendFileOperation::backup()
+{
+ const QString filename = arguments().first();
+
+ QFile file(filename);
+ if (!file.exists())
+ return; // nothing to backup
+
+ setValue(QLatin1String("backupOfFile"), backupFileName(filename));
+ if (!file.copy(value(QLatin1String("backupOfFile")).toString())) {
+ setError(UserDefinedError, tr("Cannot backup file %1: %2").arg(filename, file.errorString()));
+ clearValue(QLatin1String("backupOfFile"));
+ }
+}
+
+bool AppendFileOperation::performOperation()
+{
+ // This operation takes two arguments. First argument is the name
+ // of the file into which a text has to be appended. Second argument
+ // is the text to append.
+ QStringList args = this->arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count()));
+ return false;
+ }
+
+ QString fName = args.first();
+ QString text = args.last();
+
+ QFile file(fName);
+ if (!file.open(QFile::Append)) {
+ // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion
+ const QString newName = backupFileName(fName);
+ if (!QFile::rename(fName, newName) && QFile::copy(newName, fName) && file.open(QFile::Append)) {
+ QFile::rename(newName, fName);
+ setError(UserDefinedError);
+ setErrorString(tr("Could not open file %1 for writing: %2").arg(file.fileName(), file.errorString()));
+ return false;
+ }
+ deleteFileNowOrLater(newName);
+ }
+
+ QTextStream ts(&file);
+ ts << text;
+ file.close();
+
+ return true;
+}
+
+bool AppendFileOperation::undoOperation()
+{
+ // backupOfFile being empty -> file didn't exist before -> no error
+ const QString filename = arguments().first();
+ const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
+ if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) {
+ setError(UserDefinedError, tr("Cannot find backup file for %1.").arg(filename));
+ return false;
+ }
+
+ const bool removed = deleteFileNowOrLater(filename);
+ if (!removed) {
+ setError(UserDefinedError, tr("Could not restore backup file for %1.").arg(filename));
+ return false;
+ }
+
+ // got deleted? We might be done, if it didn't exist before
+ if (backupOfFile.isEmpty())
+ return true;
+
+ QFile backupFile(backupOfFile);
+ const bool success = backupFile.rename(filename);
+ if (!success)
+ setError(UserDefinedError, tr("Could not restore backup file for %1: %2").arg(filename, backupFile.errorString()));
+ return success;
+}
+
+bool AppendFileOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+AppendFileOperation *AppendFileOperation::clone() const
+{
+ return new AppendFileOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::PrependFileOperation
+////////////////////////////////////////////////////////////////////////////
+
+PrependFileOperation::PrependFileOperation()
+{
+ setName(QLatin1String("PrependFile"));
+}
+
+void PrependFileOperation::backup()
+{
+ const QString filename = arguments().first();
+
+ QFile file(filename);
+ if (!file.exists())
+ return; // nothing to backup
+
+ setValue(QLatin1String("backupOfFile"), backupFileName(filename));
+ if (!file.copy(value(QLatin1String("backupOfFile")).toString())) {
+ setError(UserDefinedError, tr("Cannot backup file %1: %2").arg(filename, file.errorString()));
+ clearValue(QLatin1String("backupOfFile"));
+ }
+}
+
+bool PrependFileOperation::performOperation()
+{
+ // This operation takes two arguments. First argument is the name
+ // of the file into which a text has to be appended. Second argument
+ // is the text to append.
+ QStringList args = this->arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count()));
+ return false;
+ }
+
+ QString fName = args.first();
+ QString text = args.last();
+
+ // Load the file first.
+ QFile file(fName);
+ if (!file.open(QFile::ReadOnly)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not open file %1 for reading: %2").arg(file.fileName(), file.errorString()));
+ return false;
+ }
+ QString fContents(QLatin1String(file.readAll()));
+ file.close();
+
+ // Prepend text to the file text
+ fContents = text + fContents;
+
+ // Now re-open the file in write only mode.
+ if (!file.open(QFile::WriteOnly)) {
+ // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion
+ const QString newName = backupFileName(fName);
+ if (!QFile::rename(fName, newName) && QFile::copy(newName, fName) && file.open(QFile::WriteOnly)) {
+ QFile::rename(newName, fName);
+ setError(UserDefinedError);
+ setErrorString(tr("Could not open file %1 for writing: %2").arg(file.fileName(), file.errorString()));
+ return false;
+ }
+ deleteFileNowOrLater(newName);
+ }
+ QTextStream ts(&file);
+ ts << fContents;
+ file.close();
+
+ return true;
+}
+
+bool PrependFileOperation::undoOperation()
+{
+ // bockupOfFile being empty -> file didn't exist before -> no error
+ const QString filename = arguments().first();
+ const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
+ if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) {
+ setError(UserDefinedError, tr("Cannot find backup file for %1.").arg(filename));
+ return false;
+ }
+
+ if (!deleteFileNowOrLater(filename)) {
+ setError(UserDefinedError, tr("Cannot restore backup file for %1.").arg(filename));
+ return false;
+ }
+
+ // got deleted? We might be done, if it didn't exist before
+ if (backupOfFile.isEmpty())
+ return true;
+
+ QFile backupF(backupOfFile);
+ const bool success = backupF.rename(filename);
+ if (!success)
+ setError(UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(filename, backupF.errorString()));
+
+ return success;
+}
+
+bool PrependFileOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+PrependFileOperation *PrependFileOperation::clone() const
+{
+ return new PrependFileOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::ExecuteOperation
+////////////////////////////////////////////////////////////////////////////
+
+ExecuteOperation::ExecuteOperation()
+ : QObject()
+{
+ setName(QLatin1String("Execute"));
+}
+
+void ExecuteOperation::backup()
+{
+ // this is not possible, since the process can do whatever...
+}
+
+#if defined(SUPPORT_DETACHED_PROCESS_EXECUTION) && defined(Q_WS_WIN)
+// stolen from qprocess_win.cpp
+static QString qt_create_commandline(const QString &program, const QStringList &arguments)
+{
+ QString args;
+ if (!program.isEmpty()) {
+ QString programName = program;
+ if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
+ programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
+ programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
+
+ // add the prgram as the first arg ... it works better
+ args = programName + QLatin1Char(' ');
+ }
+
+ for (int i = 0; i < arguments.size(); ++i) {
+ QString tmp = arguments.at(i);
+ // in the case of \" already being in the string the \ must also be escaped
+ tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\""));
+ // escape a single " because the arguments will be parsed
+ tmp.replace(QLatin1Char('\"'), QLatin1String("\\\""));
+ if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ QString endQuote(QLatin1Char('\"'));
+ int i = tmp.length();
+ while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) {
+ --i;
+ endQuote += QLatin1Char('\\');
+ }
+ args += QLatin1String(" \"") + tmp.left(i) + endQuote;
+ } else {
+ args += QLatin1Char(' ') + tmp;
+ }
+ }
+ return args;
+}
+#endif
+
+bool ExecuteOperation::performOperation()
+{
+ // This operation receives only one argument. It is the complete
+ // command line of the external program to execute.
+ QStringList args = this->arguments();
+ if (args.isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count()));
+ return false;
+ }
+
+ QList<int> allowedExitCodes;
+
+ QRegExp re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
+ if (re.exactMatch(args.first())) {
+ const QStringList numbers = re.cap(1).split(QLatin1Char(','));
+ for (QStringList::const_iterator it = numbers.begin(); it != numbers.end(); ++it)
+ allowedExitCodes.push_back(it->toInt());
+ args.pop_front();
+ } else {
+ allowedExitCodes.push_back(0);
+ }
+
+ bool success = false;
+#ifdef SUPPORT_DETACHED_PROCESS_EXECUTION
+ // unix style: when there's an ampersand after the command, it's started detached
+ if (args.count() >= 2 && args.last() == QLatin1String("&")) {
+ args.pop_back();
+#ifdef Q_WS_WIN
+ QString arguments = qt_create_commandline(args.front(), args.mid(1));
+
+ PROCESS_INFORMATION pinfo;
+
+ STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0,
+ static_cast< ulong >(CW_USEDEFAULT), static_cast< ulong >(CW_USEDEFAULT),
+ static_cast< ulong >(CW_USEDEFAULT), static_cast< ulong >(CW_USEDEFAULT),
+ 0, 0, 0, STARTF_USESHOWWINDOW, SW_HIDE, 0, 0, 0, 0, 0
+ };
+ success = CreateProcess(0, const_cast< wchar_t* >(static_cast< const wchar_t* >(arguments.utf16())),
+ 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
+ 0,
+ &startupInfo, &pinfo);
+
+#else
+ success = QProcess::startDetached(args.front(), args.mid(1));
+#endif
+ }
+ else
+#endif
+ {
+ Environment::instance().applyTo(&process); //apply non-persistent variables
+ process.start(args.front(), args.mid(1));
+
+ QEventLoop loop;
+ QObject::connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
+ QObject::connect(&process, SIGNAL(readyRead()), this, SLOT(readProcessOutput()));
+ success = process.waitForStarted(-1);
+ if (success) {
+ loop.exec();
+ setValue(QLatin1String("ExitCode"), process.exitCode());
+ success = allowedExitCodes.contains(process.exitCode());
+ }
+ }
+ if (!success) {
+ setError(UserDefinedError);
+ setErrorString(tr("Execution failed: %1").arg(args.join(QLatin1String(" "))));
+ }
+
+ return success;
+}
+
+/*!
+ Cancels the ExecuteOperation. This methods tries to terminate the process
+ gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed.
+ */
+void ExecuteOperation::cancelOperation()
+{
+ if (process.state() == QProcess::Running)
+ process.terminate();
+ if (!process.waitForFinished(10000))
+ process.kill();
+}
+
+void ExecuteOperation::readProcessOutput()
+{
+ QByteArray output = process.readAll();
+ if (!output.isEmpty())
+ emit outputTextChanged(QString::fromLocal8Bit(output));
+}
+
+bool ExecuteOperation::undoOperation()
+{
+ // this is not possible, since the process can do whatever...
+ return false;
+}
+
+bool ExecuteOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+ExecuteOperation *ExecuteOperation::clone() const
+{
+ return new ExecuteOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::UpdatePackageOperation
+////////////////////////////////////////////////////////////////////////////
+
+UpdatePackageOperation::UpdatePackageOperation()
+{
+ setName(QLatin1String("UpdatePackage"));
+}
+
+void UpdatePackageOperation::backup()
+{
+ const PackageInfo info = application()->packagesInfo()->packageInfo(application()->packagesInfo()->findPackageInfo(arguments().first()));
+ setValue(QLatin1String("oldVersion"), info.version);
+ setValue(QLatin1String("oldDate"), info.lastUpdateDate);
+}
+
+bool UpdatePackageOperation::performOperation()
+{
+ // This operation receives three arguments : the name of the package
+ // the new version and the release date
+ const QStringList args = this->arguments();
+ if (args.count() != 3) {
+ setError(InvalidArguments, tr("Invalid arguments: %1 arguments given, 3 expected.").arg(args.count()));
+ return false;
+ }
+
+ const QString &packageName = args.at(0);
+ const QString &version = args.at(1);
+ const QDate date = QDate::fromString(args.at(2));
+ const bool success = application()->packagesInfo()->updatePackage(packageName, version, date);
+ if (!success)
+ setError(UserDefinedError, tr("Cannot update %1-%2").arg(packageName, version));
+
+ return success;
+}
+
+bool UpdatePackageOperation::undoOperation()
+{
+ const QString packageName = arguments().first();
+ const QString version = arguments().at(1);
+ const QString oldVersion = value(QLatin1String("oldVersion")).toString();
+ const QDate oldDate = value(QLatin1String("oldDate")).toDate();
+ const bool success = application()->packagesInfo()->updatePackage(packageName, oldVersion, oldDate);
+ if (!success)
+ setError(UserDefinedError, tr("Cannot restore %1-%2").arg(packageName, version));
+
+ return success;
+}
+
+bool UpdatePackageOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+UpdatePackageOperation *UpdatePackageOperation::clone() const
+{
+ return new UpdatePackageOperation;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// KDUpdater::UpdateCompatOperation
+////////////////////////////////////////////////////////////////////////////
+
+UpdateCompatOperation::UpdateCompatOperation()
+{
+ setName(QLatin1String("UpdateCompatLevel"));
+}
+
+void UpdateCompatOperation::backup()
+{
+ setValue(QLatin1String("oldCompatLevel"), application()->packagesInfo()->compatLevel());
+}
+
+bool UpdateCompatOperation::performOperation()
+{
+ // This operation receives one argument : the new compat level
+ const QStringList args = this->arguments();
+ if (args.count() != 1) {
+ setError(InvalidArguments, tr("Invalid arguments: %1 arguments given, 1 expected.").arg(args.count()));
+ return false;
+ }
+
+ const int level = args.first().toInt();
+ application()->packagesInfo()->setCompatLevel(level);
+ return true;
+}
+
+bool UpdateCompatOperation::undoOperation()
+{
+ if (!hasValue(QLatin1String("oldCompatLevel"))) {
+ setError(UserDefinedError, tr("Cannot restore previous compat-level"));
+ return false;
+ }
+
+ application()->packagesInfo()->setCompatLevel(value(QLatin1String("oldCompatLevel")).toInt());
+ return true;
+}
+
+bool UpdateCompatOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+UpdateCompatOperation *UpdateCompatOperation::clone() const
+{
+ return new UpdateCompatOperation;
+}
diff --git a/src/libs/kdtools/kdupdaterupdateoperations.h b/src/libs/kdtools/kdupdaterupdateoperations.h
new file mode 100644
index 000000000..40480d1e1
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdateoperations.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_OPERATIONS_H
+#define KD_UPDATER_UPDATE_OPERATIONS_H
+
+#include "kdupdaterupdateoperation.h"
+
+#include <QDir>
+#include <QObject>
+#include <QProcess>
+
+namespace KDUpdater {
+
+class KDTOOLS_EXPORT CopyOperation : public UpdateOperation
+{
+public:
+ CopyOperation();
+ ~CopyOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ CopyOperation *clone() const;
+
+ QDomDocument toXml() const;
+};
+
+class KDTOOLS_EXPORT MoveOperation : public UpdateOperation
+{
+public:
+ MoveOperation();
+ ~MoveOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ MoveOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT DeleteOperation : public UpdateOperation
+{
+public:
+ DeleteOperation();
+ ~DeleteOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ DeleteOperation *clone() const;
+
+ QDomDocument toXml() const;
+};
+
+class KDTOOLS_EXPORT MkdirOperation : public UpdateOperation
+{
+public:
+ MkdirOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ MkdirOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT RmdirOperation : public UpdateOperation
+{
+public:
+ RmdirOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ RmdirOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation
+{
+public:
+ AppendFileOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ AppendFileOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation
+{
+public:
+ PrependFileOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ PrependFileOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT ExecuteOperation : public QObject, public UpdateOperation
+{
+ Q_OBJECT
+
+public:
+ ExecuteOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ ExecuteOperation *clone() const;
+
+public Q_SLOTS:
+ void cancelOperation();
+
+private Q_SLOTS:
+ void readProcessOutput();
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &text);
+
+private:
+ QProcess process;
+};
+
+class KDTOOLS_EXPORT UpdatePackageOperation : public UpdateOperation
+{
+public:
+ UpdatePackageOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ UpdatePackageOperation *clone() const;
+};
+
+class KDTOOLS_EXPORT UpdateCompatOperation : public UpdateOperation
+{
+public:
+ UpdateCompatOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ UpdateCompatOperation *clone() const;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_OPERATIONS_H
diff --git a/src/libs/kdtools/kdupdaterupdatesinfo.cpp b/src/libs/kdtools/kdupdaterupdatesinfo.cpp
new file mode 100644
index 000000000..420876056
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatesinfo.cpp
@@ -0,0 +1,352 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdatesinfo_p.h"
+
+#include <QCoreApplication>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QFile>
+#include <QSharedData>
+
+using namespace KDUpdater;
+
+//
+// UpdatesInfo::UpdatesInfoData
+//
+struct UpdatesInfo::UpdatesInfoData : public QSharedData
+{
+ Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData)
+
+public:
+ UpdatesInfoData() : error(UpdatesInfo::NotYetReadError), compatLevel(-1) { }
+
+ QString errorMessage;
+ UpdatesInfo::Error error;
+ QString updateXmlFile;
+ QString applicationName;
+ QString applicationVersion;
+ int compatLevel;
+ QList<UpdateInfo> updateInfoList;
+
+ void parseFile(const QString &updateXmlFile);
+ bool parsePackageUpdateElement(const QDomElement &updateE);
+ bool parseCompatUpdateElement(const QDomElement &updateE);
+
+ void setInvalidContentError(const QString &detail);
+};
+
+void UpdatesInfo::UpdatesInfoData::setInvalidContentError(const QString &detail)
+{
+ error = UpdatesInfo::InvalidContentError;
+ errorMessage = tr("Updates.Xml contains invalid content: %1").arg(detail);
+}
+
+void UpdatesInfo::UpdatesInfoData::parseFile(const QString &updateXmlFile)
+{
+ QFile file(updateXmlFile);
+ if (!file.open(QFile::ReadOnly)) {
+ error = UpdatesInfo::CouldNotReadUpdateInfoFileError;
+ errorMessage = tr("Could not read \"%1\"").arg(updateXmlFile);
+ return;
+ }
+
+ QDomDocument doc;
+ QString parseErrorMessage;
+ int parseErrorLine;
+ int parseErrorColumn;
+ if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
+ error = UpdatesInfo::InvalidXmlError;
+ errorMessage = tr("Parse error in %1 at %2, %3: %4")
+ .arg(updateXmlFile,
+ QString::number(parseErrorLine),
+ QString::number(parseErrorColumn),
+ parseErrorMessage);
+ return;
+ }
+
+ QDomElement rootE = doc.documentElement();
+ if (rootE.tagName() != QLatin1String("Updates")) {
+ setInvalidContentError(tr("root element %1 unexpected, should be \"Updates\"").arg(rootE.tagName()));
+ return;
+ }
+
+ QDomNodeList childNodes = rootE.childNodes();
+ for(int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.at(i);
+ QDomElement childE = childNode.toElement();
+ if (childE.isNull())
+ continue;
+
+ if (childE.tagName() == QLatin1String("ApplicationName"))
+ applicationName = childE.text();
+ else if (childE.tagName() == QLatin1String("ApplicationVersion"))
+ applicationVersion = childE.text();
+ else if (childE.tagName() == QLatin1String("RequiredCompatLevel"))
+ compatLevel = childE.text().toInt();
+ else if (childE.tagName() == QLatin1String("PackageUpdate")) {
+ const bool res = parsePackageUpdateElement(childE);
+ if (!res) {
+ //error handled in subroutine
+ return;
+ }
+ } else if (childE.tagName() == QLatin1String("CompatUpdate")) {
+ const bool res = parseCompatUpdateElement(childE);
+ if (!res) {
+ //error handled in subroutine
+ return;
+ }
+ }
+ }
+
+ if (applicationName.isEmpty()) {
+ setInvalidContentError(tr("ApplicationName element is missing"));
+ return;
+ }
+
+ if (applicationVersion.isEmpty()) {
+ setInvalidContentError(tr("ApplicationVersion element is missing"));
+ return;
+ }
+
+ error = UpdatesInfo::NoError;
+ errorMessage.clear();
+}
+
+bool UpdatesInfo::UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
+{
+ if (updateE.isNull())
+ return false;
+
+ UpdateInfo info;
+ info.type = PackageUpdate;
+
+ QDomNodeList childNodes = updateE.childNodes();
+ for (int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.at(i);
+ QDomElement childE = childNode.toElement();
+ if (childE.isNull())
+ continue;
+
+ if (childE.tagName() == QLatin1String("ReleaseNotes")) {
+ info.data[childE.tagName()] = QUrl(childE.text());
+ } else if (childE.tagName() == QLatin1String("UpdateFile")) {
+ UpdateFileInfo ufInfo;
+ ufInfo.arch = childE.attribute(QLatin1String("Arch"), QLatin1String("i386"));
+ ufInfo.os = childE.attribute(QLatin1String("OS"));
+ ufInfo.compressedSize = childE.attribute(QLatin1String("CompressedSize")).toLongLong();
+ ufInfo.uncompressedSize = childE.attribute(QLatin1String("UncompressedSize")).toLongLong();
+ ufInfo.sha1sum = QByteArray::fromHex(childE.attribute(QLatin1String("sha1sum")).toAscii());
+ ufInfo.fileName = childE.text();
+ info.updateFiles.append(ufInfo);
+ } else if (childE.tagName() == QLatin1String("Licenses")) {
+ QHash<QString, QVariant> licenseHash;
+ const QDomNodeList licenseNodes = childE.childNodes();
+ for (int i = 0; i < licenseNodes.count(); ++i) {
+ const QDomNode licenseNode = licenseNodes.at(i);
+ if (licenseNode.nodeName() == QLatin1String("License")) {
+ QDomElement element = licenseNode.toElement();
+ licenseHash.insert(element.attributeNode(QLatin1String("name")).value(),
+ element.attributeNode(QLatin1String("file")).value());
+ }
+ }
+ if (!licenseHash.isEmpty())
+ info.data.insert(QLatin1String("Licenses"), licenseHash);
+ } else if (childE.tagName() == QLatin1String("Version")) {
+ info.data.insert(QLatin1String("inheritVersionFrom"), childE.attribute(QLatin1String("inheritVersionFrom")));
+ info.data[childE.tagName()] = childE.text();
+ } else {
+ info.data[childE.tagName()] = childE.text();
+ }
+ }
+
+ if (!info.data.contains(QLatin1String("Name"))) {
+ setInvalidContentError(tr("PackageUpdate element without Name"));
+ return false;
+ }
+ if (!info.data.contains(QLatin1String("Version"))) {
+ setInvalidContentError(tr("PackageUpdate element without Version"));
+ return false;
+ }
+ if (!info.data.contains(QLatin1String("ReleaseDate"))) {
+ setInvalidContentError(tr("PackageUpdate element without ReleaseDate"));
+ return false;
+ }
+ if (info.updateFiles.isEmpty()) {
+ setInvalidContentError(tr("PackageUpdate element without UpdateFile"));
+ return false;
+ }
+
+ updateInfoList.append(info);
+ return true;
+}
+
+bool UpdatesInfo::UpdatesInfoData::parseCompatUpdateElement(const QDomElement &updateE)
+{
+ if (updateE.isNull())
+ return false;
+
+ UpdateInfo info;
+ info.type = CompatUpdate;
+
+ QDomNodeList childNodes = updateE.childNodes();
+ for (int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.at(i);
+ QDomElement childE = childNode.toElement();
+ if (childE.isNull())
+ continue;
+
+ if (childE.tagName() == QLatin1String("ReleaseNotes")) {
+ info.data[childE.tagName()] = QUrl(childE.text());
+ } else if (childE.tagName() == QLatin1String("UpdateFile")) {
+ UpdateFileInfo ufInfo;
+ ufInfo.arch = childE.attribute(QLatin1String("Arch"), QLatin1String("i386"));
+ ufInfo.os = childE.attribute(QLatin1String("OS"));
+ ufInfo.fileName = childE.text();
+ info.updateFiles.append(ufInfo);
+ } else {
+ info.data[childE.tagName()] = childE.text();
+ }
+ }
+
+ if (!info.data.contains(QLatin1String("CompatLevel"))) {
+ setInvalidContentError(tr("CompatUpdate element without CompatLevel"));
+ return false;
+ }
+
+ if (!info.data.contains(QLatin1String("ReleaseDate"))) {
+ setInvalidContentError(tr("CompatUpdate element without ReleaseDate"));
+ return false;
+ }
+
+ if (info.updateFiles.isEmpty()) {
+ setInvalidContentError(tr("CompatUpdate element without UpdateFile"));
+ return false;
+ }
+
+ updateInfoList.append(info);
+ return true;
+}
+
+
+//
+// UpdatesInfo
+//
+UpdatesInfo::UpdatesInfo()
+ : d(new UpdatesInfo::UpdatesInfoData)
+{
+}
+
+UpdatesInfo::~UpdatesInfo()
+{
+}
+
+bool UpdatesInfo::isValid() const
+{
+ return d->error == NoError;
+}
+
+QString UpdatesInfo::errorString() const
+{
+ return d->errorMessage;
+}
+
+void UpdatesInfo::setFileName(const QString &updateXmlFile)
+{
+ if (d->updateXmlFile == updateXmlFile)
+ return;
+
+ d->applicationName.clear();
+ d->applicationVersion.clear();
+ d->updateInfoList.clear();
+
+ d->updateXmlFile = updateXmlFile;
+ d->parseFile(d->updateXmlFile);
+}
+
+QString UpdatesInfo::fileName() const
+{
+ return d->updateXmlFile;
+}
+
+QString UpdatesInfo::applicationName() const
+{
+ return d->applicationName;
+}
+
+QString UpdatesInfo::applicationVersion() const
+{
+ return d->applicationVersion;
+}
+
+int UpdatesInfo::compatLevel() const
+{
+ return d->compatLevel;
+}
+
+int UpdatesInfo::updateInfoCount(int type) const
+{
+ if (type == AllUpdate)
+ return d->updateInfoList.count();
+
+ int count = 0;
+ for (int i = 0; i < d->updateInfoList.count(); ++i) {
+ if (d->updateInfoList.at(i).type == type)
+ ++count;
+ }
+ return count;
+}
+
+UpdateInfo UpdatesInfo::updateInfo(int index) const
+{
+ if (index < 0 || index >= d->updateInfoList.count())
+ return UpdateInfo();
+
+ return d->updateInfoList.at(index);
+}
+
+QList<UpdateInfo> UpdatesInfo::updatesInfo(int type, int compatLevel) const
+{
+ QList<UpdateInfo> list;
+ if (compatLevel == -1) {
+ if (type == AllUpdate)
+ return d->updateInfoList;
+ for (int i = 0; i < d->updateInfoList.count(); ++i) {
+ if (d->updateInfoList.at(i).type == type)
+ list.append(d->updateInfoList.at(i));
+ }
+ } else {
+ for (int i = 0; i < d->updateInfoList.count(); ++i) {
+ UpdateInfo updateInfo = d->updateInfoList.at(i);
+ if (updateInfo.type == type) {
+ if (updateInfo.type == CompatUpdate) {
+ if (updateInfo.data.value(QLatin1String("CompatLevel")) == compatLevel)
+ list.append(updateInfo);
+ } else {
+ if (updateInfo.data.value(QLatin1String("RequiredCompatLevel")) == compatLevel)
+ list.append(updateInfo);
+ }
+ }
+ }
+ }
+ return list;
+}
diff --git a/src/libs/kdtools/kdupdaterupdatesinfo_p.h b/src/libs/kdtools/kdupdaterupdatesinfo_p.h
new file mode 100644
index 000000000..7c4bc5b3f
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatesinfo_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_INFO_H
+#define KD_UPDATER_UPDATE_INFO_H
+
+#include "kdupdater.h"
+
+#include <QSharedDataPointer>
+#include <QString>
+#include <QDate>
+#include <QList>
+#include <QStringList>
+#include <QUrl>
+#include <QMap>
+#include <QVariant>
+
+// Classes and structures in this header file are for internal use only.
+// They are not a part of the public API
+
+namespace KDUpdater {
+
+struct UpdateFileInfo
+{
+ UpdateFileInfo()
+ : compressedSize(0),
+ uncompressedSize(0)
+ {}
+
+ QString arch;
+ QString os;
+ QString fileName;
+ QByteArray sha1sum;
+ quint64 compressedSize;
+ quint64 uncompressedSize;
+};
+
+struct UpdateInfo
+{
+ int type;
+ QMap<QString, QVariant> data;
+ QList<UpdateFileInfo> updateFiles;
+};
+
+class UpdatesInfo
+{
+public:
+ enum Error
+ {
+ NoError = 0,
+ NotYetReadError,
+ CouldNotReadUpdateInfoFileError,
+ InvalidXmlError,
+ InvalidContentError
+ };
+
+ UpdatesInfo();
+ ~UpdatesInfo();
+
+ bool isValid() const;
+ QString errorString() const;
+ Error error() const;
+
+ void setFileName(const QString &updateXmlFile);
+ QString fileName() const;
+
+ QString applicationName() const;
+ QString applicationVersion() const;
+ int compatLevel() const;
+
+ int updateInfoCount(int type = AllUpdate) const;
+ UpdateInfo updateInfo(int index) const;
+ QList<UpdateInfo> updatesInfo(int type = AllUpdate, int compatLevel = -1) const;
+
+private:
+ struct UpdatesInfoData;
+ QSharedDataPointer<UpdatesInfoData> d;
+};
+
+} // namespace KDUpdater
+
+#endif // KD_UPDATER_UPDATE_INFO_H
diff --git a/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp b/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp
new file mode 100644
index 000000000..e4fbae43c
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp
@@ -0,0 +1,500 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdatesourcesinfo.h"
+#include "kdupdaterapplication.h"
+
+#include <QDomElement>
+#include <QDomDocument>
+#include <QDomText>
+#include <QDomCDATASection>
+#include <QFileInfo>
+#include <QFile>
+#include <QTextStream>
+
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::UpdateSourcesInfo kdupdaterupdatesourcesinfo.h KDUpdaterUpdateSourcesInfo
+ \brief Provides access to information about the update sources set for the application.
+
+ An update source is a repository that contains updates applicable for the application.
+ Applications can download updates from the update source and install them locally.
+
+ Each application can have one or more update sources from which it can download updates.
+ Information about update source is stored in a file called UpdateSources.xml. This class helps
+ access and modify the UpdateSources.xml file.
+
+ The complete file name of the UpdateSources.xml file can be specified via the \ref setFileName()
+ method. The class then parses the XML file and makes available information contained in
+ that XML file through an easy to use API. You can
+
+ \li Get update sources information via the \ref updateSourceInfoCount() and \ref updateSourceInfo()
+ methods.
+ \li You can add/remove/change update source information via the \ref addUpdateSourceInfo(),
+ \ref removeUpdateSource(), \ref setUpdateSourceAt() methods.
+
+ The class emits appropriate signals to inform listeners about changes in the update application.
+*/
+
+/*! \enum UpdateSourcesInfo::Error
+ * Error codes related to retrieving update sources
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::NoError
+ * No error occurred
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::NotYetReadError
+ * The package information was not parsed yet from the XML file
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::CouldNotReadSourceFileError
+ * the specified update source file could not be read (does not exist or not readable)
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::InvalidXmlError
+ * The source file contains invalid XML.
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::InvalidContentError
+ * The source file contains valid XML, but does not match the expected format for source descriptions
+ */
+
+/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::CouldNotSaveChangesError
+ * Changes made to the object could be saved back to the source file
+ */
+
+using namespace KDUpdater;
+
+struct UpdateSourceInfoPriorityHigherThan
+{
+ bool operator()(const UpdateSourceInfo &lhs, const UpdateSourceInfo &rhs) const
+ {
+ return lhs.priority > rhs.priority;
+ }
+};
+
+
+struct UpdateSourcesInfo::UpdateSourcesInfoData
+{
+ UpdateSourcesInfoData(UpdateSourcesInfo *qq) :
+ q(qq),
+ error(UpdateSourcesInfo::NotYetReadError),
+ application(0),
+ modified(false)
+ {}
+
+ UpdateSourcesInfo *q;
+
+ QString errorMessage;
+ UpdateSourcesInfo::Error error;
+ Application *application;
+ bool modified;
+ QString fileName;
+ QList<UpdateSourceInfo> updateSourceInfoList;
+
+ void addUpdateSourceFrom(const QDomElement &element);
+ void addChildElement(QDomDocument &doc, QDomElement &parentE, const QString &tagName, const QString &text, bool htmlText = false);
+ void setInvalidContentError(const QString &detail);
+ void clearError();
+ void saveChanges();
+};
+
+void UpdateSourcesInfo::UpdateSourcesInfoData::setInvalidContentError(const QString &detail)
+{
+ error = UpdateSourcesInfo::InvalidContentError;
+ errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail);
+}
+
+void UpdateSourcesInfo::UpdateSourcesInfoData::clearError()
+{
+ error = UpdateSourcesInfo::NoError;
+ errorMessage.clear();
+}
+
+/*!
+ \internal
+*/
+UpdateSourcesInfo::UpdateSourcesInfo(Application *application)
+ : QObject(application),
+ d(new UpdateSourcesInfo::UpdateSourcesInfoData(this))
+{
+ d->application = application;
+}
+
+/*!
+ \internal
+*/
+UpdateSourcesInfo::~UpdateSourcesInfo()
+{
+ d->saveChanges();
+ delete d;
+}
+
+/*!
+ Returns a pointer to the update application for which this class manages update sources.
+*/
+Application *UpdateSourcesInfo::application() const
+{
+ return d->application;
+}
+
+/*!
+ \internal
+*/
+bool UpdateSourcesInfo::isValid() const
+{
+ return d->error == NoError;
+}
+
+/*!
+ returns a human-readable description of the error
+ */
+QString UpdateSourcesInfo::errorString() const
+{
+ return d->errorMessage;
+}
+
+/*!
+ returns the last error
+ */
+UpdateSourcesInfo::Error UpdateSourcesInfo::error() const
+{
+ return d->error;
+}
+
+bool UpdateSourcesInfo::isModified() const
+{
+ return d->modified;
+}
+
+void UpdateSourcesInfo::setModified(bool modified)
+{
+ d->modified = modified;
+}
+
+/*!
+ Sets the complete file name of the UpdateSources.xml file. The function also issues a call
+ to refresh() to reload package information from the XML file.
+
+ \sa KDUpdater::Application::setUpdateSourcesXMLFileName()
+*/
+void UpdateSourcesInfo::setFileName(const QString &fileName)
+{
+ if (d->fileName == fileName)
+ return;
+
+ d->fileName = fileName;
+ refresh(); // load new file
+}
+
+/*!
+ Returns the name of the UpdateSources.xml file that this class referred to.
+*/
+QString UpdateSourcesInfo::fileName() const
+{
+ return d->fileName;
+}
+
+/*!
+ Returns the number of update source info structures contained in this class.
+*/
+int UpdateSourcesInfo::updateSourceInfoCount() const
+{
+ return d->updateSourceInfoList.count();
+}
+
+/*!
+ Returns the update source info structure at \c index. If an invalid index is passed
+ the function returns a dummy constructor.
+*/
+UpdateSourceInfo UpdateSourcesInfo::updateSourceInfo(int index) const
+{
+ if (index < 0 || index >= d->updateSourceInfoList.count())
+ return UpdateSourceInfo();
+
+ return d->updateSourceInfoList[index];
+}
+
+/*!
+ Adds an update source info to this class. Upon successful addition, the class emits a
+ \ref updateSourceInfoAdded() signal.
+*/
+void UpdateSourcesInfo::addUpdateSourceInfo(const UpdateSourceInfo &info)
+{
+ if (d->updateSourceInfoList.contains(info))
+ return;
+ d->updateSourceInfoList.push_back(info);
+ qSort(d->updateSourceInfoList.begin(), d->updateSourceInfoList.end(), UpdateSourceInfoPriorityHigherThan());
+ emit updateSourceInfoAdded(info);
+ d->modified = true;
+}
+
+/*!
+ Removes an update source info from this class. Upon successful removal, the class emits a
+ \ref updateSourceInfoRemoved() signal.
+*/
+void UpdateSourcesInfo::removeUpdateSourceInfo(const UpdateSourceInfo &info)
+{
+ if (!d->updateSourceInfoList.contains(info))
+ return;
+ d->updateSourceInfoList.removeAll(info);
+ emit updateSourceInfoRemoved(info);
+ d->modified = true;
+}
+
+/*!
+ Removes an update source info at \index in this class. Upon successful removal, the class emits a
+ \ref updateSourceInfoRemoved() signal.
+*/
+void UpdateSourcesInfo::removeUpdateSourceInfoAt(int index)
+{
+ if (index < 0 || index >= d->updateSourceInfoList.count())
+ return;
+ UpdateSourceInfo info = d->updateSourceInfoList[index];
+ d->updateSourceInfoList.removeAt(index);
+ emit updateSourceInfoRemoved(info);
+ d->modified = true;
+}
+
+/*!
+ Changes the update source info at \c index to \c info. If \c index is equal to the number of
+ source info structures in this class (\ref updateSourceInfoCount()) then \c info is appended;
+ otherwise the existing info at \c index will be changed.
+
+ Depending on what the function does \ref updateSourceInfoAdded() or \ref updateSourceInfoChanged()
+ signal is emitted.
+*/
+void UpdateSourcesInfo::setUpdateSourceInfoAt(int index, const UpdateSourceInfo &info)
+{
+ if (index < 0 || index > d->updateSourceInfoList.count())
+ return;
+
+ if (index == d->updateSourceInfoList.count()) {
+ d->updateSourceInfoList.append(info);
+ emit updateSourceInfoAdded(info);
+ } else {
+ UpdateSourceInfo oldInfo = d->updateSourceInfoList[index];
+ if (info == oldInfo)
+ return;
+
+ d->updateSourceInfoList[index] = info;
+ emit updateSourceInfoChanged(info, oldInfo);
+ }
+ d->modified = true;
+}
+
+/*!
+ This slot reloads the update source information from UpdateSources.xml.
+*/
+void UpdateSourcesInfo::refresh()
+{
+ d->saveChanges(); // save changes done in the previous file
+ d->updateSourceInfoList.clear();
+
+ QFile file(d->fileName);
+
+ // if the file does not exist then we just skip the reading
+ if (!file.exists()) {
+ d->clearError();
+ emit reset();
+ return;
+ }
+
+ // Open the XML file
+ if (!file.open(QFile::ReadOnly)) {
+ d->errorMessage = tr("Could not read \"%1\"").arg(d->fileName);
+ d->error = CouldNotReadSourceFileError;
+ emit reset();
+ return;
+ }
+
+ QDomDocument doc;
+ QString parseErrorMessage;
+ int parseErrorLine;
+ int parseErrorColumn;
+ if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
+ d->error = InvalidXmlError;
+ d->errorMessage = tr("XML Parse error in %1 at %2, %3: %4")
+ .arg(d->fileName,
+ QString::number(parseErrorLine),
+ QString::number(parseErrorColumn),
+ parseErrorMessage);
+ emit reset();
+ return;
+ }
+
+ // Now parse the XML file.
+ QDomElement rootE = doc.documentElement();
+ if (rootE.tagName() != QLatin1String("UpdateSources")) {
+ d->setInvalidContentError(tr("Root element %1 unexpected, should be \"UpdateSources\"").arg(rootE.tagName()));
+ emit reset();
+ return;
+ }
+
+ QDomNodeList childNodes = rootE.childNodes();
+ for (int i = 0; i < childNodes.count(); i++) {
+ QDomNode childNode = childNodes.item(i);
+ QDomElement childNodeE = childNode.toElement();
+ if (childNodeE.isNull())
+ continue;
+
+ if (childNodeE.tagName() == QLatin1String("UpdateSource"))
+ d->addUpdateSourceFrom(childNodeE);
+ }
+
+ d->clearError();
+ emit reset();
+}
+
+void UpdateSourcesInfo::UpdateSourcesInfoData::saveChanges()
+{
+ if (!modified || fileName.isEmpty())
+ return;
+
+ const bool hadSaveError = error == UpdateSourcesInfo::CouldNotSaveChangesError;
+
+ QDomDocument doc;
+
+ QDomElement rootE = doc.createElement(QLatin1String("UpdateSources"));
+ doc.appendChild(rootE);
+
+ for (int i = 0; i < updateSourceInfoList.count(); i++) {
+ UpdateSourceInfo info = updateSourceInfoList.at(i);
+
+ QDomElement infoE = doc.createElement(QLatin1String("UpdateSource"));
+ rootE.appendChild(infoE);
+ addChildElement(doc, infoE, QLatin1String("Name"), info.name);
+ addChildElement(doc, infoE, QLatin1String("Title"), info.title);
+ addChildElement(doc, infoE, QLatin1String("Description"), info.description,
+ (info.description.length() && info.description.at(0) == QLatin1Char('<')));
+ addChildElement(doc, infoE, QLatin1String("Url"), info.url.toString());
+ }
+
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly)) {
+ error = UpdateSourcesInfo::CouldNotSaveChangesError;
+ errorMessage = tr("Could not save changes to \"%1\": %2").arg(fileName, file.errorString());
+ return;
+ }
+
+ QTextStream stream(&file);
+ doc.save(stream, 2);
+ stream.flush();
+ file.close();
+
+ if (file.error() != QFile::NoError) {
+ error = UpdateSourcesInfo::CouldNotSaveChangesError;
+ errorMessage = tr("Could not save changes to \"%1\": %2").arg(fileName, file.errorString());
+ return;
+ }
+
+ //if there was a write error before, clear the error, as the write was successful now
+ if (hadSaveError)
+ clearError();
+
+ modified = false;
+}
+
+void UpdateSourcesInfo::UpdateSourcesInfoData::addUpdateSourceFrom(const QDomElement &element)
+{
+ if (element.tagName() != QLatin1String("UpdateSource"))
+ return;
+
+ QDomNodeList childNodes = element.childNodes();
+ if (!childNodes.count())
+ return;
+
+ UpdateSourceInfo info;
+
+ for (int i = 0; i<childNodes.count(); i++) {
+ QDomNode childNode = childNodes.item(i);
+ QDomElement childNodeE = childNode.toElement();
+ if (childNodeE.isNull())
+ continue;
+
+ if (childNodeE.tagName() == QLatin1String("Name"))
+ info.name = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Title"))
+ info.title = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Description"))
+ info.description = childNodeE.text();
+ else if (childNodeE.tagName() == QLatin1String("Url"))
+ info.url = childNodeE.text();
+ }
+
+ this->updateSourceInfoList.append(info);
+}
+
+void UpdateSourcesInfo::UpdateSourcesInfoData::addChildElement(QDomDocument &doc, QDomElement &parentE, const QString &tagName, const QString &text, bool htmlText)
+{
+ QDomElement childE = doc.createElement(tagName);
+ parentE.appendChild(childE);
+
+ if (htmlText) {
+ QDomCDATASection textE = doc.createCDATASection(text);
+ childE.appendChild(textE);
+ } else {
+ QDomText textE = doc.createTextNode(text);
+ childE.appendChild(textE);
+ }
+}
+
+/*!
+ \ingroup kdupdater
+ \struct KDUpdater::UpdateSourceInfo kdupdaterupdatesourcesinfo.h KDUpdaterUpdateSourcesInfo
+ \brief Describes a single update source
+
+ An update source is a repository that contains updates applicable for the application.
+ This structure describes a single update source in terms of name, title, description, url and priority.
+*/
+
+/*!
+ \var QString KDUpdater::UpdateSourceInfo::name
+*/
+
+/*!
+ \var QString KDUpdater::UpdateSourceInfo::title
+*/
+
+/*!
+ \var QString KDUpdater::UpdateSourceInfo::description
+*/
+
+/*!
+ \var QUrl KDUpdater::UpdateSourceInfo::url
+*/
+
+/*!
+ \var QUrl KDUpdater::UpdateSourceInfo::priority
+*/
+
+namespace KDUpdater {
+
+bool operator==(const UpdateSourceInfo &lhs, const UpdateSourceInfo &rhs)
+{
+ return lhs.name == rhs.name && lhs.title == rhs.title
+ && lhs.description == rhs.description && lhs.url == rhs.url;
+}
+
+} // namespace KDUpdater
diff --git a/src/libs/kdtools/kdupdaterupdatesourcesinfo.h b/src/libs/kdtools/kdupdaterupdatesourcesinfo.h
new file mode 100644
index 000000000..2afeda966
--- /dev/null
+++ b/src/libs/kdtools/kdupdaterupdatesourcesinfo.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#ifndef KD_UPDATER_UPDATE_SOURCES_INFO_H
+#define KD_UPDATER_UPDATE_SOURCES_INFO_H
+
+#include "kdupdater.h"
+
+#include <QObject>
+#include <QVariant>
+#include <QUrl>
+
+namespace KDUpdater {
+
+class Application;
+
+struct KDTOOLS_EXPORT UpdateSourceInfo
+{
+ UpdateSourceInfo() : priority(-1) { }
+
+ QString name;
+ QString title;
+ QString description;
+ QUrl url;
+ int priority;
+};
+
+KDTOOLS_EXPORT bool operator==(const UpdateSourceInfo &lhs, const UpdateSourceInfo &rhs);
+
+inline bool operator!=(const UpdateSourceInfo &lhs, const UpdateSourceInfo &rhs)
+{
+ return !operator==(lhs, rhs);
+}
+
+class KDTOOLS_EXPORT UpdateSourcesInfo : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~UpdateSourcesInfo();
+
+ enum Error
+ {
+ NoError = 0,
+ NotYetReadError,
+ CouldNotReadSourceFileError,
+ InvalidXmlError,
+ InvalidContentError,
+ CouldNotSaveChangesError
+ };
+
+ Application *application() const;
+
+ bool isValid() const;
+ QString errorString() const;
+ Error error() const;
+
+ bool isModified() const;
+ void setModified(bool modified);
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ int updateSourceInfoCount() const;
+ UpdateSourceInfo updateSourceInfo(int index) const;
+
+ void addUpdateSourceInfo(const UpdateSourceInfo &info);
+ void removeUpdateSourceInfo(const UpdateSourceInfo &info);
+ void removeUpdateSourceInfoAt(int index);
+ void setUpdateSourceInfoAt(int index, const UpdateSourceInfo &info);
+
+protected:
+ explicit UpdateSourcesInfo(Application *application);
+
+public Q_SLOTS:
+ void refresh();
+
+Q_SIGNALS:
+ void reset();
+ void updateSourceInfoAdded(const UpdateSourceInfo &info);
+ void updateSourceInfoRemoved(const UpdateSourceInfo &info);
+ void updateSourceInfoChanged(const UpdateSourceInfo &newInfo,
+ const UpdateSourceInfo &oldInfo);
+
+private:
+ friend class Application;
+ struct UpdateSourcesInfoData;
+ UpdateSourcesInfoData *d;
+};
+
+} // namespace KDUpdater
+
+Q_DECLARE_METATYPE(KDUpdater::UpdateSourceInfo)
+
+#endif // KD_UPDATER_UPDATE_SOURCES_INFO_H