summaryrefslogtreecommitdiffstats
path: root/kpilot/lib
diff options
context:
space:
mode:
Diffstat (limited to 'kpilot/lib')
-rw-r--r--kpilot/lib/CMakeLists.txt90
-rw-r--r--kpilot/lib/COPYING509
-rw-r--r--kpilot/lib/Makefile.am60
-rw-r--r--kpilot/lib/actionQueue.cc172
-rw-r--r--kpilot/lib/actionQueue.h162
-rw-r--r--kpilot/lib/actions.cc137
-rw-r--r--kpilot/lib/actions.h115
-rw-r--r--kpilot/lib/idmapper.cc247
-rw-r--r--kpilot/lib/idmapper.h159
-rw-r--r--kpilot/lib/idmapperxml.cc213
-rw-r--r--kpilot/lib/idmapperxml.h84
-rw-r--r--kpilot/lib/idmapping.cc89
-rw-r--r--kpilot/lib/idmapping.h66
-rw-r--r--kpilot/lib/kpilotdevicelink.cc966
-rw-r--r--kpilot/lib/kpilotdevicelink.h220
-rw-r--r--kpilot/lib/kpilotdevicelinkPrivate.h330
-rw-r--r--kpilot/lib/kpilotlib.kcfg9
-rw-r--r--kpilot/lib/kpilotlibSettings.kcfgc7
-rw-r--r--kpilot/lib/kpilotlink.cc272
-rw-r--r--kpilot/lib/kpilotlink.h501
-rw-r--r--kpilot/lib/kpilotlocallink.cc368
-rw-r--r--kpilot/lib/kpilotlocallink.h95
-rw-r--r--kpilot/lib/options.cc157
-rw-r--r--kpilot/lib/options.h199
-rw-r--r--kpilot/lib/pilot.cc264
-rw-r--r--kpilot/lib/pilot.h410
-rw-r--r--kpilot/lib/pilotAddress.cc636
-rw-r--r--kpilot/lib/pilotAddress.h339
-rw-r--r--kpilot/lib/pilotAppInfo.cc77
-rw-r--r--kpilot/lib/pilotAppInfo.h216
-rw-r--r--kpilot/lib/pilotCard.h65
-rw-r--r--kpilot/lib/pilotDatabase.cc112
-rw-r--r--kpilot/lib/pilotDatabase.h272
-rw-r--r--kpilot/lib/pilotDateEntry.cc478
-rw-r--r--kpilot/lib/pilotDateEntry.h388
-rw-r--r--kpilot/lib/pilotLinkVersion.h60
-rw-r--r--kpilot/lib/pilotLocalDatabase.cc762
-rw-r--r--kpilot/lib/pilotLocalDatabase.h201
-rw-r--r--kpilot/lib/pilotMemo.cc135
-rw-r--r--kpilot/lib/pilotMemo.h105
-rw-r--r--kpilot/lib/pilotRecord.cc132
-rw-r--r--kpilot/lib/pilotRecord.h355
-rw-r--r--kpilot/lib/pilotSerialDatabase.cc432
-rw-r--r--kpilot/lib/pilotSerialDatabase.h144
-rw-r--r--kpilot/lib/pilotSysInfo.h144
-rw-r--r--kpilot/lib/pilotTodoEntry.cc270
-rw-r--r--kpilot/lib/pilotTodoEntry.h166
-rw-r--r--kpilot/lib/pilotUser.h128
-rw-r--r--kpilot/lib/plugin.cc760
-rw-r--r--kpilot/lib/plugin.h476
-rw-r--r--kpilot/lib/pluginfactory.h98
-rw-r--r--kpilot/lib/recordConduit.cc1145
-rw-r--r--kpilot/lib/recordConduit.h181
-rw-r--r--kpilot/lib/syncAction.cc512
-rw-r--r--kpilot/lib/syncAction.h410
55 files changed, 15100 insertions, 0 deletions
diff --git a/kpilot/lib/CMakeLists.txt b/kpilot/lib/CMakeLists.txt
new file mode 100644
index 000000000..efc36ce59
--- /dev/null
+++ b/kpilot/lib/CMakeLists.txt
@@ -0,0 +1,90 @@
+include(CheckIncludeFiles)
+include(CheckFunctionExists)
+
+check_include_files( stdint.h HAVE_STDINT_H )
+check_include_files( alloca.h HAVE_ALLOCA_H )
+check_include_files( "sys/time.h" HAVE_SYS_TIME_H )
+check_include_files( "sys/stat.h" HAVE_SYS_STAT_H )
+check_function_exists( cfsetspeed HAVE_CFSETSPEED )
+check_function_exists( strdup HAVE_STRDUP )
+check_function_exists( setenv HAVE_SETENV )
+check_function_exists( unsetenv HAVE_UNSETENV )
+check_function_exists( usleep HAVE_USLEEP )
+check_function_exists( random HAVE_RANDOM )
+check_function_exists( putenv HAVE_PUTENV )
+check_function_exists( seteuid HAVE_SETEUID )
+check_function_exists( mkstemps HAVE_MKSTEMPS )
+check_function_exists( mkstemp HAVE_MKSTEMP )
+check_function_exists( mkdtemp HAVE_MKDTEMP )
+check_function_exists( revoke HAVE_REVOKE )
+check_function_exists( strlcpy HAVE_STRLCPY )
+check_function_exists( strlcat HAVE_STRLCAT )
+check_function_exists( inet_aton HAVE_INET_ATON )
+
+configure_file(
+ ${CMAKE_SOURCE_DIR}/config.h.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/config.h
+)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(lib_SRCS
+ options.cc
+ plugin.cc
+ syncAction.cc
+ actions.cc
+ actionQueue.cc
+ idmapping.cc
+ idmapperxml.cc
+ idmapper.cc
+ kpilotlink.cc
+ kpilotdevicelink.cc
+ kpilotlocallink.cc
+ pilot.cc
+ pilotAppInfo.cc
+ pilotRecord.cc
+ pilotDatabase.cc
+ pilotLocalDatabase.cc
+ pilotSerialDatabase.cc
+ pilotMemo.cc
+ pilotAddress.cc
+ pilotDateEntry.cc
+ pilotTodoEntry.cc
+)
+
+kde3_automoc(${lib_SRCS})
+kde3_add_kcfg_files(lib_SRCS kpilotlibSettings.kcfgc)
+add_library(kpilot SHARED ${lib_SRCS})
+target_link_libraries(kpilot ${PILOTLINK_LIBRARY} ${QT_LIBRARIES} kdeui kio)
+kpilot_rpath(kpilot)
+
+#---------- INSTALL -----------------------*
+set(kpilotinclude_HEADERS
+ kpilotlink.h
+ kpilotdevicelink.h
+ kpilotlocallink.h
+ pilot.h
+ pilotDatabase.h
+ pilotLinkVersion.h
+ pilotLocalDatabase.h
+ pilotRecord.h
+ pilotSerialDatabase.h
+ plugin.h
+ pluginfactory.h
+ syncAction.h
+)
+
+install(
+ TARGETS kpilot
+ LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
+)
+
+install(
+ FILES ${kpilotinclude_HEADERS}
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/kpilot
+)
+
+install(
+ FILES kpilotlib.kcfg DESTINATION ${KDE3_KCFG_DIR}
+)
+
diff --git a/kpilot/lib/COPYING b/kpilot/lib/COPYING
new file mode 100644
index 000000000..7bbb8e8bd
--- /dev/null
+++ b/kpilot/lib/COPYING
@@ -0,0 +1,509 @@
+[This license applies only to the contents of the lib/ directory,
+whereas the rest of KPilot is under the regular GNU GENERAL PUBLIC
+LICENSE. This has been done to ensure that non-GPL yet LGPL-compatibly
+licensed plugins may be written for KPilot.]
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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 Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ 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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ 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, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) 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.
+
+ d) 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.
+
+ e) 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 materials to be 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 with
+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 Lesser 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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with 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/kpilot/lib/Makefile.am b/kpilot/lib/Makefile.am
new file mode 100644
index 000000000..b2ceadf1f
--- /dev/null
+++ b/kpilot/lib/Makefile.am
@@ -0,0 +1,60 @@
+### Makefile.am for kpilot/lib
+###
+###
+
+METASOURCES = AUTO
+
+INCLUDES = $(PISOCK_INCLUDE) -I$(top_srcdir) $(all_includes)
+
+### If you must get debugging output on a platform where
+### the libs are built without debugging support, define
+### DEBUG_CERR. Define DEBUG to get debugging support anywhere.
+###
+### KDE_CXXFLAGS=-DDEBUG -DDEBUG_CERR
+##KDE_CXXFLAGS=-DNDEBUG -UDEBUG
+##KDE_CXXFLAGS=-DDEBUG
+
+lib_LTLIBRARIES = libkpilot.la
+
+libkpilot_la_SOURCES = kpilotlibSettings.kcfgc \
+ options.cc plugin.cc syncAction.cc \
+ kpilotlink.cc kpilotdevicelink.cc kpilotlocallink.cc \
+ actions.cc actionQueue.cc \
+ pilot.cc \
+ pilotAppInfo.cc pilotRecord.cc pilotDatabase.cc \
+ pilotLocalDatabase.cc pilotSerialDatabase.cc \
+ pilotMemo.cc \
+ pilotAddress.cc \
+ pilotDateEntry.cc \
+ pilotTodoEntry.cc
+
+libkpilot_la_LDFLAGS = $(PISOCK_LDFLAGS) -no-undefined $(all_libraries) $(KDE_EXTRA_RPATH) $(KDE_RPATH)
+libkpilot_la_LIBADD = $(PISOCK_LIB) $(LIB_KDEUI) $(LIB_KABC) $(top_builddir)/libkcal/libkcal.la
+
+kpilotincludedir = $(includedir)/kpilot
+kpilotinclude_HEADERS = \
+ kpilotlink.h kpilotlocallink.h kpilotdevicelink.h \
+ pilot.h \
+ pilotDatabase.h \
+ pilotLinkVersion.h \
+ pilotLocalDatabase.h \
+ pilotRecord.h \
+ pilotSerialDatabase.h \
+ plugin.h \
+ pluginfactory.h \
+ syncAction.h
+
+
+kde_kcfg_DATA = kpilotlib.kcfg
+
+check-local:
+ rm -f FAILED
+ for i in $(srcdir)/*.h ; do \
+ ( echo "#include <kdemacros.h>" ; echo "#include \"$$i\"" ; echo "int main(int argc,char **argv){return 0;}" ) > header-test.cc; \
+ echo "$$i" ; \
+ g++ $(all_includes) -I$(top_builddir) -DQT_THREAD_SUPPORT -c header-test.cc || echo "$$i" >> FAILED; \
+ done
+ test ! -e FAILED
+
+DOXYGEN_REFERENCES=libkcal kdecore
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/kpilot/lib/actionQueue.cc b/kpilot/lib/actionQueue.cc
new file mode 100644
index 000000000..e4770d369
--- /dev/null
+++ b/kpilot/lib/actionQueue.cc
@@ -0,0 +1,172 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This defines the "ActionQueue", which is the pile of actions
+** that will occur during a HotSync.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+#include "options.h"
+
+#include <qtimer.h>
+
+#include "actions.h"
+#include "plugin.h"
+
+#include "actionQueue.moc"
+
+
+
+
+ActionQueue::ActionQueue(KPilotLink *d) :
+ SyncAction(d,"ActionQueue")
+ // The string lists have default constructors
+{
+ FUNCTIONSETUP;
+}
+
+ActionQueue::~ActionQueue()
+{
+ FUNCTIONSETUP;
+ clear();
+}
+
+void ActionQueue::clear()
+{
+ SyncAction *del = 0L;
+ while ( (del = nextAction()) )
+ {
+ delete del;
+ }
+
+ Q_ASSERT(isEmpty());
+}
+
+void ActionQueue::queueInit()
+{
+ FUNCTIONSETUP;
+
+ addAction(new WelcomeAction(fHandle));
+}
+
+void ActionQueue::queueConduits(const QStringList &l,
+ const SyncAction::SyncMode &m)
+{
+ FUNCTIONSETUP;
+
+ // Add conduits here ...
+ //
+ //
+ for (QStringList::ConstIterator it = l.begin();
+ it != l.end();
+ ++it)
+ {
+ if ((*it).startsWith(CSL1("internal_")))
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << fname <<
+ ": Ignoring conduit " << *it << endl;
+#endif
+ continue;
+ }
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname
+ << ": Creating proxy with mode=" << m.name() << endl;
+#endif
+ ConduitProxy *cp = new ConduitProxy(fHandle,*it,m);
+ addAction(cp);
+ }
+}
+
+void ActionQueue::queueCleanup()
+{
+ addAction(new CleanupAction(fHandle));
+}
+
+bool ActionQueue::exec()
+{
+ actionCompleted(0L);
+ return true;
+}
+
+void ActionQueue::actionCompleted(SyncAction *b)
+{
+ FUNCTIONSETUP;
+
+ if (b)
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << fname
+ << ": Completed action "
+ << b->name()
+ << endl;
+#endif
+ delete b;
+ }
+
+ if (isEmpty())
+ {
+ delayDone();
+ return;
+ }
+ if ( deviceLink() && (!deviceLink()->tickle()) )
+ {
+ emit logError(i18n("The connection to the handheld "
+ "was lost. Synchronization cannot continue."));
+ clear();
+ delayDone();
+ return;
+ }
+
+ SyncAction *a = nextAction();
+
+ if (!a)
+ {
+ WARNINGKPILOT << "NULL action on stack."
+ << endl;
+ return;
+ }
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname
+ << ": Will run action "
+ << a->name()
+ << endl;
+#endif
+
+ QObject::connect(a, SIGNAL(logMessage(const QString &)),
+ this, SIGNAL(logMessage(const QString &)));
+ QObject::connect(a, SIGNAL(logError(const QString &)),
+ this, SIGNAL(logMessage(const QString &)));
+ QObject::connect(a, SIGNAL(logProgress(const QString &, int)),
+ this, SIGNAL(logProgress(const QString &, int)));
+ QObject::connect(a, SIGNAL(syncDone(SyncAction *)),
+ this, SLOT(actionCompleted(SyncAction *)));
+
+ // Run the action picked from the queue when we get back
+ // to the event loop.
+ QTimer::singleShot(0,a,SLOT(execConduit()));
+}
+
diff --git a/kpilot/lib/actionQueue.h b/kpilot/lib/actionQueue.h
new file mode 100644
index 000000000..bc8b11560
--- /dev/null
+++ b/kpilot/lib/actionQueue.h
@@ -0,0 +1,162 @@
+#ifndef _KPILOT_ACTIONQUEUE_H
+#define _KPILOT_ACTIONQUEUE_H
+/*
+**
+** Copyright (C) 1998-2001,2003 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qptrqueue.h>
+
+#include "syncAction.h"
+
+/** @file
+* This file defines the ActionQueue.
+*
+* This used to be called SyncStack, and while a stack is cool
+* for some things, it actually rather confuses the issue because
+* you _use_ this class for specifying "do this, then that, then ..."
+* and in program code you need to reverse that order when adding
+* items to a stack. So now it's a Queue, FIFO, and program
+* code looks more normal.
+*/
+
+/**
+* The ActionQueue is a meta-action, which handles running a bunch of
+* SyncActions in sequence. It is a SyncAction itself, so it can even
+* be queued on another ActionQueue.
+*
+* An ActionQueue is constructed with a @p device. As usual, you should
+* connect the device's deviceReady() signal with the exec() slot --
+* or something to that effect. The ActionQueue will then run all the
+* actions in the queue in sequence.
+*
+*/
+KDE_EXPORT class ActionQueue : public SyncAction
+{
+Q_OBJECT
+public:
+ /**
+ * Constructor. Pass in a KPilot device link for it to act on.
+ * It is legal to pass in 0 (NULL) as a device. Ownership of
+ * the device is unchanged.
+ */
+ ActionQueue(KPilotLink *device);
+
+ /** Destructor. */
+ virtual ~ActionQueue();
+
+ /** Is the queue empty? Returns @c true if it is. */
+ bool isEmpty() const
+ {
+ return SyncActionQueue.isEmpty();
+ };
+
+ /**
+ * You can push your own action @p a onto the queue. Ownership
+ * of the action is given to the ActionQueue object.
+ */
+ void addAction(SyncAction *a)
+ {
+ SyncActionQueue.enqueue(a);
+ };
+
+public:
+ /*
+ * Call these queue*() functions to append standard functional
+ * blocks. You should at least call queueInit() and
+ * queueCleanup() to add the welcome and cleanup actions to
+ * the queue (unless you do that yourself.)
+ *
+ * For queueInit, a WelcomeAction is added.
+ * For queueConduits, whatever is relevant for the conduits
+ * can be used, usually things in the FlagMask and ActionMask.
+ * The list of conduits in @p conduits is queued automatically.
+ */
+
+ /**
+ * Initialize the queue. This empties it out and adds a
+ * welcome action (see WelcomeAction in actions.h) so that
+ * the user knows what is happening when the ActionQueue
+ * begins to execute. Equivalent to
+ * @code
+ * clear(); addAction(new WelcomeAction);
+ * @endcode
+ */
+ void queueInit();
+
+ /**
+ * Queue a (series) of conduits @p conduits with a given
+ * sync mode @p mode. Each of the conduits named is called
+ * through a ConduitProxy object which handles loading the
+ * conduit's shared library and creating the actual SyncAction
+ * for that conduit. Actions named "internal_*" are silently
+ * ignored since those names are used by KPilot internally
+ * for administrative purposes.
+ */
+ void queueConduits(const QStringList &conduits,
+ const SyncAction::SyncMode &mode);
+
+ /**
+ * Convenience function for adding a cleanup action (see
+ * CleanupAction in actions.h) to the queue. Should be the
+ * last action added to the queue because a HotSync can only
+ * have @em one cleanup.
+ */
+ void queueCleanup();
+
+protected:
+ /**
+ * Remove all the actions from the queue and delete them
+ * (the queue owns the actions, after all).
+ */
+ void clear();
+
+ /**
+ * Dequeue the next action in the queue, ready for processing.
+ * This takes the action off the queue, so remember to delete it
+ * eventually.
+ */
+ SyncAction *nextAction()
+ {
+ return SyncActionQueue.dequeue();
+ };
+
+ /** Reimplemented from SyncAction. */
+ virtual bool exec();
+
+protected slots:
+ /**
+ * When one action finishes, start the next one.
+ */
+ void actionCompleted(SyncAction *);
+
+private:
+ /** A queue of actions to take. */
+ QPtrQueue < SyncAction > SyncActionQueue;
+};
+
+
+#endif
diff --git a/kpilot/lib/actions.cc b/kpilot/lib/actions.cc
new file mode 100644
index 000000000..6cd88273b
--- /dev/null
+++ b/kpilot/lib/actions.cc
@@ -0,0 +1,137 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+#include "options.h"
+
+#include <qapplication.h>
+#include <qdir.h>
+#include <qfile.h>
+
+#include <ksavefile.h>
+
+#include "pilot.h"
+#include "pilotUser.h"
+
+#include "actions.h"
+
+
+
+WelcomeAction::WelcomeAction(KPilotLink *p) :
+ SyncAction(p,"welcomeAction")
+{
+ FUNCTIONSETUP;
+}
+
+/* virtual */ bool WelcomeAction::exec()
+{
+ FUNCTIONSETUP;
+
+ addSyncLogEntry(i18n("KPilot %1 HotSync starting...\n")
+ .arg(QString::fromLatin1(KPILOT_VERSION)));
+ emit logMessage( i18n("Using encoding %1 on the handheld.").arg(Pilot::codecName()) );
+ emit syncDone(this);
+ return true;
+}
+
+SorryAction::SorryAction(KPilotLink *p, const QString &s) :
+ SyncAction(p,"sorryAction"),
+ fMessage(s)
+{
+ if (fMessage.isEmpty())
+ {
+ fMessage = i18n("KPilot is busy and cannot process the "
+ "HotSync right now.");
+ }
+}
+
+bool SorryAction::exec()
+{
+ FUNCTIONSETUP;
+
+ addSyncLogEntry(fMessage);
+ return delayDone();
+}
+
+CleanupAction::CleanupAction(KPilotLink *p) : SyncAction(p,"cleanupAction")
+{
+ FUNCTIONSETUP;
+}
+
+/* virtual */ bool CleanupAction::exec()
+{
+ FUNCTIONSETUP;
+
+ if (deviceLink())
+ {
+ deviceLink()->endSync( KPilotLink::UpdateUserInfo );
+ }
+ emit syncDone(this);
+ return true;
+}
+
+
+TestLink::TestLink(KPilotLink * p) :
+ SyncAction(p, "testLink")
+{
+ FUNCTIONSETUP;
+
+}
+
+/* virtual */ bool TestLink::exec()
+{
+ FUNCTIONSETUP;
+
+ int i;
+ int dbindex = 0;
+ int count = 0;
+ struct DBInfo db;
+
+ addSyncLogEntry(i18n("Testing.\n"));
+
+ while ((i = deviceLink()->getNextDatabase(dbindex,&db)) > 0)
+ {
+ count++;
+ dbindex = db.index + 1;
+
+ DEBUGKPILOT << fname
+ << ": Read database " << db.name
+ << " with index " << db.index
+ << endl;
+
+ // Let the Pilot User know what's happening
+ openConduit();
+ // Let the KDE User know what's happening
+ // Pretty sure all database names are in latin1.
+ emit logMessage(i18n("Syncing database %1...")
+ .arg(Pilot::fromPilot(db.name)));
+ }
+
+ emit logMessage(i18n("HotSync finished."));
+ emit syncDone(this);
+ return true;
+}
diff --git a/kpilot/lib/actions.h b/kpilot/lib/actions.h
new file mode 100644
index 000000000..ee353551f
--- /dev/null
+++ b/kpilot/lib/actions.h
@@ -0,0 +1,115 @@
+#ifndef _KPILOT_ACTIONS_H
+#define _KPILOT_ACTIONS_H
+/*
+**
+** Copyright (C) 1998-2001,2003 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "syncAction.h"
+
+/** @file
+* This file defines some simple standard actions. None of these
+* actions require much in the way of configuration; none provide
+* particularly complicated functionality.
+*/
+
+/**
+* This action puts "Welcome to KPilot" in the sync log of the handheld.
+* It is added automatically to a ActionQueue by queueInit() in order
+* to inform the user of the sync.
+*/
+KDE_EXPORT class WelcomeAction : public SyncAction
+{
+public:
+ /** Constructor. */
+ WelcomeAction(KPilotLink *);
+
+protected:
+ /** Reimplemented from SyncAction. */
+ virtual bool exec();
+} ;
+
+/**
+* This one just says "sorry, can't sync now". This is used
+* in cases when the hotsync starts while KPilot is busy configuring
+* something and can't be interrupted.
+*/
+KDE_EXPORT class SorryAction : public SyncAction
+{
+public:
+ /**
+ * Constructor. The action will be executed on the given
+ * link @p device . If the given string @p s is non-empty,
+ * print that message (it must be i18n()ed already) instead of
+ * the standard message.
+ */
+ SorryAction(KPilotLink *device, const QString &s=QString::null);
+
+protected:
+ /** Reimplemented from SyncAction. */
+ virtual bool exec();
+
+ /** Message to print to the sync log. */
+ QString fMessage;
+} ;
+
+/**
+* End the HotSync. This action cleans up the handheld and
+* removes cruft. There should be exactly @em one CleanupAction
+* executed during a HotSync. Since this action informs the
+* device that the HotSync is over, it should be the last
+* action executed.
+*/
+KDE_EXPORT class CleanupAction : public SyncAction
+{
+public:
+ /** Constructor. */
+ CleanupAction(KPilotLink *device);
+
+protected:
+ /** Reimplemented from SyncAction. */
+ virtual bool exec();
+} ;
+
+/**
+* This action is intended to test the link with the handheld
+* and not do anything spectacular. It lists all the databases
+* on the handheld in the sync log.
+*/
+KDE_EXPORT class TestLink : public SyncAction
+{
+public:
+ /** Constructor. */
+ TestLink(KPilotLink *device);
+
+protected:
+ /** Reimplemented from SyncAction. */
+ virtual bool exec();
+} ;
+
+
+#endif
diff --git a/kpilot/lib/idmapper.cc b/kpilot/lib/idmapper.cc
new file mode 100644
index 000000000..6e1031efe
--- /dev/null
+++ b/kpilot/lib/idmapper.cc
@@ -0,0 +1,247 @@
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "idmapper.h"
+#include "idmapperxml.h"
+#include "options.h"
+
+#include <qsqldatabase.h>
+#include <qfile.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+class IDMapperPrivate
+{
+public:
+ IDMapperPrivate()
+ {
+ fXmlSource = 0L;
+ }
+
+ ~IDMapperPrivate()
+ {
+ FUNCTIONSETUP;
+
+ KPILOT_DELETE(fXmlSource);
+ }
+
+ IDMapperXml *fXmlSource;
+};
+
+IDMapper::IDMapper()
+{
+ FUNCTIONSETUP;
+
+ fP = new IDMapperPrivate();
+
+ QString dbPath = KGlobal::dirs()->
+ saveLocation("data", CSL1("kpilot/") );
+ QString dbFile = dbPath + CSL1("mapping.xml");
+
+ if( !openDatasource( dbFile ) )
+ {
+ DEBUGKPILOT << fname << "Could not open or create xml file." << endl;
+ }
+}
+
+IDMapper::IDMapper( const QString &file)
+{
+ FUNCTIONSETUP;
+
+ fP = new IDMapperPrivate();
+
+ if( !openDatasource( file ) )
+ {
+ DEBUGKPILOT << fname << "Could not open or create xml file." << endl;
+ }
+}
+
+IDMapper::~IDMapper()
+{
+ KPILOT_DELETE(fP);
+}
+
+bool IDMapper::openDatasource( const QString &file )
+{
+ FUNCTIONSETUP;
+
+ fP->fXmlSource = new IDMapperXml( file );
+ return fP->fXmlSource->open();
+}
+
+void IDMapper::registerPCObjectId( const QString &conduit, const QString &uid )
+{
+ FUNCTIONSETUP;
+
+ IDMapping mapping = IDMapping( conduit );
+ mapping.setUid( uid );
+
+ fP->fXmlSource->addMapping( mapping );
+ fP->fXmlSource->save();
+}
+
+void IDMapper::registerHHObjectId( const QString &conduit, recordid_t pid )
+{
+ FUNCTIONSETUP;
+
+ IDMapping mapping = IDMapping( conduit );
+ mapping.setPid( pid );
+
+ fP->fXmlSource->addMapping( mapping );
+ fP->fXmlSource->save();
+}
+
+QValueList<QString> IDMapper::getPCObjectIds( const QString &conduit )
+{
+ FUNCTIONSETUP;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+ QValueList<QString> uids;
+
+ DEBUGKPILOT << fname << ": total " << mappings.count() << endl;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = (*it);
+
+ DEBUGKPILOT << fname << ": mapping.conduit() = " << mapping.conduit() << endl;
+ DEBUGKPILOT << fname << ": conduit = " << conduit << endl;
+
+ if( (mapping.conduit() == conduit) && !mapping.uid().isNull() )
+ {
+ DEBUGKPILOT << fname << ": mapping.conduit() == conduit" << endl;
+ uids.append( mapping.uid() );
+ }
+ }
+
+ return uids;
+}
+
+QValueList<recordid_t> IDMapper::getHHObjectIds( const QString &conduit )
+{
+ FUNCTIONSETUP;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+ QValueList<recordid_t> pids;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = *it;
+ DEBUGKPILOT << fname << ": mapping.conduit() = " << mapping.conduit() << endl;
+ DEBUGKPILOT << fname << ": " << mapping.pid() << endl;
+ if( mapping.conduit() == conduit && mapping.pid() != 0 )
+ {
+ DEBUGKPILOT << fname << ": mapping.conduit() == conduit" << endl;
+ pids.append( mapping.pid() );
+ }
+ }
+
+ return pids;
+}
+
+bool IDMapper::hasPCId( const QString &conduit, recordid_t pid )
+{
+ FUNCTIONSETUP;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = *it;
+ if( mapping.conduit() == conduit && mapping.pid() == pid )
+ {
+ return !mapping.uid().isNull();
+ }
+ }
+
+ return false;
+}
+
+bool IDMapper::hasHHId( const QString &conduit, const QString &uid )
+{
+ FUNCTIONSETUP;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = *it;
+ if( mapping.conduit() == conduit && mapping.uid() == uid )
+ {
+ return mapping.pid() != 0;
+ }
+ }
+
+ return false;
+}
+
+void IDMapper::setHHObjectId( const QString &conduit, const QString &uid
+ , recordid_t pid )
+{
+ FUNCTIONSETUP;
+
+ bool modified = false;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = *it;
+ if( mapping.conduit() == conduit && mapping.uid() == uid )
+ {
+ mapping.setPid( pid );
+ fP->fXmlSource->save();
+ modified = true;
+ }
+ }
+}
+
+void IDMapper::setPCObjectId( const QString &conduit, recordid_t pid
+ , const QString &uid )
+{
+ FUNCTIONSETUP;
+
+ bool modified = false;
+
+ QValueList<IDMapping> &mappings = fP->fXmlSource->mappings();
+ QValueList<IDMapping>::iterator it;
+
+ for ( it = mappings.begin(); it != mappings.end(); ++it )
+ {
+ IDMapping &mapping = *it;
+ if( mapping.conduit() == conduit && mapping.pid() == pid )
+ {
+ mapping.setUid( uid );
+ fP->fXmlSource->save();
+ modified = true;
+ }
+ }
+}
diff --git a/kpilot/lib/idmapper.h b/kpilot/lib/idmapper.h
new file mode 100644
index 000000000..0364dbc93
--- /dev/null
+++ b/kpilot/lib/idmapper.h
@@ -0,0 +1,159 @@
+#ifndef _KPILOT_IDMAPPER_H
+#define _KPILOT_IDMAPPER_H
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qvaluelist.h>
+
+#include "pi-macros.h"
+
+#include <kconfig.h>
+
+class IDMapperPrivate;
+
+/**
+ * Much of the conduits are recordbased. This class can be used bij the conduits
+ * to keep track of the mapping between records on the handheld and records on
+ * the pc.
+ */
+class IDMapper
+{
+public:
+ /**
+ * Creates a new IDMapper with default datasource.
+ */
+ IDMapper();
+
+ /**
+ * Creates a new IDMapper with file as datasource.
+ */
+ IDMapper( const QString &file );
+
+ ~IDMapper();
+
+ /**
+ * Adds an uid for PC objects to the database.
+ */
+ void registerPCObjectId( const QString &conduit, const QString &uid );
+
+ /**
+ * Returns all known uid's for given conduit.
+ */
+ QValueList<QString> getPCObjectIds( const QString &conduit );
+
+ /**
+ * Adds a pid for HH objects to the database.
+ */
+ void registerHHObjectId( const QString &conduit, recordid_t pid );
+
+ /**
+ * Returns all know pids for given conduit.
+ */
+ QValueList<recordid_t> getHHObjectIds( const QString &conduit );
+
+ /**
+ * Sets the PC uid for the handheld record with pid. Does nothing when
+ * there is no handheld record with pid.
+ */
+ void setPCObjectId( const QString &conduit, recordid_t pid
+ , const QString &uid );
+
+ /**
+ * Sets the PC uid for the handheld record with pid. Does nothing when
+ * there is no handheld record with pid.
+ */
+ void setHHObjectId( const QString &conduit, const QString &uid
+ , recordid_t pid );
+
+ /**
+ * Returns the PC uid for the handheld record with pid. Returns 0 when no
+ * pid exists for given uid.
+ */
+ recordid_t getHHObjectId( const QString &conduit, const QString &uid );
+
+ /**
+ * Returns the HH pid for the PC record with uid. Returns an empty string
+ * when no uid exists for given pid.
+ */
+ QString getHHObjectId( const QString &conduit, recordid_t pid );
+
+ /**
+ * Returns true when there is a uid set for given pid. The conduit itself
+ * must determine if the two objects are in sync if this function returns
+ * true.
+ */
+ bool hasPCId( const QString &conduit, recordid_t pid );
+
+ /**
+ * Returns true when there is a pid set for given uid. The conduit itself
+ * must determine if the two objects are in sync if this function returns
+ * true.
+ */
+ bool hasHHId( const QString &conduit, const QString &uid );
+
+ /**
+ * Sets the time that the two objects where last synced. The conduits
+ * should call this method (or the pid version) when two objects are synced.
+ * When the uid does not exist nothing happens.
+ */
+ void setLastSyncTime( const QString &conduit, const QString &uid,
+ const QDateTime &date );
+
+ /**
+ * Sets the time that the two objects where last synced. The conduits
+ * should call this (or the uid version) method when two objects are synced.
+ * When the pid does not exist nothing happens.
+ */
+ void setLastSyncTime( const QString &conduit, recordid_t pid
+ , const QDateTime &date );
+
+ /**
+ * Returns the date/time for the last time that the item with uid was
+ * synced. This date is set by:
+ * - setLastSyncTime (uid/pid)
+ *
+ * Returns a null datetime when the pid does not excist.
+ */
+ QDateTime lastTimeSynced( const QString &conduit, const QString &uid );
+
+ /**
+ * Returns the date/time for the last time that the item with pid was
+ * synced. This date is set by:
+ * - setLastSyncTime (uid/pid)
+ *
+ * Returns a null datetime when the pid does not excist.
+ */
+ QDateTime lastTimeSynced( const QString &conduit, recordid_t pid );
+
+protected:
+ bool openDatasource( const QString &file );
+
+private:
+ IDMapperPrivate *fP;
+};
+
+#endif
diff --git a/kpilot/lib/idmapperxml.cc b/kpilot/lib/idmapperxml.cc
new file mode 100644
index 000000000..a2a78a0c9
--- /dev/null
+++ b/kpilot/lib/idmapperxml.cc
@@ -0,0 +1,213 @@
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "idmapperxml.h"
+
+#include "options.h"
+
+IDMapperXml::IDMapperXml( const QString &file ) : fFile(file)
+ , fCurrentMapping( 0l )
+{
+}
+
+IDMapperXml::~IDMapperXml()
+{
+ FUNCTIONSETUP;
+}
+
+bool IDMapperXml::open()
+{
+ FUNCTIONSETUP;
+
+ root = doc.createElement( CSL1("mappings") );
+ QDomNode node = doc.createProcessingInstruction(CSL1("xml")
+ ,CSL1("version=\"1.0\" encoding=\"UTF-8\""));
+
+ doc.appendChild( node );
+ doc.appendChild( root );
+
+ if( !fFile.exists() )
+ {
+ DEBUGKPILOT << fname << ": Creating " << fFile.name() << endl;
+
+ if( fFile.open( IO_ReadWrite ) )
+ {
+ QTextStream out( &fFile );
+ doc.save( out, 4 );
+ fFile.close();
+ return true;
+ }
+ else
+ {
+ DEBUGKPILOT << fname << ": Could not create " << fFile.name() << endl;
+ return false;
+ }
+ }
+ else
+ {
+ DEBUGKPILOT << fname << ": Parsing file " << fFile.name() << endl;
+ QXmlSimpleReader reader;
+ reader.setContentHandler( this );
+
+ // Make sure that the file is closed after parsing.
+ bool result = reader.parse( fFile );
+ fFile.close();
+
+ return result;
+ }
+}
+
+void IDMapperXml::save()
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname << ": Saving " << fMappings.count()
+ << " mappings..." << endl;
+ DEBUGKPILOT << fname << ": ";
+
+ QValueList<IDMapping>::const_iterator it;
+ for ( it = fMappings.begin(); it != fMappings.end(); ++it )
+ {
+ DEBUGKPILOT << ".";
+
+ IDMapping mapping = (*it);
+
+ DEBUGKPILOT << fname << ": " << mapping.conduit();
+
+ QDomElement mappingElement = doc.createElement( CSL1("mapping") );
+ mappingElement.setAttribute( CSL1("conduit"), mapping.conduit() );
+
+ if( !mapping.uid().isNull() )
+ {
+ QDomElement uidElement = doc.createElement( CSL1("uid") );
+ uidElement.setAttribute( CSL1("value"), mapping.uid() );
+ mappingElement.appendChild( uidElement );
+ }
+
+ if( mapping.pid() != 0 )
+ {
+ QDomElement uidElement = doc.createElement( CSL1("pid") );
+ uidElement.setAttribute( CSL1("value"), mapping.pid() );
+ mappingElement.appendChild( uidElement );
+ }
+
+ if( !mapping.lastSyncTime().isNull() )
+ {
+ QDomElement uidElement = doc.createElement( CSL1("pid") );
+ uidElement.setAttribute( CSL1("value"), QString::number( mapping.pid() ) );
+ mappingElement.appendChild( uidElement );
+ }
+
+ root.appendChild( mappingElement );
+ }
+
+ if( fFile.open( IO_ReadWrite ) )
+ {
+ QTextStream out( &fFile );
+ doc.save( out, 4 );
+ fFile.close();
+
+ DEBUGKPILOT << endl << fname << ": finished saving." << endl;
+ }
+}
+
+void IDMapperXml::addMapping( const IDMapping &mapping )
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname << ": " << mapping.conduit() << endl;
+
+ fMappings.append( mapping );
+
+ DEBUGKPILOT << fname << ": " << fMappings.first().conduit() << endl;
+}
+
+QValueList<IDMapping>& IDMapperXml::mappings()
+{
+ return fMappings;
+}
+
+bool IDMapperXml::startElement( const QString &namespaceURI
+ , const QString &localName, const QString &qName
+ , const QXmlAttributes &attribs )
+{
+ FUNCTIONSETUP;
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+
+ if( qName == CSL1("mapping") )
+ {
+ QString conduit( attribs.value( CSL1("conduit") ) );
+
+ fCurrentMapping = new IDMapping( conduit );
+ }
+ else if( qName == CSL1("uid") )
+ {
+ fCurrentMapping->setUid( attribs.value( CSL1("value") ) );
+ }
+ else if( qName == CSL1("pid") )
+ {
+ fCurrentMapping->setPid( attribs.value( CSL1("value") ).toULong() );
+ }
+ else if( qName == CSL1("lastsync") )
+ {
+ // NOTE: this isn't very robuust!
+ // Parses only dates in the form: dd-mm-yyyy hh:mm:ss
+ QString date = attribs.value( CSL1("value") );
+ int day = date.left(2).toInt();
+ int month = date.mid(3,2).toInt();
+ int year = date.mid(6, 4).toInt();
+
+ int hour = date.mid(11,2).toInt();
+ int minute = date.mid(14,2).toInt();
+ int second = date.mid(17,2).toInt();
+
+ QDate tmpDate = QDate( year, month, day );
+ QTime tmpTime = QTime( hour, minute, second );
+
+ fCurrentMapping->setLastSyncTime( QDateTime( tmpDate, tmpTime ) );
+ }
+
+ return true;
+}
+
+bool IDMapperXml::endElement( const QString &namespaceURI
+ , const QString &localName, const QString &qName )
+{
+ FUNCTIONSETUP;
+
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+ Q_UNUSED(qName);
+
+ if( qName == CSL1("mapping") )
+ {
+ addMapping( *fCurrentMapping );
+ delete fCurrentMapping;
+ fCurrentMapping = 0l;
+ }
+
+ return true;
+}
diff --git a/kpilot/lib/idmapperxml.h b/kpilot/lib/idmapperxml.h
new file mode 100644
index 000000000..455673888
--- /dev/null
+++ b/kpilot/lib/idmapperxml.h
@@ -0,0 +1,84 @@
+#ifndef _KPILOT_IDMAPPERXML_H
+#define _KPILOT_IDMAPPERXML_H
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "idmapping.h"
+
+#include <qxml.h>
+#include <qdom.h>
+#include <qstring.h>
+#include <qptrcollection.h>
+
+class IDMapperXml : public QXmlDefaultHandler
+{
+public:
+ IDMapperXml( const QString &file );
+
+ ~IDMapperXml();
+
+ /**
+ * Opens and parses the file or creates a new one if the file does not exist.
+ */
+ bool open();
+
+ /**
+ * Saves the current mappings to the xml-file. Note this function must be
+ * called after changes and before deleting the IDMapperXml object. Otherwise
+ * the changes won't be saved.
+ */
+ void save();
+
+ /**
+ * Adds a mapping to the collection of mappings.
+ */
+ void addMapping( const IDMapping &mapping );
+
+ /**
+ * Returns the collection of mappings.
+ */
+ QValueList<IDMapping> &mappings();
+
+ /**
+ * Overloaded function to parse the xml file.
+ */
+ bool startElement( const QString &namespaceURI, const QString &localName
+ , const QString &qName, const QXmlAttributes &attribs );
+
+ /**
+ * Overloaded function to parse the xml file.
+ */
+ bool endElement( const QString &namespaceURI, const QString &localName
+ , const QString &qName );
+
+private:
+ QFile fFile;
+ QDomDocument doc;
+ QDomElement root;
+ IDMapping *fCurrentMapping;
+ QValueList<IDMapping> fMappings;
+};
+
+#endif
diff --git a/kpilot/lib/idmapping.cc b/kpilot/lib/idmapping.cc
new file mode 100644
index 000000000..7a49e9e3e
--- /dev/null
+++ b/kpilot/lib/idmapping.cc
@@ -0,0 +1,89 @@
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "idmapping.h"
+
+IDMapping::IDMapping()
+{
+}
+
+IDMapping::IDMapping( const QString &conduit )
+{
+ fConduit = conduit;
+ fPid = 0;
+}
+
+IDMapping::IDMapping( const IDMapping &m )
+{
+ fConduit = m.fConduit;
+ fUid = m.fUid;
+ fPid = m.fPid;
+ fLastSync = m.fLastSync;
+}
+
+IDMapping IDMapping::operator=( const IDMapping &m )
+{
+ IDMapping local( m.fConduit );
+ local.fUid = m.fUid;
+ local.fPid = m.fPid;
+ local.fLastSync = m.fLastSync;
+
+ return local;
+}
+
+void IDMapping::setUid( const QString &uid )
+{
+ fUid = uid;
+}
+
+void IDMapping::setPid( recordid_t pid )
+{
+ fPid = pid;
+}
+
+void IDMapping::setLastSyncTime( const QDateTime &datetime )
+{
+ fLastSync = datetime;
+}
+
+QString IDMapping::conduit() const
+{
+ return fConduit;
+}
+
+QString IDMapping::uid() const
+{
+ return fUid;
+}
+
+recordid_t IDMapping::pid() const
+{
+ return fPid;
+}
+
+QDateTime IDMapping::lastSyncTime() const
+{
+ return fLastSync;
+}
diff --git a/kpilot/lib/idmapping.h b/kpilot/lib/idmapping.h
new file mode 100644
index 000000000..acb17dc8a
--- /dev/null
+++ b/kpilot/lib/idmapping.h
@@ -0,0 +1,66 @@
+#ifndef _KPILOT_IDMAPPING_H
+#define _KPILOT_IDMAPPING_H
+/*
+** Copyright (C) 2006 Bertjan Broeksema <bbroeksema@bluebottle.com>
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "pi-macros.h"
+
+#include <qstring.h>
+#include <qdatetime.h>
+
+class IDMapping
+{
+public:
+ IDMapping();
+
+ IDMapping( const QString &conduit );
+
+ IDMapping( const IDMapping &m );
+
+ IDMapping operator=( const IDMapping &m );
+
+ void setUid( const QString &uid );
+
+ void setPid( recordid_t uid );
+
+ void setLastSyncTime( const QDateTime &datetime );
+
+ QString conduit() const;
+
+ QString uid() const;
+
+ recordid_t pid() const;
+
+ QDateTime lastSyncTime() const;
+
+private:
+ QString fConduit;
+ QString fUid;
+ recordid_t fPid;
+ QDateTime fLastSync;
+};
+
+#endif
diff --git a/kpilot/lib/kpilotdevicelink.cc b/kpilot/lib/kpilotdevicelink.cc
new file mode 100644
index 000000000..55027d763
--- /dev/null
+++ b/kpilot/lib/kpilotdevicelink.cc
@@ -0,0 +1,966 @@
+/* KPilot
+ **
+ ** Copyright (C) 1998-2001 by Dan Pilone
+ ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+ ** Copyright (C) 2006-2007 Adriaan de Groot <groot@kde.org>
+ ** Copyright (C) 2007 Jason 'vanRijn' Kasper <vR@movingparts.net>
+ **
+ */
+
+/*
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU Lesser General Public License as published by
+ ** the Free Software Foundation; either version 2.1 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ** GNU Lesser General Public License for more details.
+ **
+ ** You should have received a copy of the GNU Lesser General Public License
+ ** along with this program in a file called COPYING; if not, write to
+ ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ ** MA 02110-1301, USA.
+ */
+
+/*
+ ** Bug reports and questions can be sent to kde-pim@kde.org
+ */
+
+#include "options.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <iostream>
+
+#include <pi-source.h>
+#include <pi-socket.h>
+#include <pi-dlp.h>
+#include <pi-file.h>
+#include <pi-buffer.h>
+
+#include <qdir.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qthread.h>
+#include <qsocketnotifier.h>
+
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include "pilotUser.h"
+#include "pilotSysInfo.h"
+#include "pilotCard.h"
+#include "pilotSerialDatabase.h"
+#include "pilotLocalDatabase.h"
+
+#include "kpilotlink.h"
+#include "kpilotdevicelinkPrivate.moc"
+#include "kpilotdevicelink.moc"
+
+
+DeviceMap *DeviceMap::mThis = 0L;
+
+
+static inline void startOpenTimer(DeviceCommThread *dev, QTimer *&t)
+{
+ if ( !t)
+ {
+ t = new QTimer(dev);
+ QObject::connect(t, SIGNAL(timeout()), dev, SLOT(openDevice()));
+ }
+ // just a single-shot timer. we'll know when to start it again...
+ t->start(1000, true);
+}
+
+DeviceCommThread::DeviceCommThread(KPilotDeviceLink *d) :
+ QThread(),
+ fDone(true),
+ fHandle(d),
+ fOpenTimer(0L),
+ fSocketNotifier(0L),
+ fSocketNotifierActive(false),
+ fWorkaroundUSBTimer(0L),
+ fPilotSocket(-1),
+ fTempSocket(-1),
+ fAcceptedCount(0)
+{
+ FUNCTIONSETUP;
+}
+
+
+DeviceCommThread::~DeviceCommThread()
+{
+ FUNCTIONSETUPL(2);
+ close();
+ KPILOT_DELETE(fWorkaroundUSBTimer);
+}
+
+void DeviceCommThread::close()
+{
+ FUNCTIONSETUPL(2);
+
+ KPILOT_DELETE(fWorkaroundUSBTimer);
+ KPILOT_DELETE(fOpenTimer);
+ KPILOT_DELETE(fSocketNotifier);
+ fSocketNotifierActive=false;
+
+ if (fTempSocket != -1)
+ {
+ DEBUGKPILOT << fname
+ << ": device comm thread closing socket: ["
+ << fTempSocket << "]" << endl;
+
+ pi_close(fTempSocket);
+ }
+
+ if (fPilotSocket != -1)
+ {
+ DEBUGKPILOT << fname
+ << ": device comm thread closing socket: ["
+ << fPilotSocket << "]" << endl;
+
+ pi_close(fPilotSocket);
+ }
+
+ fTempSocket = (-1);
+ fPilotSocket = (-1);
+
+ DeviceMap::self()->unbindDevice(link()->fRealPilotPath);
+}
+
+void DeviceCommThread::reset()
+{
+ FUNCTIONSETUP;
+
+ if (link()->fMessages->shouldPrint(Messages::OpenFailMessage))
+ {
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogMessage,
+ i18n("Could not open device: %1 (will retry)")
+ .arg(link()->pilotPath() )));
+ }
+
+ link()->fMessages->reset();
+ close();
+
+ // Timer already deleted by close() call.
+ startOpenTimer(this,fOpenTimer);
+
+ link()->fLinkStatus = WaitingForDevice;
+}
+
+/**
+ * This is an asyncronous process. We try to create a socket with the Palm
+ * and then bind to it (in open()). If we're able to do those 2 things, then
+ * we do 2 things: we set a timeout timer (which will tell us that X amount of
+ * time has transpired before we get into the meat of the sync transaction), and
+ * we also set up a QSocketNotifier, which will tell us when data is available
+ * to be read from the Palm socket. If we were unable to create a socket
+ * and/or bind to the Palm in this method, we'll start our timer again.
+ */
+void DeviceCommThread::openDevice()
+{
+ FUNCTIONSETUPL(2);
+
+ bool deviceOpened = false;
+
+ // This transition (from Waiting to Found) can only be
+ // taken once.
+ //
+ if (link()->fLinkStatus == WaitingForDevice)
+ {
+ link()->fLinkStatus = FoundDevice;
+ }
+
+ if (link()->fMessages->shouldPrint(Messages::OpenMessage))
+ {
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogMessage,
+ i18n("Trying to open device %1...")
+ .arg(link()->fPilotPath)));
+ }
+
+ // if we're not supposed to be done, try to open the main pilot
+ // path...
+ if (!fDone && link()->fPilotPath.length() > 0)
+ {
+ DEBUGKPILOT << fname << ": Opening main pilot path: ["
+ << link()->fPilotPath << "]." << endl;
+ deviceOpened = open(link()->fPilotPath);
+ }
+
+ // only try the temp device if our earlier attempt didn't work and the temp
+ // device is different than the main device, and it's a non-empty
+ // string
+ bool tryTemp = !deviceOpened && (link()->fTempDevice.length() > 0) && (link()->fPilotPath != link()->fTempDevice);
+
+ // if we're not supposed to be done, and we should try the temp
+ // device, try the temp device...
+ if (!fDone && tryTemp)
+ {
+ DEBUGKPILOT << fname << ": Couldn't open main pilot path. "
+ << "Now trying temp device: ["
+ << link()->fTempDevice << "]." << endl;
+ deviceOpened = open(link()->fTempDevice);
+ }
+
+ // if we couldn't connect, try to connect again...
+ if (!fDone && !deviceOpened)
+ {
+ startOpenTimer(this, fOpenTimer);
+ }
+}
+
+bool DeviceCommThread::open(const QString &device)
+{
+ FUNCTIONSETUPL(2);
+
+ int ret;
+ int e = 0;
+ QString msg;
+
+ if (fTempSocket != -1)
+ {
+ pi_close(fTempSocket);
+ }
+ fTempSocket = (-1);
+
+ link()->fRealPilotPath
+ = KStandardDirs::realFilePath(device.isEmpty() ? link()->fPilotPath : device);
+
+ if ( !DeviceMap::self()->canBind(link()->fRealPilotPath) )
+ {
+ msg = i18n("Already listening on that device");
+
+ WARNINGKPILOT << "Pilot Path: ["
+ << link()->fRealPilotPath << "] already connected." << endl;
+ WARNINGKPILOT << msg << endl;
+
+ link()->fLinkStatus = PilotLinkError;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));
+
+ return false;
+ }
+
+ DEBUGKPILOT << fname << ": Trying to create socket." << endl;
+
+ fTempSocket = pi_socket(PI_AF_PILOT, PI_SOCK_STREAM, PI_PF_DLP);
+
+ if (fTempSocket < 0)
+ {
+ e = errno;
+ msg = i18n("Cannot create socket for communicating "
+ "with the Pilot (%1)").arg(errorMessage(e));
+ DEBUGKPILOT << msg << endl;
+ DEBUGKPILOT << "(" << strerror(e) << ")" << endl;
+
+ link()->fLinkStatus = PilotLinkError;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));
+
+ return false;
+ }
+
+ DEBUGKPILOT << fname << ": Got socket: [" << fTempSocket << "]" << endl;
+
+ link()->fLinkStatus = CreatedSocket;
+
+ DEBUGKPILOT << fname << ": Binding to path: ["
+ << link()->fRealPilotPath << "]" << endl;
+
+ ret = pi_bind(fTempSocket, QFile::encodeName(link()->fRealPilotPath));
+
+ if (ret < 0)
+ {
+ DEBUGKPILOT << fname
+ << ": pi_bind error: ["
+ << strerror(errno) << "]" << endl;
+
+ e = errno;
+ msg = i18n("Cannot open Pilot port \"%1\". ").arg(link()->fRealPilotPath);
+
+ DEBUGKPILOT << msg << endl;
+ DEBUGKPILOT << "(" << strerror(e) << ")" << endl;
+
+ link()->fLinkStatus = PilotLinkError;
+
+ if (link()->fMessages->shouldPrint(Messages::OpenFailMessage))
+ {
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));
+ }
+
+ return false;
+ }
+
+ link()->fLinkStatus = DeviceOpen;
+ DeviceMap::self()->bindDevice(link()->fRealPilotPath);
+
+ fSocketNotifier = new QSocketNotifier(fTempSocket,
+ QSocketNotifier::Read, this);
+ QObject::connect(fSocketNotifier, SIGNAL(activated(int)),
+ this, SLOT(acceptDevice()));
+ fSocketNotifierActive=true;
+
+ /**
+ * We _always_ want to set a maximum amount of time that we will wait
+ * for the sync process to start. In the case where our user
+ * has told us that he has a funky USB device, set the workaround timeout
+ * for shorter than normal.
+ */
+ int timeout=20000;
+ if (link()->fWorkaroundUSB)
+ {
+ timeout=5000;
+ }
+
+ fWorkaroundUSBTimer = new QTimer(this);
+ connect(fWorkaroundUSBTimer, SIGNAL(timeout()), this, SLOT(workaroundUSB()));
+ fWorkaroundUSBTimer->start(timeout, true);
+
+ return true;
+}
+
+/**
+ * We've been notified by our QSocketNotifier that we have data available on the
+ * socket. Try to go through the remaining steps of the connnection process.
+ * Note: If we return at all from this before the very end without a successful
+ * connection, we need to make sure we restart our connection open timer, otherwise
+ * it won't be restarted.
+ */
+void DeviceCommThread::acceptDevice()
+{
+ FUNCTIONSETUP;
+
+ int ret;
+
+ /**
+ * Our socket notifier should be the only reason that we end up here.
+ * If we're here without him being active, we have a problem. Try to clean
+ * up and get out.
+ */
+ if (!fSocketNotifierActive)
+ {
+ if (!fAcceptedCount)
+ {
+ kdWarning() << k_funcinfo << ": Accidentally in acceptDevice()"
+ << endl;
+ }
+ fAcceptedCount++;
+ if (fAcceptedCount>10)
+ {
+ // Damn the torpedoes
+ KPILOT_DELETE(fSocketNotifier);
+ }
+ return;
+ }
+
+ if (fSocketNotifier)
+ {
+ // fSocketNotifier->setEnabled(false);
+ fSocketNotifierActive=false;
+ KPILOT_DELETE(fSocketNotifier);
+ }
+
+ DEBUGKPILOT << fname << ": Found connection on device: ["
+ << link()->pilotPath().latin1() << "]." <<endl;
+
+ DEBUGKPILOT << fname
+ << ": Current status: ["
+ << link()->statusString()
+ << "] and socket: [" << fTempSocket << "]" << endl;
+
+ ret = pi_listen(fTempSocket, 1);
+ if (ret < 0)
+ {
+ char *s = strerror(errno);
+
+ WARNINGKPILOT << "pi_listen returned: [" << s << "]" << endl;
+
+ // Presumably, strerror() returns things in
+ // local8Bit and not latin1.
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
+ i18n("Cannot listen on Pilot socket (%1)").
+ arg(QString::fromLocal8Bit(s))));
+ reset();
+ return;
+ }
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, QString::null, 10));
+
+ DEBUGKPILOT << fname <<
+ ": Listening to pilot. Now trying accept..." << endl;
+
+ int timeout = 20;
+ fPilotSocket = pi_accept_to(fTempSocket, 0, 0, timeout);
+
+ if (fPilotSocket < 0)
+ {
+ char *s = strerror(errno);
+
+ WARNINGKPILOT << "pi_accept returned: [" << s << "]" << endl;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError, i18n("Cannot accept Pilot (%1)")
+ .arg(QString::fromLocal8Bit(s))));
+
+ link()->fLinkStatus = PilotLinkError;
+ reset();
+ return;
+ }
+
+ DEBUGKPILOT << fname << ": Link accept done." << endl;
+
+ if ((link()->fLinkStatus != DeviceOpen) || (fPilotSocket == -1))
+ {
+ link()->fLinkStatus = PilotLinkError;
+ WARNINGKPILOT << "Already connected or unable to connect!" << endl;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError, i18n("Cannot accept Pilot (%1)")
+ .arg(i18n("already connected"))));
+
+ reset();
+ return;
+ }
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, QString::null, 30));
+
+ DEBUGKPILOT << fname << ": doing dlp_ReadSysInfo..." << endl;
+
+ struct SysInfo sys_info;
+ if (dlp_ReadSysInfo(fPilotSocket, &sys_info) < 0)
+ {
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
+ i18n("Unable to read system information from Pilot")));
+
+ link()->fLinkStatus=PilotLinkError;
+ reset();
+ return;
+ }
+ else
+ {
+ DEBUGKPILOT << fname << ": dlp_ReadSysInfo successful..." << endl;
+
+ KPILOT_DELETE(link()->fPilotSysInfo);
+ link()->fPilotSysInfo = new KPilotSysInfo(&sys_info);
+ DEBUGKPILOT << fname
+ << ": RomVersion: [" << link()->fPilotSysInfo->getRomVersion()
+ << "] Locale: [" << link()->fPilotSysInfo->getLocale()
+ << "] Product: [" << link()->fPilotSysInfo->getProductID()
+ << "]" << endl;
+ }
+
+ // If we've made it this far, make sure our USB workaround timer doesn't fire!
+ fWorkaroundUSBTimer->stop();
+ KPILOT_DELETE(fWorkaroundUSBTimer);
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, QString::null, 60));
+
+ KPILOT_DELETE(link()->fPilotUser);
+ link()->fPilotUser = new KPilotUser;
+
+ DEBUGKPILOT << fname << ": doing dlp_ReadUserInfo..." << endl;
+
+ /* Ask the pilot who it is. And see if it's who we think it is. */
+ dlp_ReadUserInfo(fPilotSocket, link()->fPilotUser->data());
+
+ QString n = link()->getPilotUser().name();
+ DEBUGKPILOT << fname
+ << ": Read user name: [" << n << "]" << endl;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, i18n("Checking last PC..."), 90));
+
+ /* Tell user (via Pilot) that we are starting things up */
+ if ((ret=dlp_OpenConduit(fPilotSocket)) < 0)
+ {
+ DEBUGKPILOT << fname
+ << ": dlp_OpenConduit returned: [" << ret << "]" << endl;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
+ i18n("Could not read user information from the Pilot. "
+ "Perhaps you have a password set on the device?")));
+
+ }
+ link()->fLinkStatus = AcceptedDevice;
+
+ QApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, QString::null, 100));
+
+ DeviceCommEvent * ev = new DeviceCommEvent(EventDeviceReady);
+ ev->setCurrentSocket(fPilotSocket);
+ QApplication::postEvent(link(), ev);
+
+}
+
+void DeviceCommThread::workaroundUSB()
+{
+ FUNCTIONSETUP;
+
+ reset();
+}
+
+void DeviceCommThread::run()
+{
+ FUNCTIONSETUP;
+ fDone = false;
+
+ startOpenTimer(this, fOpenTimer);
+
+ int sleepBetweenPoll = 2;
+ // keep the thread alive until we're supposed to be done
+ while (!fDone)
+ {
+ QThread::sleep(sleepBetweenPoll);
+ }
+
+ close();
+ // now sleep one last bit to make sure the pthread inside
+ // pilot-link (potentially, if it's libusb) is done before we exit
+ QThread::sleep(1);
+
+ DEBUGKPILOT << fname << ": comm thread now done..." << endl;
+}
+
+KPilotDeviceLink::KPilotDeviceLink(QObject * parent, const char *name,
+ const QString &tempDevice) :
+ KPilotLink(parent, name), fLinkStatus(Init), fWorkaroundUSB(false),
+ fPilotSocket(-1), fTempDevice(tempDevice), fMessages(new Messages(this)), fDeviceCommThread(0L)
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname
+ << ": Pilot-link version: [" << PILOT_LINK_NUMBER
+ << "]" << endl;
+}
+
+KPilotDeviceLink::~KPilotDeviceLink()
+{
+ FUNCTIONSETUP;
+ close();
+ KPILOT_DELETE(fPilotSysInfo);
+ KPILOT_DELETE(fPilotUser);
+ KPILOT_DELETE(fMessages);
+}
+
+/* virtual */bool KPilotDeviceLink::isConnected() const
+{
+ return fLinkStatus == AcceptedDevice;
+}
+
+/* virtual */bool KPilotDeviceLink::event(QEvent *e)
+{
+ FUNCTIONSETUP;
+
+ bool handled = false;
+
+ if ((int)e->type() == EventDeviceReady)
+ {
+ DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
+ fPilotSocket = t->currentSocket();
+ emit deviceReady( this);
+ handled = true;
+ }
+ else if ((int)e->type() == EventLogMessage)
+ {
+ DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
+ emit logMessage(t->message());
+ handled = true;
+ }
+ else if ((int)e->type() == EventLogError)
+ {
+ DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
+ emit logError(t->message());
+ handled = true;
+ }
+ else if ((int)e->type() == EventLogProgress)
+ {
+ DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
+ emit logProgress(t->message(), t->progress());
+ handled = true;
+ }
+ else
+ {
+ handled = KPilotLink::event(e);
+ }
+
+ return handled;
+}
+
+void KPilotDeviceLink::stopCommThread()
+{
+ FUNCTIONSETUP;
+ if (fDeviceCommThread)
+ {
+ fDeviceCommThread->setDone(true);
+
+ // try to wait for our thread to finish, but don't
+ // block the main thread forever
+ if (fDeviceCommThread->running())
+ {
+ DEBUGKPILOT << fname
+ << ": comm thread still running. "
+ << "waiting for it to complete." << endl;
+ bool done = fDeviceCommThread->wait(5000);
+ if (!done)
+ {
+ DEBUGKPILOT << fname
+ << ": comm thread still running "
+ << "after wait(). "
+ << "going to have to terminate it."
+ << endl;
+ // not normally to be done, but we must make sure
+ // that this device doesn't come back alive
+ fDeviceCommThread->terminate();
+ fDeviceCommThread->wait();
+ }
+ }
+
+ fDeviceCommThread->close();
+
+ KPILOT_DELETE(fDeviceCommThread);
+ }
+}
+
+void KPilotDeviceLink::close()
+{
+ FUNCTIONSETUP;
+
+ stopCommThread();
+
+ fPilotSocket = (-1);
+}
+
+void KPilotDeviceLink::reset(const QString & dP)
+{
+ FUNCTIONSETUP;
+
+ fLinkStatus = Init;
+
+ // Release all resources
+ //
+ close();
+ fPilotPath = QString::null;
+
+ fPilotPath = dP;
+ if (fPilotPath.isEmpty())
+ fPilotPath = fTempDevice;
+ if (fPilotPath.isEmpty())
+ return;
+
+ reset();
+}
+
+void KPilotDeviceLink::startCommThread()
+{
+ FUNCTIONSETUP;
+
+ stopCommThread();
+
+ if (fTempDevice.isEmpty() && pilotPath().isEmpty())
+ {
+ WARNINGKPILOT << "No point in trying empty device."
+ << endl;
+
+ QString msg = i18n("The Pilot device is not configured yet.");
+ WARNINGKPILOT << msg << endl;
+
+ fLinkStatus = PilotLinkError;
+
+ emit logError(msg);
+ return;
+ }
+
+ fDeviceCommThread = new DeviceCommThread(this);
+ fDeviceCommThread->start();
+}
+
+void KPilotDeviceLink::reset()
+{
+ FUNCTIONSETUP;
+
+ fMessages->reset();
+ close();
+
+ checkDevice();
+
+ fLinkStatus = WaitingForDevice;
+
+ startCommThread();
+}
+
+void KPilotDeviceLink::checkDevice()
+{
+ // If the device exists yet doesn't have the right
+ // permissions, complain and then continue anyway.
+ //
+ QFileInfo fi(fPilotPath);
+ if (fi.exists())
+ {
+ // If it exists, it ought to be RW already.
+ //
+ if (!(fi.isReadable() && fi.isWritable()))
+ {
+ emit logError(i18n("Pilot device %1 is not read-write.")
+ .arg(fPilotPath));
+ }
+ }
+ else
+ {
+ // It doesn't exist, mention this in the log
+ // (relevant as long as we use only one device type)
+ //
+ emit
+ logError(i18n("Pilot device %1 does not exist. "
+ "Probably it is a USB device and will appear during a HotSync.")
+ .arg(fPilotPath));
+ // Suppress all normal and error messages about opening the device.
+ fMessages->block(Messages::OpenMessage | Messages::OpenFailMessage,
+ true);
+ }
+}
+
+void KPilotDeviceLink::setTempDevice(const QString &d)
+{
+ fTempDevice = d;
+ DeviceMap::self()->bindDevice(fTempDevice);
+}
+
+/* virtual */bool KPilotDeviceLink::tickle()
+{
+ // No FUNCTIONSETUP here because it may be called from
+ // a separate thread.
+ return pi_tickle(pilotSocket()) >= 0;
+}
+
+/* virtual */void KPilotDeviceLink::addSyncLogEntryImpl(const QString &entry)
+{
+ dlp_AddSyncLogEntry(fPilotSocket,
+ const_cast<char *>((const char *)Pilot::toPilot(entry)));
+}
+
+bool KPilotDeviceLink::installFile(const QString & f, const bool deleteFile)
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname << ": Installing file " << f << endl;
+
+ if (!QFile::exists(f))
+ return false;
+
+ char buffer[PATH_MAX];
+ memset(buffer, 0, PATH_MAX);
+ strlcpy(buffer, QFile::encodeName(f), PATH_MAX);
+ struct pi_file *pf = pi_file_open(buffer);
+
+ if (!f)
+ {
+ WARNINGKPILOT << "Cannot open file " << f << endl;
+ emit logError(i18n
+ ("<qt>Cannot install the file &quot;%1&quot;.</qt>").
+ arg(f));
+ return false;
+ }
+
+ if (pi_file_install(pf, fPilotSocket, 0, 0L) < 0)
+ {
+ WARNINGKPILOT << "Cannot pi_file_install " << f << endl;
+ emit logError(i18n
+ ("<qt>Cannot install the file &quot;%1&quot;.</qt>").
+ arg(f));
+ return false;
+ }
+
+ pi_file_close(pf);
+ if (deleteFile)
+ QFile::remove(f);
+
+ return true;
+}
+
+int KPilotDeviceLink::openConduit()
+{
+ return dlp_OpenConduit(fPilotSocket);
+}
+
+QString KPilotDeviceLink::statusString(LinkStatus l)
+{
+ QString s= CSL1("KPilotDeviceLink=");
+
+ switch (l)
+ {
+ case Init:
+ s.append(CSL1("Init"));
+ break;
+ case WaitingForDevice:
+ s.append(CSL1("WaitingForDevice"));
+ break;
+ case FoundDevice:
+ s.append(CSL1("FoundDevice"));
+ break;
+ case CreatedSocket:
+ s.append(CSL1("CreatedSocket"));
+ break;
+ case DeviceOpen:
+ s.append(CSL1("DeviceOpen"));
+ break;
+ case AcceptedDevice:
+ s.append(CSL1("AcceptedDevice"));
+ break;
+ case SyncDone:
+ s.append(CSL1("SyncDone"));
+ break;
+ case PilotLinkError:
+ s.append(CSL1("PilotLinkError"));
+ break;
+ case WorkaroundUSB:
+ s.append(CSL1("WorkaroundUSB"));
+ break;
+ }
+
+ return s;
+}
+
+QString KPilotDeviceLink::statusString() const
+{
+ return statusString(status() );
+}
+
+void KPilotDeviceLink::endSync(EndOfSyncFlags f)
+{
+ FUNCTIONSETUP;
+
+ if (UpdateUserInfo == f)
+ {
+ getPilotUser().setLastSyncPC((unsigned long) gethostid());
+ getPilotUser().setLastSyncDate(time(0));
+
+ DEBUGKPILOT << fname << ": Writing username " << getPilotUser().name() << endl;
+
+ dlp_WriteUserInfo(pilotSocket(), getPilotUser().data());
+ addSyncLogEntry(i18n("End of HotSync\n"));
+ }
+ dlp_EndOfSync(pilotSocket(), 0);
+ KPILOT_DELETE(fPilotSysInfo);
+ KPILOT_DELETE(fPilotUser);
+}
+
+int KPilotDeviceLink::getNextDatabase(int index, struct DBInfo *dbinfo)
+{
+ FUNCTIONSETUP;
+
+ pi_buffer_t buf = { 0, 0, 0 };
+ int r = dlp_ReadDBList(pilotSocket(), 0, dlpDBListRAM, index, &buf);
+ if (r >= 0)
+ {
+ memcpy(dbinfo, buf.data, sizeof(struct DBInfo));
+ }
+ return r;
+}
+
+// Find a database with the given name. Info about the DB is stored into dbinfo (e.g. to be used later on with retrieveDatabase).
+int KPilotDeviceLink::findDatabase(const char *name, struct DBInfo *dbinfo,
+ int index, unsigned long type, unsigned long creator)
+{
+ FUNCTIONSETUP;
+ return dlp_FindDBInfo(pilotSocket(), 0, index, const_cast<char *>(name),
+ type, creator, dbinfo);
+}
+
+bool KPilotDeviceLink::retrieveDatabase(const QString &fullBackupName,
+ DBInfo *info)
+{
+ FUNCTIONSETUP;
+
+ if (fullBackupName.isEmpty() || !info)
+ {
+ // Don't even bother trying to convert or retrieve.
+ return false;
+ }
+
+ DEBUGKPILOT << fname << ": Writing DB <" << info->name << "> "
+ << " to " << fullBackupName << endl;
+
+ QCString encodedName = QFile::encodeName(fullBackupName);
+ struct pi_file *f = pi_file_create(encodedName, info);
+
+ if (!f)
+ {
+ WARNINGKPILOT << "Failed, unable to create file" << endl;
+ return false;
+ }
+
+ if (pi_file_retrieve(f, pilotSocket(), 0, 0L) < 0)
+ {
+ WARNINGKPILOT << "Failed, unable to back up database" << endl;
+
+ pi_file_close(f);
+ return false;
+ }
+
+ pi_file_close(f);
+ return true;
+}
+
+KPilotLink::DBInfoList KPilotDeviceLink::getDBList(int cardno, int flags)
+{
+ bool cont=true;
+ DBInfoList dbs;
+ int index=0;
+ while (cont)
+ {
+ pi_buffer_t buf = { 0, 0, 0 };
+ pi_buffer_clear(&buf);
+ // DBInfo*dbi=new DBInfo();
+ if (dlp_ReadDBList(pilotSocket(), cardno, flags | dlpDBListMultiple,
+ index, &buf)<0)
+ {
+ cont=false;
+ }
+ else
+ {
+ DBInfo db_n;
+ DBInfo *db_it = (DBInfo *)buf.data;
+ int info_count = buf.used / sizeof(struct DBInfo);
+
+ while (info_count>0)
+ {
+ memcpy(&db_n, db_it, sizeof(struct DBInfo));
+ ++db_it;
+ info_count--;
+ dbs.append(db_n);
+ }
+ index=db_n.index+1;
+ }
+ }
+ return dbs;
+}
+
+const KPilotCard *KPilotDeviceLink::getCardInfo(int card)
+{
+ KPilotCard *cardinfo=new KPilotCard();
+ if (dlp_ReadStorageInfo(pilotSocket(), card, cardinfo->cardInfo())<0)
+ {
+ WARNINGKPILOT << "Could not get info for card " << card << endl;
+
+ KPILOT_DELETE(cardinfo);
+ return 0L;
+ }
+ return cardinfo;
+}
+
+PilotDatabase *KPilotDeviceLink::database(const QString &name)
+{
+ return new PilotSerialDatabase( this, name );
+}
+
+PilotDatabase *KPilotDeviceLink::database(const DBInfo *info)
+{
+ return new PilotSerialDatabase( this, info );
+}
+
diff --git a/kpilot/lib/kpilotdevicelink.h b/kpilot/lib/kpilotdevicelink.h
new file mode 100644
index 000000000..d2527aee4
--- /dev/null
+++ b/kpilot/lib/kpilotdevicelink.h
@@ -0,0 +1,220 @@
+#ifndef _KPILOT_KPILOTDEVICELINK_H
+#define _KPILOT_KPILOTDEVICELINK_H
+/*
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "kpilotlink.h"
+
+class QThread;
+
+class DeviceMap; ///< Globally tracks all devices that have a link assigned
+class Messages; ///< Tracks which messages have been printed
+class DeviceCommThread; ///< Thread for doing all palm device communications
+
+/**
+* The link behaves like a state machine most of the time:
+* it waits for the actual device to become available, and
+* then becomes ready to handle syncing.
+*/
+enum LinkStatus {
+ Init,
+ WaitingForDevice,
+ FoundDevice,
+ CreatedSocket,
+ DeviceOpen,
+ AcceptedDevice,
+ SyncDone,
+ PilotLinkError,
+ WorkaroundUSB
+} ;
+
+/**
+* Custom events we can be handling...
+*/
+enum DeviceCustomEvents {
+ EventLogMessage = QEvent::User + 777,
+ EventLogError,
+ EventLogProgress,
+ EventDeviceReady
+};
+
+/**
+* Definition of the device link class for physical
+* handheld devices, which communicate with the PC
+* using DLP / SLP via the pilot-link library.
+*/
+class KDE_EXPORT KPilotDeviceLink : public KPilotLink
+{
+friend class PilotSerialDatabase;
+friend class DeviceCommThread;
+
+Q_OBJECT
+
+public:
+ /**
+ * Constructor. Creates a link that can sync to a physical handheld.
+ * Call reset() on it to start looking for a device.
+ *
+ * @param parent Parent object.
+ * @param name Name of this object.
+ * @param tempDevice Path to device node to use as an alternative
+ * to the "normal" one set by KPilot.
+ */
+ KPilotDeviceLink( QObject *parent = 0,
+ const char *name = 0,
+ const QString &tempDevice = QString::null );
+
+ /**
+ * Destructor. This rudely ends the communication with the handheld.
+ * It is best to call endOfSync() or finishSync() before destroying
+ * the device.
+ */
+ virtual ~KPilotDeviceLink();
+
+ /**
+ * Get the status (state enum) of this link.
+ * @return The LinkStatus enum for the link's current state.
+ */
+ LinkStatus status() const
+ {
+ return fLinkStatus;
+ }
+
+ /** Get a human-readable string for the given status @p l. */
+ static QString statusString(LinkStatus l);
+
+ // The following API is the actual implementation of
+ // the KPilotLink API, for documentation see that file.
+ //
+ virtual QString statusString() const;
+ virtual bool isConnected() const;
+ virtual void reset( const QString & );
+ virtual void close();
+ virtual void reset();
+ virtual bool event(QEvent *e);
+ virtual bool tickle();
+ virtual const KPilotCard *getCardInfo(int card);
+ virtual void endSync( EndOfSyncFlags f );
+ virtual int openConduit();
+ virtual int getNextDatabase(int index,struct DBInfo *);
+ virtual int findDatabase(const char *name, struct DBInfo*,
+ int index=0, unsigned long type=0, unsigned long creator=0);
+ virtual bool retrieveDatabase(const QString &path, struct DBInfo *db);
+ virtual DBInfoList getDBList(int cardno=0, int flags=dlpDBListRAM);
+ virtual PilotDatabase *database( const QString &name );
+ virtual PilotDatabase *database( const DBInfo *info );
+
+protected:
+ virtual bool installFile(const QString &, const bool deleteFile);
+ virtual void addSyncLogEntryImpl( const QString &s );
+ virtual int pilotSocket() const
+ {
+ return fPilotSocket;
+ }
+
+
+private:
+ LinkStatus fLinkStatus;
+
+
+public:
+
+ /**
+ * Special-cases. Call this after a reset to set device-
+ * specific workarounds; the only one currently known
+ * is the Zire 31/72 T5 quirk of doing a non-HotSync
+ * connect when it's switched on.
+ */
+ void setWorkarounds(bool usb)
+ {
+ fWorkaroundUSB = usb;
+ }
+
+ /**
+ * Sets an additional device, which should be tried as fallback.
+ * Useful for hotplug enviroments, this device is used @em once
+ * for accepting a connection.
+ */
+ void setTempDevice( const QString &device );
+
+ /**
+ * Sets the device to use. Used by probe dialog, since we know
+ * what device to use, but we don't want to start the detection
+ * immediately.
+ */
+ void setDevice( const QString &device )
+ {
+ fPilotPath = device;
+ }
+
+
+protected:
+ /** Should we work around the Zire31/72 quirk? @see setWorkarounds() */
+ bool fWorkaroundUSB;
+
+
+ /**
+ * Check for device permissions and existence, emitting
+ * warnings for weird situations. This is primarily intended
+ * to inform the user.
+ */
+ void checkDevice();
+
+protected:
+ /**
+ * Path with resolved symlinks, to prevent double binding
+ * to the same device.
+ */
+ QString fRealPilotPath;
+
+ /**
+ * Pilot-link library handles for the device once it's opened.
+ */
+ int fPilotSocket;
+ QString fTempDevice;
+
+ /**
+ * Handle cases where we can't accept or open the device,
+ * and data remains available on the pilot socket.
+ */
+ int fAcceptedCount;
+
+ /**
+ * Start/Stop our device communication thread.
+ */
+ void startCommThread();
+ void stopCommThread();
+
+protected:
+ Messages *fMessages;
+ DeviceCommThread *fDeviceCommThread;
+} ;
+
+#endif
+
diff --git a/kpilot/lib/kpilotdevicelinkPrivate.h b/kpilot/lib/kpilotdevicelinkPrivate.h
new file mode 100644
index 000000000..be2bbda35
--- /dev/null
+++ b/kpilot/lib/kpilotdevicelinkPrivate.h
@@ -0,0 +1,330 @@
+#ifndef _KPILOT_KPILOTDEVICELINKPRIVATE_H
+#define _KPILOT_KPILOTDEVICELINKPRIVATE_H
+/*
+**
+** Copyright (C) 2007 by Jason 'vanRijn' Kasper <vR@movingparts.net>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <errno.h>
+
+#include <qstringlist.h>
+#include <qthread.h>
+
+#include "kpilotdevicelink.h"
+#include "options.h"
+
+class QTimer;
+class QSocketNotifier;
+
+// singleton helper class
+class DeviceMap
+{
+public:
+ static DeviceMap *self()
+ {
+ if (!mThis)
+ mThis = new DeviceMap();
+ return mThis;
+ }
+
+ bool canBind(const QString &device)
+ {
+ FUNCTIONSETUPL(5);
+ DEBUGKPILOT << fname << ": device: ["
+ << device << "]" << endl;
+
+ showList();
+ return !mBoundDevices.contains(device);
+ }
+
+ void bindDevice(const QString &device)
+ {
+ FUNCTIONSETUPL(5);
+ DEBUGKPILOT << fname << ": device: ["
+ << device << "]" << endl;
+
+ mBoundDevices.append(device);
+ showList();
+ }
+
+ void unbindDevice(const QString &device)
+ {
+ FUNCTIONSETUPL(5);
+ DEBUGKPILOT << fname << ": device: ["
+ << device << "]" << endl;
+
+ mBoundDevices.remove(device);
+ showList();
+ }
+
+protected:
+ DeviceMap()
+ {
+ mBoundDevices.clear();
+ }
+ ~DeviceMap()
+ {
+ }
+
+ QStringList mBoundDevices;
+ static DeviceMap *mThis;
+
+private:
+ void showList() const
+ {
+ FUNCTIONSETUPL(5);
+
+ if ( !(mBoundDevices.count() > 0))
+ return;
+
+ DEBUGKPILOT << fname << ": Bound devices: ["
+ << ((mBoundDevices.count() > 0) ?
+ mBoundDevices.join(CSL1(", ")) : CSL1("<none>"))
+ << "]" << endl;
+ }
+};
+
+class Messages
+{
+public:
+ Messages(KPilotDeviceLink *parent) :
+ fDeviceLink(parent)
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ messages = 0;
+ messagesMask = ~messageIsError; // Never block errors
+ }
+
+ void block(unsigned int m, bool force=false)
+ {
+ if (force)
+ {
+ // Force blocking this message, even if it's an error msg.
+ messages |= m;
+ }
+ else
+ {
+ messages |= (m & messagesMask);
+ }
+ }
+
+ /**
+ * Some messages are only printed once and are suppressed
+ * after that. These are indicated by flag bits in
+ * messages. The following enum is a bitfield.
+ */
+ enum
+ {
+ OpenMessage=1, ///< Trying to open device ..
+ OpenFailMessage=2 ///< Failed to open device ...
+ };
+ int messages;
+ int messagesMask;
+ static const int messageIsError = 0;
+
+ /** Determines whether message @p s which has an id of @p msgid (one of
+ * the enum values mentioned above) should be printed, which is only if that
+ * message has not been suppressed through messagesMask.
+ * If return is true, this method also adds it to the messagesMask.
+ */
+ bool shouldPrint(int msgid)
+ {
+ if (!(messages & msgid))
+ {
+ block(msgid);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+protected:
+ KPilotDeviceLink *fDeviceLink;
+};
+
+class DeviceCommEvent : public QEvent
+{
+public:
+ DeviceCommEvent(DeviceCustomEvents type, QString msg = QString::null,
+ int progress = 0) :
+ QEvent( (QEvent::Type)type ), fMessage(msg), fProgress(progress),
+ fPilotSocket(-1)
+ {
+ }
+ QString message() const
+ {
+ return fMessage;
+ }
+ int progress()
+ {
+ return fProgress;
+ }
+
+ inline void setCurrentSocket(int i)
+ {
+ fPilotSocket = i;
+ }
+
+ inline int currentSocket()
+ {
+ return fPilotSocket;
+ }
+private:
+ QString fMessage;
+ int fProgress;
+ /**
+ * Pilot-link library handles for the device once it's opened.
+ */
+ int fPilotSocket;
+};
+
+/** Class that handles all device communications. We do this
+ in a different thread so that we do not block the main Qt
+ Event thread (similar to Swing's AWT event dispatch thread).
+ */
+
+class DeviceCommThread : public QObject, public QThread
+{
+friend class KPilotDeviceLink;
+
+Q_OBJECT
+
+public:
+ DeviceCommThread(KPilotDeviceLink *d);
+ virtual ~DeviceCommThread();
+
+ virtual void run();
+
+ void setDone(bool b)
+ {
+ FUNCTIONSETUP;
+ fDone = b;
+ }
+
+protected:
+
+ void close();
+
+ void reset();
+
+ /**
+ * Does the low-level opening of the device and handles the
+ * pilot-link library initialisation.
+ */
+ bool open(const QString &device = QString::null);
+
+protected slots:
+ /**
+ * Attempt to open the device. Called regularly to check
+ * if the device exists (to handle USB-style devices).
+ */
+ void openDevice();
+
+ /**
+ * Called when the device is opened *and* activity occurs on the
+ * device. This indicates the beginning of a hotsync.
+ */
+ void acceptDevice();
+
+ /**
+ * This slot fires whenever we've been trying to establish a hotsync with
+ * the device for longer than a given amount of time. When this slot is
+ * fired, we will tear down the communications process and start over again.
+ */
+ void workaroundUSB();
+
+private:
+ volatile bool fDone;
+
+ KPilotDeviceLink *fHandle;
+ inline KPilotDeviceLink *link()
+ {
+ if (fHandle)
+ {
+ return fHandle;
+ }
+ else
+ {
+ FUNCTIONSETUP;
+ WARNINGKPILOT << "Link asked for, but either I'm "
+ << "done or I don't have a valid handle. "
+ << "Shutting down comm thread." << endl;
+ QThread::exit();
+ return 0;
+ }
+ }
+
+ /**
+ * Timers and Notifiers for detecting activity on the device.
+ */
+ QTimer *fOpenTimer;
+ QSocketNotifier *fSocketNotifier;
+ bool fSocketNotifierActive;
+
+ /** Timer used to check for a badly-connected Z31/72 */
+ QTimer *fWorkaroundUSBTimer;
+
+ /**
+ * Pilot-link library handles for the device once it's opened.
+ */
+ int fPilotSocket;
+ int fTempSocket;
+
+ inline QString errorMessage(int e)
+ {
+ switch (e)
+ {
+ case ENOENT:
+ return i18n(" The port does not exist.");
+ break;
+ case ENODEV:
+ return i18n(" There is no such device.");
+ break;
+ case EPERM:
+ return i18n(" You do not have permission to open the "
+ "Pilot device.");
+ break;
+ default:
+ return i18n(" Check Pilot path and permissions.");
+ }
+ }
+
+ /**
+ * Handle cases where we can't accept or open the device,
+ * and data remains available on the pilot socket.
+ */
+ int fAcceptedCount;
+
+};
+
+
+#endif
+
diff --git a/kpilot/lib/kpilotlib.kcfg b/kpilot/lib/kpilotlib.kcfg
new file mode 100644
index 000000000..09e829216
--- /dev/null
+++ b/kpilot/lib/kpilotlib.kcfg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kpilotrc"/>
+ <group name="Notification Messages">
+ </group>
+</kcfg>
diff --git a/kpilot/lib/kpilotlibSettings.kcfgc b/kpilot/lib/kpilotlibSettings.kcfgc
new file mode 100644
index 000000000..d3b0d1352
--- /dev/null
+++ b/kpilot/lib/kpilotlibSettings.kcfgc
@@ -0,0 +1,7 @@
+File=kpilotlib.kcfg
+ClassName=KPilotLibSettings
+Singleton=true
+ItemAccessors=true
+Mutators=true
+GlobalEnums=true
+SetUserTexts=true
diff --git a/kpilot/lib/kpilotlink.cc b/kpilot/lib/kpilotlink.cc
new file mode 100644
index 000000000..9c0b85ee9
--- /dev/null
+++ b/kpilot/lib/kpilotlink.cc
@@ -0,0 +1,272 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006-2007 Adriaan de Groot <groot@kde.org>
+** Copyright (C) 2007 Jason 'vanRijn' Kasper <vR@movingparts.net>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <iostream>
+
+#include <pi-source.h>
+#include <pi-socket.h>
+#include <pi-dlp.h>
+#include <pi-file.h>
+#include <pi-buffer.h>
+
+#include <qdir.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qthread.h>
+
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include "pilotUser.h"
+#include "pilotSysInfo.h"
+#include "pilotCard.h"
+#include "pilotSerialDatabase.h"
+#include "pilotLocalDatabase.h"
+
+#include "kpilotlink.moc"
+
+/** Class that handles periodically tickling the handheld through
+* the virtual tickle() method; deals with cancels through the
+* shared fDone variable.
+*/
+class TickleThread : public QThread
+{
+public:
+ TickleThread(KPilotLink *d, bool *done, int timeout) :
+ QThread(),
+ fHandle(d),
+ fDone(done),
+ fTimeout(timeout)
+ { };
+ virtual ~TickleThread();
+
+ virtual void run();
+
+ static const int ChecksPerSecond = 5;
+ static const int SecondsPerTickle = 5;
+
+private:
+ KPilotLink *fHandle;
+ bool *fDone;
+ int fTimeout;
+} ;
+
+TickleThread::~TickleThread()
+{
+}
+
+void TickleThread::run()
+{
+ FUNCTIONSETUP;
+ int subseconds = ChecksPerSecond;
+ int ticktock = SecondsPerTickle;
+ int timeout = fTimeout;
+ DEBUGKPILOT << fname << ": Running for "
+ << timeout << " seconds." << endl;
+ DEBUGKPILOT << fname << ": Done @" << (void *) fDone << endl;
+
+ while (!(*fDone))
+ {
+ QThread::msleep(1000/ChecksPerSecond);
+ if (!(--subseconds))
+ {
+ if (timeout)
+ {
+ if (!(--timeout))
+ {
+ QApplication::postEvent(fHandle, new QEvent(static_cast<QEvent::Type>(KPilotLink::EventTickleTimeout)));
+ break;
+ }
+ }
+ subseconds=ChecksPerSecond;
+ if (!(--ticktock))
+ {
+ ticktock=SecondsPerTickle;
+ fHandle->tickle();
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+KPilotLink::KPilotLink( QObject *parent, const char *name ) :
+ QObject( parent, name ),
+ fPilotPath(QString::null),
+ fPilotUser(0L),
+ fPilotSysInfo(0L),
+ fTickleDone(true),
+ fTickleThread(0L)
+
+{
+ FUNCTIONSETUP;
+
+ fPilotUser = new KPilotUser();
+ strncpy( fPilotUser->data()->username, "Henk Westbroek",
+ sizeof(fPilotUser->data()->username)-1);
+ fPilotUser->setLastSuccessfulSyncDate( 1139171019 );
+
+ fPilotSysInfo = new KPilotSysInfo();
+ memset(fPilotSysInfo->sysInfo()->prodID, 0,
+ sizeof(fPilotSysInfo->sysInfo()->prodID));
+ strncpy(fPilotSysInfo->sysInfo()->prodID, "LocalLink",
+ sizeof(fPilotSysInfo->sysInfo()->prodID)-1);
+ fPilotSysInfo->sysInfo()->prodIDLength =
+ strlen(fPilotSysInfo->sysInfo()->prodID);
+}
+
+KPilotLink::~KPilotLink()
+{
+ FUNCTIONSETUP;
+ KPILOT_DELETE(fPilotUser);
+ KPILOT_DELETE(fPilotSysInfo);
+}
+
+/* virtual */ bool KPilotLink::event(QEvent *e)
+{
+ if ((int)e->type() == EventTickleTimeout)
+ {
+ stopTickle();
+ emit timeout();
+ return true;
+ }
+ else return QObject::event(e);
+}
+
+/*
+Start a tickle thread with the indicated timeout.
+*/
+void KPilotLink::startTickle(unsigned int timeout)
+{
+ FUNCTIONSETUP;
+
+ Q_ASSERT(fTickleDone);
+
+ /*
+ ** We've told the thread to finish up, but it hasn't
+ ** done so yet - so wait for it to do so, should be
+ ** only 200ms at most.
+ */
+ if (fTickleDone && fTickleThread)
+ {
+ fTickleThread->wait();
+ KPILOT_DELETE(fTickleThread);
+ }
+
+ DEBUGKPILOT << fname << ": Done @" << (void *) (&fTickleDone) << endl;
+
+ fTickleDone = false;
+ fTickleThread = new TickleThread(this,&fTickleDone,timeout);
+ fTickleThread->start();
+}
+
+void KPilotLink::stopTickle()
+{
+ FUNCTIONSETUP;
+ fTickleDone = true;
+ if (fTickleThread)
+ {
+ fTickleThread->wait();
+ KPILOT_DELETE(fTickleThread);
+ }
+}
+
+unsigned int KPilotLink::installFiles(const QStringList & l, const bool deleteFiles)
+{
+ FUNCTIONSETUP;
+
+ QStringList::ConstIterator i,e;
+ unsigned int k = 0;
+ unsigned int n = 0;
+ unsigned int total = l.count();
+
+ for (i = l.begin(), e = l.end(); i != e; ++i)
+ {
+ emit logProgress(QString::null,
+ (int) ((100.0 / total) * (float) n));
+
+ if (installFile(*i, deleteFiles))
+ k++;
+ n++;
+ }
+ emit logProgress(QString::null, 100);
+
+ return k;
+}
+
+void KPilotLink::addSyncLogEntry(const QString & entry, bool log)
+{
+ FUNCTIONSETUP;
+ if (entry.isEmpty()) return;
+
+ addSyncLogEntryImpl(entry);
+ if (log)
+ {
+ emit logMessage(entry);
+ }
+}
+
+
+/* virtual */ int KPilotLink::openConduit()
+{
+ return 0;
+}
+
+/* virtual */ int KPilotLink::pilotSocket() const
+{
+ return -1;
+}
+
+/* virtual */ PilotDatabase *KPilotLink::database( const DBInfo *info )
+{
+ FUNCTIONSETUP;
+ return database( Pilot::fromPilot( info->name ) );
+}
+
diff --git a/kpilot/lib/kpilotlink.h b/kpilot/lib/kpilotlink.h
new file mode 100644
index 000000000..5c3865c3e
--- /dev/null
+++ b/kpilot/lib/kpilotlink.h
@@ -0,0 +1,501 @@
+#ifndef _KPILOT_KPILOTLINK_H
+#define _KPILOT_KPILOTLINK_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <pi-dlp.h>
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+/** @file
+* Encapsulates all the communication with the handheld. Also
+* does daemon-like polling of the handheld. Interesting status
+* changes are signalled.
+*/
+
+class QThread;
+class KPilotUser;
+class KPilotSysInfo;
+class KPilotCard;
+class PilotDatabase;
+
+
+
+/**
+* KPilotLink handles some aspects of
+* communication with a Handheld. A KPilotLink object represents a
+* connection to a device (which may be active or inactive -- the latter in
+* cases where the link is @e waiting for a device to show up). The object
+* handles waiting, protocol initialization and some general
+* tasks such as getting system information or user data.
+*
+* The actual communication with the handheld should use the
+* PilotDatabase methods or use pilot-link dlp_* functions directly
+* on the file descriptor returned by handle().
+*
+* Implementations of this abstract class are KPilotDeviceLink
+* (for real physical devices) and KPilotLocalLink (for devices
+* represented by an on-disk directory).
+*
+*
+* @section General
+*
+* A KPilotLink object (or one of its subclasses) represents a single
+* (potential) link to a handheld device. The handheld device may be
+* a real physical one (subclass KPilotDeviceLink) or a virtual one
+* (subclass KPilotLocalLink). Every KPilotLink is associated with exactly
+* one identifier for @em what device it is attached to. Physical devices
+* have physical locations as interpreted by libpisock -- /dev/ttyUSB0 for
+* instance, or net:any -- while virtual devices are associated with a location
+* in the filesystem.
+*
+* A particular KPilotLink object may be connected -- communicating with
+* a device -- or not. For physical devices, that means that the device is
+* attached to the system (for USB-connected devices, think of it as a
+* metaphor in the case of net:any) and that the HotSync button has been
+* pressed. Virtual devices are immediately connected on creation, since there
+* is no sensible "not connected" state. A connected KPilotLink has access to the
+* data on the handheld and can give that data to the rest of the application.
+*
+* The data access API is divided into roughly three parts, with tickle handling
+* being a special fourth part (see section below). These are:
+*
+* - Message logging
+* - System information access
+* - Database access
+*
+* @section Lifecycle
+*
+* The life-cycle of a KPilotLink object is as follows:
+*
+* # Object is created (one of the concrete subclasses, anyway)
+* # Object gets a location assigned through reset(const QString &)
+* # Object is connected to the handheld device (somehow, depends on subclass)
+* # Object emits signal deviceReady()
+*
+* After this, the application is free to use the API to access the information from
+* the handheld. When the device connection is no longer needed, call either
+* endOfSync() or finishSync() to wrap up the communications. The object remains
+* alive and may be re-used by calling reset() to use the same location or
+* reset(const QString &) to give it a new location.
+*
+* @section Tickle handling.
+*
+* During a HotSync, the Pilot expects to be kept awake by (nearly)
+* continuous communication with the PC. The Pilot doesn't like
+* long periods of inactivity, since they drain the batteries while
+* the communications hardware is kept powered up. If the period of
+* inactivity is too long, the Pilot times out, shuts down the
+* communication, and the HotSync is broken.
+
+* Sometimes, however, periods of inactivity cannot be avoided --
+* for instance, if you _have_ to ask the user something during a
+* sync, or if you are fetching a large amount of data from a slow
+* source (libkabc can do that, if your addressbook is on an LDAP
+* server). During these periods of inactivity (as far as the Pilot
+* can tell), you can "tickle" the Pilot to keep it awake. This
+* prevents the communications from being shut down. It's not
+* a good idea to do this all the time -- battery life and possible
+* corruption of the dlp_ communications streams. Hence, you should
+* start and stop tickling the Pilot around any computation which:
+* - may take a long time
+* - does not in itself @em ever communicate directly with the Pilot
+*
+*
+*
+* You can call slot tickle() whenever you like just to do a
+* dlp_tickle() call on the Pilot. It will return true if the
+* tickle was successful, false otherwise (this can be used to
+* detect if the communication with the Pilot has shut down for
+* some reason).
+*
+* The protected methods startTickle() and stopTickle() are intended
+* to be called only from SyncActions -- I can't think of any other
+* legitimate use, since everything being done during a HotSync is
+* done via subclasses of SyncActions anyway, and SyncAction provides
+* access to these methods though its own start- and stopTickle().
+*
+* Call startTickle with a timeout in seconds, or 0 for no timeout.
+* This timeout is _unrelated_ to the timeout in the Pilot's
+* communications. Instead, it indicates how long to continue
+* tickling the Pilot before emitting the timeout() signal. This
+* can be useful for placing an upper bound on the amount of
+* time to wait for, say, user interaction -- you don't want an
+* inattentive user to drain the batteries during a sync because
+* he doesn't click on "Yes" for some question. If you pass a
+* timeout of 0, the Pilot will continue to be tickled until you
+* call stopTickle().
+*
+* Call stopTickle() to stop tickling the Pilot and continue with
+* normal operation. You @em must call stopTickle() before calling
+* anything else that might communicate with the Pilot, to avoid
+* corrupting the dlp_ communications stream. (TODO: Mutex the heck
+* out of this to avoid this problem). Note that stopTickle() may
+* hang up the caller for a small amount of time (up to 200ms)
+* before returning.
+*
+* event() and TickleTimeoutEvent are part of the implementation
+* of tickling, and are only accidentally visible.
+*
+* Signal timeout() is emitted if startTickle() has been called
+* with a non-zero timeout and that timeout has elapsed. The
+* tickler is stopped before timeout is emitted.
+*/
+class KDE_EXPORT KPilotLink : public QObject
+{
+Q_OBJECT
+friend class SyncAction;
+public:
+ /** A list of DBInfo structures. */
+ typedef QValueList<struct DBInfo> DBInfoList;
+
+ /** Constructor. Use reset() to start looking for a device. */
+ KPilotLink( QObject *parent = 0, const char *name = 0 );
+
+ /** Destructor. This rudely interrupts any communication in progress.
+ * It is best to call endOfSync() or finishSync() before destroying
+ * the device.
+ */
+ virtual ~KPilotLink();
+
+
+ /** Provides a human-readable status string. */
+ virtual QString statusString() const = 0;
+
+ /**
+ * True if HotSync has been started but not finished yet
+ * (ie. the physical Pilot is waiting for sync commands)
+ */
+ virtual bool isConnected() const = 0;
+
+
+ /**
+ * Information on what kind of device we're dealing with.
+ * A link is associated with a path -- either the node in
+ * /dev that the physical device is attached to, or an
+ * IP address, or a filesystem path for local links.
+ * Whichever is being used, this function returns its
+ * name in a human-readable form.
+ */
+ QString pilotPath() const
+ {
+ return fPilotPath;
+ }
+
+ /**
+ * Return the device link to the Init state and try connecting
+ * to the given device path (if it's non-empty). What the
+ * path means depends on the kind of link we're instantiating.
+ *
+ * @see reset()
+ * @see pilotPath()
+ */
+ virtual void reset(const QString &pilotPath) = 0;
+
+ /** Allows our class to receive custom events that our threads
+ * will be giving to us, including tickle timeouts and
+ * device communication events.
+ */
+ virtual bool event(QEvent *e);
+
+ /**
+ * Install the list of files (full paths!) named by @p l
+ * onto the handheld (or whatever this link represents).
+ * If @p deleteFiles is true, the source files are removed.
+ *
+ * @return the number of files successfully installed.
+ */
+ unsigned int installFiles(const QStringList &l, const bool deleteFiles);
+
+ /**
+ * Write a log entry to the handheld. If @p log is true,
+ * then the signal logMessage() is also emitted. This
+ * function is supposed to @em only write to the handheld's
+ * log (with a physical device, that is what appears on
+ * screen at the end of a sync).
+ */
+ void addSyncLogEntry(const QString &entry,bool log=true);
+
+ /**
+ * Find a database with the given @p name (and optionally,
+ * type @p type and creator ID (from pi_mktag) @p creator,
+ * on searching from index @p index on the handheld.
+ * Fills in the DBInfo structure @p info if found.
+ *
+ * @return >=0 on success. See the documentation for each
+ * subclass for particular meanings.
+ * @return < 0 on error.
+ */
+ virtual int findDatabase(const char *name,
+ struct DBInfo *info,
+ int index=0,
+ unsigned long type=0,
+ unsigned long creator=0) = 0;
+
+ /**
+ * Retrieve the database indicated by DBInfo @p *db into the
+ * local file @p path. This copies all the data, and you can
+ * create a PilotLocalDatabase from the resulting @p path .
+ *
+ * @return @c true on success
+ */
+ virtual bool retrieveDatabase(const QString &path, struct DBInfo *db) = 0;
+
+ /**
+ * Fill the DBInfo structure @p db with information about
+ * the next database (in some ordering) counting from
+ * @p index.
+ *
+ * @return < 0 on error
+ */
+ virtual int getNextDatabase(int index,struct DBInfo *db) = 0;
+
+ /**
+ * Returns a list of DBInfo structures describing all the
+ * databases available on the link (ie. device) with the
+ * given card number @p cardno and flags @p flags. No known
+ * handheld uses a cardno other than 0; use flags to
+ * indicate what kind of databases to fetch -- @c dlpDBListRAM
+ * or @c dlpDBListROM.
+ *
+ * @return list of DBInfo objects, one for each database
+ * @note ownership of the DBInfo objects is passed to the
+ * caller, who must delete the objects.
+ */
+ virtual DBInfoList getDBList(int cardno=0, int flags=dlpDBListRAM) = 0;
+
+ /**
+ * Return a database object for manipulating the database with
+ * name @p name on the link. This database may be local or
+ * remote, depending on the kind of link in use.
+ *
+ * @return pointer to database object, or 0 on error.
+ * @note ownership of the database object is given to the caller,
+ * who must delete the object in time.
+ */
+ virtual PilotDatabase *database( const QString &name ) = 0;
+
+ /**
+ * Return a database object for manipulating the database with
+ * the name stored in the DBInfo structure @p info . The default
+ * version goes through method database( const QString & ), above.
+ *
+ * @return pointer to database object, or 0 on error.
+ * @note ownership of the database object is given to the caller.
+ */
+ virtual PilotDatabase *database( const DBInfo *info );
+
+ /**
+ * Retrieve the user information from the device. Ownership
+ * is kept by the link, and at the end of a sync the user
+ * information is synced back to the link -- so it may be
+ * modified, but don't make local copies of it.
+ *
+ * @note Do not call this before the sync begins!
+ */
+ KPilotUser &getPilotUser()
+ {
+ return *fPilotUser;
+ }
+
+ /**
+ * System information about the handheld. Ownership is kept
+ * by the link. For non-device links, something fake is
+ * returned.
+ *
+ * @note Do not call this before the sync begins!
+ */
+ const KPilotSysInfo &getSysInfo()
+ {
+ return *fPilotSysInfo;
+ }
+
+ /**
+ * Retrieve information about the data card @p card;
+ * I don't think that any pilot supports card numbers
+ * other than 0. Non-device links return something fake.
+ *
+ * This function may return NULL (non-device links or
+ * on error).
+ *
+ * @note Ownership of the KPilotCard object is given
+ * to the caller, who must delete it.
+ */
+ virtual const KPilotCard *getCardInfo(int card=0) = 0;
+
+ /**
+ * When ending the sync, you can do so gracefully, updating the
+ * last-sync time to indicate a successful sync and setting the
+ * user name on the device, or you can skip that (for unsuccessful
+ * syncs, generally).
+ */
+ enum EndOfSyncFlags {
+ NoUpdate, ///< Do not update the user info
+ UpdateUserInfo ///< Update user info and last successful sync date
+ } ;
+
+ /**
+ * Custom events we can be handling...
+ */
+ enum CustomEvents {
+ EventTickleTimeout = 1066
+ };
+
+ /**
+ * End the sync in a gracuful manner. If @p f is UpdateUserInfo,
+ * the sync was successful and the user info and last successful sync
+ * timestamp are updated.
+ */
+ virtual void endSync( EndOfSyncFlags f ) = 0;
+
+signals:
+ /**
+ * A timeout associated with tickling has occurred. Each
+ * time startTickle() is called, you can state how long
+ * tickling should last (at most) before timing out.
+ *
+ * You can only get a timeout when the Qt event loop is
+ * running, which somewhat limits the usefulness of timeouts.
+ */
+ void timeout();
+
+ /** Signal that a message has been written to the sync log. */
+ void logMessage(const QString &);
+
+ /** Signal that an error has occurred, for logging. */
+ void logError(const QString &);
+
+ /**
+ * Signal that progress has been made, for logging purposes.
+ * @p p is the percentage completed (0 <= s <= 100).
+ * The string @p s is logged as well, if non-Null.
+ */
+ void logProgress(const QString &s, int p);
+
+ /**
+ * Emitted once the user information has been read and
+ * the HotSync is really ready to go.
+ */
+ void deviceReady( KPilotLink * );
+
+
+public slots:
+ /**
+ * Release all resources, including the master pilot socket,
+ * timers, etc.
+ */
+ virtual void close() = 0;
+
+ /**
+ * Assuming things have been set up at least once already by
+ * a call to reset() with parameters, use this slot to re-start
+ * with the same settings.
+ */
+ virtual void reset() = 0;
+
+ /** Tickle the underlying device exactly once. */
+ virtual bool tickle() = 0;
+
+protected:
+ /**
+ * Path of the device special file that will be used.
+ * Usually /dev/pilot, /dev/ttySx, or /dev/usb/x. May be
+ * a filesystem path for local links.
+ */
+ QString fPilotPath;
+
+ /**
+ * Start tickling the Handheld (every few seconds). This
+ * lasts until @p timeout seconds have passed (or forever
+ * if @p timeout is zero).
+ *
+ * @note Do not call startTickle() twice with no intervening
+ * stopTickle().
+ */
+ void startTickle(unsigned int timeout=0);
+
+ /**
+ * Stop tickling the Handheld. This may block for some
+ * time (less than a second) to allow the tickle thread
+ * to finish.
+ */
+ void stopTickle();
+
+ /**
+ * Install a single file onto the device link. Full pathname
+ * @p f is used; in addition, if @p deleteFile is true remove
+ * the source file. Returns @c true if the install succeeded.
+ */
+ virtual bool installFile( const QString &f, const bool deleteFile ) = 0;
+
+ /**
+ * Notify the Pilot user that a conduit is running now.
+ * On real devices, this prints out (on screen) which database
+ * is now opened; useful for progress reporting.
+ *
+ * @return -1 on error
+ * @note the default implementation returns 0
+ */
+ virtual int openConduit();
+
+ /**
+ * Returns a file handle for raw operations. Not recommended.
+ * On links with no physical device backing, returns -1.
+ *
+ * @note the default implementation returns -1
+ */
+ virtual int pilotSocket() const;
+
+ /**
+ * Actually write an entry to the device link. The message
+ * @p s must be guaranteed non-empty.
+ */
+ virtual void addSyncLogEntryImpl( const QString &s ) = 0;
+
+ /**
+ * User information structure. Should be filled in when a sync
+ * starts, so that conduits can use the information.
+ */
+ KPilotUser *fPilotUser;
+
+ /**
+ * System information about the device. Filled in when the
+ * sync starts. Non-device links need to fake something.
+ */
+ KPilotSysInfo *fPilotSysInfo;
+
+
+private:
+ bool fTickleDone;
+ QThread *fTickleThread;
+
+} ;
+
+#endif
diff --git a/kpilot/lib/kpilotlocallink.cc b/kpilot/lib/kpilotlocallink.cc
new file mode 100644
index 000000000..c3af1d342
--- /dev/null
+++ b/kpilot/lib/kpilotlocallink.cc
@@ -0,0 +1,368 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006-2007 Adriaan de Groot <groot@kde.org>
+** Copyright (C) 2007 Jason 'vanRijn' Kasper <vR@movingparts.net>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <iostream>
+
+#include <pi-source.h>
+#include <pi-socket.h>
+#include <pi-dlp.h>
+#include <pi-file.h>
+#include <pi-buffer.h>
+
+#include <qdir.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qthread.h>
+
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include "pilotSerialDatabase.h"
+#include "pilotLocalDatabase.h"
+
+#include "kpilotlink.h"
+#include "kpilotlocallink.moc"
+
+
+typedef QPair<QString, struct DBInfo> DatabaseDescriptor;
+typedef QValueList<DatabaseDescriptor> DatabaseDescriptorList;
+
+class KPilotLocalLink::Private
+{
+public:
+ DatabaseDescriptorList fDBs;
+} ;
+
+unsigned int KPilotLocalLink::findAvailableDatabases( KPilotLocalLink::Private &info, const QString &path )
+{
+ FUNCTIONSETUP;
+
+ info.fDBs.clear();
+
+ QDir d(path);
+ if (!d.exists())
+ {
+ // Perhaps return an error?
+ return 0;
+ }
+
+ // Use this to fake indexes in the list of DBInfo structs
+ unsigned int counter = 0;
+
+ QStringList dbs = d.entryList( CSL1("*.pdb"), QDir::Files | QDir::NoSymLinks | QDir::Readable );
+ for ( QStringList::ConstIterator i = dbs.begin(); i != dbs.end() ; ++i)
+ {
+ struct DBInfo dbi;
+
+ // Remove the trailing 4 characters
+ QString dbname = (*i);
+ dbname.remove(dbname.length()-4,4);
+
+ QString dbnamecheck = (*i).left((*i).findRev(CSL1(".pdb")));
+ Q_ASSERT(dbname == dbnamecheck);
+
+ if (PilotLocalDatabase::infoFromFile( path + CSL1("/") + (*i), &dbi))
+ {
+ DEBUGKPILOT << fname << ": Loaded "
+ << dbname << endl;
+ dbi.index = counter;
+ info.fDBs.append( DatabaseDescriptor(dbname,dbi) );
+ ++counter;
+ }
+ }
+
+ DEBUGKPILOT << fname << ": Total " << info.fDBs.count()
+ << " databases." << endl;
+ return info.fDBs.count();
+}
+
+
+KPilotLocalLink::KPilotLocalLink( QObject *parent, const char *name ) :
+ KPilotLink(parent,name),
+ fReady(false),
+ d( new Private )
+{
+ FUNCTIONSETUP;
+}
+
+KPilotLocalLink::~KPilotLocalLink()
+{
+ FUNCTIONSETUP;
+ KPILOT_DELETE(d);
+}
+
+/* virtual */ QString KPilotLocalLink::statusString() const
+{
+ return fReady ? CSL1("Ready") : CSL1("Waiting") ;
+}
+
+/* virtual */ bool KPilotLocalLink::isConnected() const
+{
+ return fReady;
+}
+
+/* virtual */ void KPilotLocalLink::reset( const QString &p )
+{
+ FUNCTIONSETUP;
+ fPath = p;
+ reset();
+}
+
+/* virtual */ void KPilotLocalLink::reset()
+{
+ FUNCTIONSETUP;
+ QFileInfo info( fPath );
+ fReady = !fPath.isEmpty() && info.exists() && info.isDir() ;
+ if (fReady)
+ {
+ findAvailableDatabases(*d, fPath);
+ QTimer::singleShot(500,this,SLOT(ready()));
+ }
+ else
+ {
+ WARNINGKPILOT << "The local link path <"
+ << fPath
+ << "> does not exist or is not a directory. No sync can be done."
+ << endl;
+ }
+}
+
+/* virtual */ void KPilotLocalLink::close()
+{
+ fReady = false;
+}
+
+/* virtual */ bool KPilotLocalLink::tickle()
+{
+ return true;
+}
+
+/* virtual */ const KPilotCard *KPilotLocalLink::getCardInfo(int)
+{
+ return 0;
+}
+
+/* virtual */ void KPilotLocalLink::endSync( EndOfSyncFlags f )
+{
+ Q_UNUSED(f);
+ fReady = false;
+}
+
+/* virtual */ int KPilotLocalLink::openConduit()
+{
+ FUNCTIONSETUP;
+ return 0;
+}
+
+
+/* virtual */ int KPilotLocalLink::getNextDatabase( int index, struct DBInfo *info )
+{
+ FUNCTIONSETUP;
+
+ if ( (index<0) || (index>=(int)d->fDBs.count()) )
+ {
+ WARNINGKPILOT << "Index out of range." << endl;
+ return -1;
+ }
+
+ DatabaseDescriptor dd = d->fDBs[index];
+
+ DEBUGKPILOT << fname << ": Getting database " << dd.first << endl;
+
+ if (info)
+ {
+ *info = dd.second;
+ }
+
+ return index+1;
+}
+
+/* virtual */ int KPilotLocalLink::findDatabase(const char *name, struct DBInfo*info,
+ int index, unsigned long type, unsigned long creator)
+{
+ FUNCTIONSETUP;
+
+ if ( (index<0) || (index>=(int)d->fDBs.count()) )
+ {
+ WARNINGKPILOT << "Index out of range." << endl;
+ return -1;
+ }
+
+ if (!name)
+ {
+ WARNINGKPILOT << "NULL name." << endl;
+ return -1;
+ }
+
+ QString desiredName = Pilot::fromPilot(name);
+ DEBUGKPILOT << fname << ": Looking for DB " << desiredName << endl;
+ for ( DatabaseDescriptorList::ConstIterator i = d->fDBs.at(index);
+ i != d->fDBs.end(); ++i)
+ {
+ const DatabaseDescriptor &dd = *i;
+ if (dd.first == desiredName)
+ {
+ if ( (!type || (type == dd.second.type)) &&
+ (!creator || (creator == dd.second.creator)) )
+ {
+ if (info)
+ {
+ *info = dd.second;
+ }
+ return index;
+ }
+ }
+
+ ++index;
+ }
+
+ return -1;
+}
+
+/* virtual */ void KPilotLocalLink::addSyncLogEntryImpl(QString const &s)
+{
+ FUNCTIONSETUP;
+ DEBUGKPILOT << fname << ": " << s << endl ;
+}
+
+/* virtual */ bool KPilotLocalLink::installFile(QString const &path, bool deletefile)
+{
+ FUNCTIONSETUP;
+
+ QFileInfo srcInfo(path);
+ QString canonicalSrcPath = srcInfo.dir().canonicalPath() + CSL1("/") + srcInfo.fileName() ;
+ QString canonicalDstPath = fPath + CSL1("/") + srcInfo.fileName();
+
+ if (canonicalSrcPath == canonicalDstPath)
+ {
+ // That's a cheap copy operation
+ return true;
+ }
+
+ KURL src = KURL::fromPathOrURL( canonicalSrcPath );
+ KURL dst = KURL::fromPathOrURL( canonicalDstPath );
+
+ KIO::NetAccess::file_copy(src,dst,-1,true);
+
+ if (deletefile)
+ {
+ KIO::NetAccess::del(src, 0L);
+ }
+
+ return true;
+}
+
+/* virtual */ bool KPilotLocalLink::retrieveDatabase( const QString &path, struct DBInfo *db )
+{
+ FUNCTIONSETUP;
+
+ QString dbname = Pilot::fromPilot(db->name) + CSL1(".pdb") ;
+ QString sourcefile = fPath + CSL1("/") + dbname ;
+ QString destfile = path ;
+
+ DEBUGKPILOT << fname << ": src=" << sourcefile << endl;
+ DEBUGKPILOT << fname << ": dst=" << destfile << endl;
+
+ QFile in( sourcefile );
+ if ( !in.exists() )
+ {
+ WARNINGKPILOT << "Source file " << sourcefile << " doesn't exist." << endl;
+ return false;
+ }
+ if ( !in.open( IO_ReadOnly | IO_Raw ) )
+ {
+ WARNINGKPILOT << "Can't read source file " << sourcefile << endl;
+ return false;
+ }
+
+ QFile out( destfile );
+ if ( !out.open( IO_WriteOnly | IO_Truncate | IO_Raw ) )
+ {
+ WARNINGKPILOT << "Can't write destination file " << destfile << endl;
+ return false;
+ }
+
+ const Q_ULONG BUF_SIZ = 8192 ;
+ char buf[BUF_SIZ];
+ Q_LONG r;
+
+ while ( (r=in.readBlock(buf,BUF_SIZ))>0 )
+ {
+ out.writeBlock(buf,r);
+ }
+ out.flush();
+ in.close();
+
+ return out.exists();
+}
+
+KPilotLink::DBInfoList KPilotLocalLink::getDBList( int, int )
+{
+ FUNCTIONSETUP;
+ DBInfoList l;
+ for ( DatabaseDescriptorList::ConstIterator i=d->fDBs.begin();
+ i != d->fDBs.end(); ++i)
+ {
+ l.append( (*i).second );
+ }
+ return l;
+}
+
+
+/* virtual */ PilotDatabase *KPilotLocalLink::database( const QString &name )
+{
+ FUNCTIONSETUP;
+ return new PilotLocalDatabase( fPath, name );
+}
+
+
+
+/* slot */ void KPilotLocalLink::ready()
+{
+ if (fReady)
+ {
+ emit deviceReady(this);
+ }
+}
+
diff --git a/kpilot/lib/kpilotlocallink.h b/kpilot/lib/kpilotlocallink.h
new file mode 100644
index 000000000..f18556b3c
--- /dev/null
+++ b/kpilot/lib/kpilotlocallink.h
@@ -0,0 +1,95 @@
+#ifndef _KPILOT_KPILOTLOCALLINK_H
+#define _KPILOT_KPILOTLOCALLINK_H
+/*
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "kpilotlink.h"
+
+/** @file
+* Definition of the local link class; implemented in kpilotlink.cc .
+*/
+
+
+/**
+* Implementation of the device link for file-system backed (ie. local, fake)
+* devices. Uses a directory specified in the reset() call to serve databases.
+*/
+class KDE_EXPORT KPilotLocalLink : public KPilotLink
+{
+Q_OBJECT
+public:
+ KPilotLocalLink( QObject *parent=0L, const char *name=0L );
+ virtual ~KPilotLocalLink();
+
+ virtual QString statusString() const;
+ virtual bool isConnected() const;
+ virtual void reset( const QString & );
+ virtual void close();
+ virtual void reset();
+ virtual bool tickle();
+ virtual const KPilotCard *getCardInfo(int card);
+ virtual void endSync( EndOfSyncFlags f );
+ virtual int openConduit();
+ virtual int getNextDatabase(int index,struct DBInfo *);
+ virtual int findDatabase(const char *name, struct DBInfo*,
+ int index=0, unsigned long type=0, unsigned long creator=0);
+ virtual bool retrieveDatabase(const QString &path, struct DBInfo *db);
+ virtual DBInfoList getDBList(int cardno=0, int flags=dlpDBListRAM);
+ virtual PilotDatabase *database( const QString &name );
+
+public slots:
+ void ready();
+
+protected:
+ virtual bool installFile(const QString &, const bool deleteFile);
+ virtual void addSyncLogEntryImpl( const QString &s );
+ virtual int pilotSocket() const
+ {
+ return -1;
+ }
+
+protected:
+ bool fReady;
+ QString fPath;
+
+ class Private;
+ Private *d;
+
+ /**
+ * Pre-process the directory @p path to find out which databases
+ * live there.
+ *
+ * @return Number of database in @p path.
+ */
+ unsigned int findAvailableDatabases( Private &, const QString &path );
+} ;
+
+
+#endif
+
diff --git a/kpilot/lib/options.cc b/kpilot/lib/options.cc
new file mode 100644
index 000000000..0eb1babf7
--- /dev/null
+++ b/kpilot/lib/options.cc
@@ -0,0 +1,157 @@
+/* KPilot
+**
+** Copyright (C) 2000-2001 by Adriaan de Groot
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This is a file of odds and ends, with debugging functions and stuff.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "options.h"
+
+
+#include <iostream>
+
+#include <qsize.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+
+#ifdef DEBUG
+int debug_level = 1;
+#else
+int debug_level = 0;
+#endif
+
+// The daemon also has a debug level; debug_spaces is 60 spaces,
+// to align FUNCTIONSETUP output. The one byte extra is for the NUL.
+//
+//
+static const char debug_spaces[61] =
+ " "
+ " "
+ " ";
+
+
+QString rtExpand(const QString &s, Qt::TextFormat richText)
+{
+ if (richText == Qt::RichText)
+ {
+ QString t(s);
+ return t.replace(CSL1("\n"), CSL1("<br/>\n"));
+ }
+ else
+ {
+ return s;
+ }
+
+}
+
+QDateTime readTm(const struct tm &t)
+{
+ QDateTime dt;
+ dt.setDate(QDate(1900 + t.tm_year, t.tm_mon + 1, t.tm_mday));
+ dt.setTime(QTime(t.tm_hour, t.tm_min, t.tm_sec));
+ return dt;
+}
+
+
+
+struct tm writeTm(const QDateTime &dt)
+{
+ struct tm t;
+
+ t.tm_wday = 0; // unimplemented
+ t.tm_yday = 0; // unimplemented
+ t.tm_isdst = 0; // unimplemented
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+ t.tm_zone = 0; // unimplemented
+#endif
+
+ t.tm_year = dt.date().year() - 1900;
+ t.tm_mon = dt.date().month() - 1;
+ t.tm_mday = dt.date().day();
+ t.tm_hour = dt.time().hour();
+ t.tm_min = dt.time().minute();
+ t.tm_sec = dt.time().second();
+
+ return t;
+}
+
+
+
+struct tm writeTm(const QDate &d)
+{
+ QDateTime dt(d);
+ return writeTm(dt);
+}
+
+KPilotDepthCount::KPilotDepthCount(int, int level, const char *s) :
+ fDepth(depth),
+ fLevel(level),
+ fName(s)
+{
+ DEBUGKPILOT << "! DEPRECATED Depth call.\n! "
+ << kdBacktrace(4) << endl;
+
+ if (debug_level>=fLevel)
+ {
+ DEBUGKPILOT << indent() << ">" << name() << endl;
+ }
+ depth++;
+}
+
+KPilotDepthCount::KPilotDepthCount(int level, const char *s) :
+ fDepth(depth),
+ fLevel(level),
+ fName(s)
+{
+ if (debug_level>=fLevel)
+ {
+ DEBUGKPILOT << indent() << ">" << name() << endl;
+ }
+ depth++;
+}
+
+KPilotDepthCount::~KPilotDepthCount()
+{
+ depth--;
+ std::cerr.clear(std::ios_base::goodbit);
+}
+
+const char *KPilotDepthCount::indent() const
+{
+ if (fDepth < 30)
+ {
+ return debug_spaces + 60-fDepth*2;
+ }
+ else
+ {
+ return debug_spaces;
+ }
+}
+
+int KPilotDepthCount::depth = 0;
+
diff --git a/kpilot/lib/options.h b/kpilot/lib/options.h
new file mode 100644
index 000000000..6e036e82d
--- /dev/null
+++ b/kpilot/lib/options.h
@@ -0,0 +1,199 @@
+#ifndef _KPILOT_OPTIONS_H
+#define _KPILOT_OPTIONS_H
+/* options.h KPilot
+**
+** Copyright (C) 1998-2001,2002,2003 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This file defines some global constants and macros for KPilot.
+** In particular, KDE2 is defined when KDE2 seems to be the environment
+** (is there a better way to do this?). Use of KDE2 to #ifdef sections
+** of code is deprecated though.
+**
+** Many debug functions are defined as well.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "config.h"
+
+#include <qglobal.h>
+#include <qnamespace.h>
+#include <qstring.h>
+
+#if (QT_VERSION < 0x030300)
+#error "This is KPilot for KDE3.5 and won't compile with Qt < 3.3.0"
+#endif
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <klocale.h>
+
+#if !(KDE_IS_VERSION(3,4,0))
+#error "This is KPilot for (really) KDE 3.5 and won't compile with KDE < 3.4.0"
+#endif
+
+#if !(KDE_IS_VERSION(3,5,0))
+#warning "This is KPilot for KDE 3.5 and might not compile with KDE < 3.5.0"
+#endif
+
+#include "pilotLinkVersion.h"
+
+#include <iostream>
+
+using namespace std;
+inline std::ostream& operator <<(std::ostream &o, const QString &s)
+ { if (s.isEmpty()) return o<<"<empty>"; else return o<<s.latin1(); }
+inline std::ostream& operator <<(std::ostream &o, const QCString &s)
+ { if (s.isEmpty()) return o<<"<empty>"; else return o << *s; }
+
+
+#ifndef NDEBUG
+#define DEBUG (1)
+#endif
+
+extern KDE_EXPORT int debug_level;
+
+class KDE_EXPORT KPilotDepthCount
+{
+public:
+ KPilotDepthCount(int, int level, const char *s);
+ KPilotDepthCount(int level, const char *s);
+ ~KPilotDepthCount();
+ const char *indent() const;
+ inline const char *name() const { return fName; } ;
+ inline int level() const { return fLevel; } ;
+
+protected:
+ static int depth;
+ int fDepth;
+ int fLevel;
+ const char *fName;
+} ;
+
+
+#ifdef DEBUG
+#ifdef __GNUC__
+#define KPILOT_FNAMEDEF(l) KPilotDepthCount fname(l,__FUNCTION__)
+#else
+#define KPILOT_FNAMEDEF(l) KPilotDepthCount fname(l,__FILE__ ":" "__LINE__")
+#endif
+
+#define FUNCTIONSETUP KPILOT_FNAMEDEF(1)
+#define FUNCTIONSETUPL(l) KPILOT_FNAMEDEF(l)
+
+// stderr / iostream-based debugging.
+//
+//
+#define DEBUGKPILOT std::cerr
+#define WARNINGKPILOT std::cerr.clear(std::ios_base::goodbit),\
+ std::cerr << "! " << k_funcinfo << std::endl << "! "
+
+
+
+
+inline std::ostream& operator <<(std::ostream &o, const KPilotDepthCount &d)
+{
+ if (debug_level >= d.level())
+ {
+ o.clear(std::ios_base::goodbit);
+ return o << d.indent() << ' ' << d.name();
+ }
+ else
+ {
+ o.setstate(std::ios_base::badbit | std::ios_base::failbit);
+ return o;
+ }
+}
+
+#else
+
+// no debugging at all
+//
+#define DEBUGSTREAM kndbgstream
+#define DEBUGKPILOT kndDebug()
+#define WARNINGKPILOT kndDebug()
+
+// With debugging turned off, FUNCTIONSETUP doesn't do anything.
+//
+//
+#define FUNCTIONSETUP const int fname = 0; Q_UNUSED(fname);
+#define FUNCTIONSETUPL(a) const int fname = a; Q_UNUSED(fname);
+#endif
+
+#define KPILOT_VERSION "4.9.4-3510 (elsewhere)"
+
+
+// Function to expand newlines in rich text to <br>\n
+QString rtExpand(const QString &s, Qt::TextFormat richText);
+
+
+
+/**
+ * Convert a struct tm from the pilot-link package to a QDateTime
+ */
+KDE_EXPORT QDateTime readTm(const struct tm &t);
+/**
+ * Convert a QDateTime to a struct tm for use with the pilot-link package
+ */
+KDE_EXPORT struct tm writeTm(const QDateTime &dt);
+KDE_EXPORT struct tm writeTm(const QDate &dt);
+
+
+// Some layout macros
+//
+// SPACING is a generic distance between visual elements;
+// 10 seems reasonably good even at high resolutions.
+//
+//
+#define SPACING (10)
+
+// Semi-Standard safe-free expression. Argument a may be evaluated more
+// than once though, so be careful.
+//
+//
+#define KPILOT_FREE(a) { if (a) { ::free(a); a=0L; } }
+#define KPILOT_DELETE(a) { if (a) { delete a; a=0L; } }
+
+
+// This marks strings that need to be i18n()ed in future,
+// but cannot be done now due to message freeze. The _P
+// variant is to handle plurals and is wrong, but unavoidable.
+//
+//
+#define TODO_I18N(a) QString::fromLatin1(a)
+#define TODO_I18N_P(a,b,c) ((c>1) ? a : b)
+
+// Handle some cases for QT_NO_CAST_ASCII and NO_ASCII_CAST.
+// Where possible in the source, known constant strings in
+// latin1 encoding are marked with CSL1(), to avoid gobs
+// of latin1() or fromlatin1() calls which might obscure
+// those places where the code really is translating
+// user data from latin1.
+//
+// The extra "" in CSL1 is to enforce that it's only called
+// with constant strings.
+//
+//
+#define CSL1(a) QString::fromLatin1(a "")
+
+#endif
diff --git a/kpilot/lib/pilot.cc b/kpilot/lib/pilot.cc
new file mode 100644
index 000000000..2585445c1
--- /dev/null
+++ b/kpilot/lib/pilot.cc
@@ -0,0 +1,264 @@
+/* pilot.cc KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2003-2006 Adriaan de Groot <groot@kde.org>
+**
+** These are the base class structures that reside on the
+** handheld device -- databases and their parts.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <qtextcodec.h>
+#include <qmutex.h>
+#include <kcharsets.h>
+#include <kglobal.h>
+
+#include "pilot.h"
+#include "pilotDatabase.h"
+#include "pilotAppInfo.h"
+#include "pilotRecord.h"
+
+
+namespace Pilot
+{
+static QTextCodec *codec = 0L;
+static QMutex* mutex = 0L;
+
+
+QString fromPilot( const char *c, int len )
+{
+ mutex->lock();
+ QString str = codec->toUnicode(c,len);
+ mutex->unlock();
+ return str;
+}
+
+QString fromPilot( const char *c )
+{
+ mutex->lock();
+ QString str = codec->toUnicode(c);
+ mutex->unlock();
+ return str;
+}
+
+QCString toPilot( const QString &s )
+{
+ mutex->lock();
+ QCString str = codec->fromUnicode(s);
+ mutex->unlock();
+ return str;
+}
+
+int toPilot( const QString &s, char *buf, int len)
+{
+ mutex->lock();
+ // See toPilot() below.
+ memset( buf, 0, len );
+ int used = len;
+ QCString cbuf = codec->fromUnicode(s,used);
+ if (used > len)
+ {
+ used=len;
+ }
+ memcpy( buf, cbuf.data(), used );
+ mutex->unlock();
+ return used;
+}
+
+int toPilot( const QString &s, unsigned char *buf, int len)
+{
+ mutex->lock();
+ // Clear the buffer
+ memset( buf, 0, len );
+
+ // Convert to 8-bit encoding
+ int used = len;
+ QCString cbuf = codec->fromUnicode(s,used);
+
+ // Will it fit in the buffer?
+ if (used > len)
+ {
+ // Ought to be impossible, anyway, since 8-bit encodings
+ // are shorter than the UTF-8 encodings (1 byte per character
+ // vs. 1-or-more byte per character).
+ used=len;
+ }
+
+ // Fill the buffer with encoded data.
+ memcpy( buf, cbuf.data(), used );
+ mutex->unlock();
+ return used;
+}
+
+bool setupPilotCodec(const QString &s)
+{
+ FUNCTIONSETUP;
+ mutex = new QMutex();
+ mutex->lock();
+ QString encoding(KGlobal::charsets()->encodingForName(s));
+
+ DEBUGKPILOT << fname << ": Using codec name " << s << endl;
+ DEBUGKPILOT << fname << ": Creating codec " << encoding << endl;
+
+ // if the desired codec can't be found, latin1 will be returned anyway, no need to do this manually
+ codec = KGlobal::charsets()->codecForName(encoding);
+
+ if (codec)
+ {
+ DEBUGKPILOT << fname << ": Got codec " << codec->name() << endl;
+ }
+
+ mutex->unlock();
+ return codec;
+}
+
+QString codecName()
+{
+ return QString::fromLatin1(codec->name());
+}
+
+QString category(const struct CategoryAppInfo *info, unsigned int i)
+{
+ if (!info || (i>=CATEGORY_COUNT))
+ {
+ return QString::null;
+ }
+
+ mutex->lock();
+ QString str = codec->toUnicode(info->name[i],
+ MIN(strlen(info->name[i]), CATEGORY_SIZE-1));
+ mutex->unlock();
+ return str;
+}
+
+
+int findCategory(const struct CategoryAppInfo *info,
+ const QString &selectedCategory,
+ bool unknownIsUnfiled)
+{
+ FUNCTIONSETUP;
+
+ if (!info)
+ {
+ WARNINGKPILOT << "Bad CategoryAppInfo pointer" << endl;
+ return -1;
+ }
+
+ int currentCatID = -1;
+ for (unsigned int i=0; i<CATEGORY_COUNT; i++)
+ {
+ if (!info->name[i][0]) continue;
+ if (selectedCategory == category(info, i))
+ {
+ currentCatID = i;
+ break;
+ }
+ }
+
+ if (-1 == currentCatID)
+ {
+ DEBUGKPILOT << fname << ": Category name "
+ << selectedCategory << " not found." << endl;
+ }
+ else
+ {
+ DEBUGKPILOT << fname << ": Matched category " << currentCatID << endl;
+ }
+
+ if ((currentCatID == -1) && unknownIsUnfiled)
+ currentCatID = 0;
+ return currentCatID;
+}
+
+int insertCategory(struct CategoryAppInfo *info,
+ const QString &label,
+ bool unknownIsUnfiled)
+{
+ FUNCTIONSETUP;
+
+ if (!info)
+ {
+ WARNINGKPILOT << "Bad CategoryAppInfo pointer" << endl;
+ return -1;
+ }
+
+
+ int c = findCategory(info,label,unknownIsUnfiled);
+ if (c<0)
+ {
+ // This is the case when the category is not known
+ // and unknownIsUnfiled is false.
+ for (unsigned int i=0; i<CATEGORY_COUNT; i++)
+ {
+ if (!info->name[i][0])
+ {
+ c = i;
+ break;
+ }
+ }
+
+ if ((c>0) && (c < (int)CATEGORY_COUNT))
+ {
+ // 0 is always unfiled, can't change that.
+ toPilot(label,info->name[c],CATEGORY_SIZE);
+ }
+ else
+ {
+ WARNINGKPILOT << "Category name "
+ << label
+ << " could not be added." << endl;
+ c = -1;
+ }
+ }
+
+ return c;
+}
+
+void dumpCategories(const struct CategoryAppInfo *info)
+{
+ FUNCTIONSETUP;
+
+ if (!info)
+ {
+ WARNINGKPILOT << "Dumping bad pointer." << endl;
+ return;
+ }
+
+ DEBUGKPILOT << fname << " lastUniqueId: "
+ << (int) info->lastUniqueID << endl;
+ for (unsigned int i = 0; i < CATEGORY_COUNT; i++)
+ {
+ if (!info->name[i][0]) continue;
+ DEBUGKPILOT << fname << ": " << i << " = "
+ << (int)(info->ID[i]) << " <"
+ << info->name[i] << ">" << endl;
+ }
+}
+
+
+}
+
+
diff --git a/kpilot/lib/pilot.h b/kpilot/lib/pilot.h
new file mode 100644
index 000000000..d4fec82b2
--- /dev/null
+++ b/kpilot/lib/pilot.h
@@ -0,0 +1,410 @@
+#ifndef _KPILOT_PILOT_H
+#define _KPILOT_PILOT_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2003-2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <sys/types.h>
+
+#include <pi-appinfo.h>
+#include <pi-buffer.h>
+#include <pi-dlp.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+#include "pilotLinkVersion.h"
+
+
+/** @file
+* These are some base structures that reside on the
+* handheld device -- strings and binary data.
+*/
+
+class PilotDatabase; // A database
+class PilotRecord; // ... has records
+class PilotCategoryInfo; // ... and category information
+
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+/**
+* The Pilot namespace holds constants that are global for
+* the handheld data structures. Also contains some global
+* functions that deal with pilot-link structures as well
+* as mapping user-visible strings from UTF8 (KDE side) to
+* the encoding used on the handheld.
+*/
+namespace Pilot
+{
+ /** Maximum size of an AppInfo block, taken roughly from the pilot-link source. */
+ static const int MAX_APPINFO_SIZE=8192;
+
+ /** Maximum number of categories the handheld has */
+ static const unsigned int CATEGORY_COUNT=16;
+
+ /** Maximum size of a category label */
+ static const unsigned int CATEGORY_SIZE=16;
+
+ /** Category number for unfiled records */
+ static const int Unfiled = 0;
+
+ /** Maximum size (in bytes) of a record's data */
+ static const int MAX_RECORD_SIZE = 65535;
+
+ typedef QValueList<recordid_t> RecordIDList;
+
+ /** Static translation function that maps handheld native (8 bit,
+ * usually latin1 but sometimes someting else) encoded data to
+ * a Unicode string. Converts the @p len characters in @p c
+ * to a Unicode string.
+ */
+ QString fromPilot( const char *c, int len );
+
+ /** Static translation function mapping a NUL-terminated
+ * string from the handheld's encoding to UTF-8.
+ * @param c the NUL-terminated string to decode
+ * @return QString (UTF-8) value of @p c
+ * @note NUL-terminated strings are rare on the handheld.
+ */
+ QString fromPilot( const char *c );
+
+ /** Static translation function that maps a QString onto the
+ * native 8 bit encoding of the handheld. Writes the result into
+ * the buffer @p buf which has size @p len. Returns the length
+ * of the result. Zero-fills the buffer as needed.
+ */
+ int toPilot( const QString &s, char *buf, int len);
+ int toPilot( const QString &s, unsigned char *buf, int len);
+
+ /** Static translation function that maps a QString onto the
+ * native 8 bit encoding of the handheld.
+ *
+ * @param s String to encode
+ * @return Encoded string in a QCString
+ */
+ QCString toPilot( const QString &s );
+
+ /** Create a codec for translating handheld native 8 bit to Unicode,
+ * using the given codec @p name -- this will often be latin1, but
+ * might be something else for, say, Russian-language Pilots.
+ * If @p name is empty, use latin1.
+ *
+ * @return @c true on success, @c false otherwise
+ */
+ bool setupPilotCodec(const QString &name);
+
+ /** Returns the name of the codec being used. */
+ QString codecName();
+
+ /** For debugging, display category names for the given AppInfo
+ * structure. Called by dump(). You must pass a valid reference.
+ */
+ void dumpCategories(const struct CategoryAppInfo *info);
+
+ /** Check that a given category number is valid. This
+ * restricts the range of integers to [0..CATEGORY_COUNT-1]
+ * (i.e. [0..15]) which is what the handheld supports.
+ */
+ inline bool validCategory(int c)
+ {
+ if (c<0)
+ {
+ return false;
+ }
+ return ((unsigned int)c<CATEGORY_COUNT);
+ }
+
+ /** Returns the QString for the requested category @p i
+ * in the category structure @p info. Returns @c QString::null
+ * on error (bad pointer or bad category number). May also
+ * return @c QString::null if the category name is empty.
+ */
+ inline QString categoryName(const struct CategoryAppInfo *info, unsigned int i)
+ {
+ if ( ( i < CATEGORY_COUNT ) && ( info->name[i][0] ) )
+ {
+ /*
+ * Seems to be important that we try to pass the real length here
+ * to the codec.
+ */
+ return fromPilot( info->name[i], MIN(strlen(info->name[i]),CATEGORY_SIZE) );
+ }
+ else
+ {
+ return QString::null;
+ }
+ }
+
+ /** Returns a list of all the category names available on the
+ * handheld. This list is neither ordered nor does it contain
+ * all sixteen categories -- empty category names on the
+ * handheld are skipped.
+ */
+ inline QStringList categoryNames(const struct CategoryAppInfo *info)
+ {
+ QStringList l;
+ if (!info)
+ {
+ return l;
+ }
+ for (unsigned int i=0; i<CATEGORY_COUNT; ++i)
+ {
+ QString s = categoryName(info,i);
+ if (!s.isEmpty())
+ {
+ l.append(s);
+ }
+ }
+ return l;
+ }
+
+ /** Search for the given category @p name in the list
+ * of categories; returns the category number. If @p unknownIsUnfiled
+ * is true, then map unknown categories to Unfiled instead of returning
+ * an error number.
+ *
+ * @return >=0 is a specific category based on the text-to-
+ * category number mapping defined by the Pilot,
+ * where 0 is always the 'unfiled' category.
+ * @return -1 means unknown category selected when
+ * @p unknownIsUnfiled is false.
+ * @return 0 == Unfiled means unknown category selected when
+ * @p unknownIsUnfiled is true.
+ *
+ */
+ int findCategory(const struct CategoryAppInfo *info, const QString &name, bool unknownIsUnfiled);
+
+ /** Search for the given category @p name in the list
+ * of categories; returns the category number. If @p unknownIsUnfiled
+ * is @c true, then map unknown categories to Unfiled.
+ * If @p unknownIsUnfiled is @c false, insert a @em new
+ * category into the structure and return the category
+ * number of the new category. Return -1 if (and only if)
+ * @p unknownIsUnfiled is false and the category structure
+ * is already full.
+ *
+ * @return >=0 is a specific category based on the text-to-
+ * category number mapping defined by the Pilot,
+ * where 0 is always the 'unfiled' category.
+ * @return 0 Unknown category and @p unknownIsUnfiled is @c true
+ * @return -1 means unknown category selected when
+ * @p unknownIsUnfiled is false and categories
+ * are all full.
+ *
+ */
+ int insertCategory(struct CategoryAppInfo *info, const QString &label, bool unknownIsUnfiled);
+
+ /** The handheld also holds data about each database
+ * in a DBInfo structure; check if the database described
+ * by this structure is a resource database.
+ */
+ static inline bool isResource(struct DBInfo *info)
+ {
+ return (info->flags & dlpDBFlagResource);
+ }
+
+
+/** @section Binary blob handling
+*
+* For reading and writing binary blobs -- which has to happen to
+* pack data into the format that the handheld needs -- it is important
+* to remember that the handheld has only four data types (as far
+* as I can tell: byte, short (a 2 byte integer), long (a 4 byte integer)
+* and string (NUL terminated). The sizes of the types on the handheld
+* do not necessarily correspond to the sizes of the same-named types
+* on the desktop. This means that 'reading a long' from a binary
+* blob must always be 4 bytes -- not sizeof(long).
+*
+* The following templates help out in manipulating the blobs.
+* Instantiate them with the type @em name you need (char, short, long or
+* char *) and you get a ::size enum specifying the number of bytes
+* (where applicable) and ::append and ::read methods for appending
+* a value of the given type to a pi_buffer_t or reading one from
+* the buffer, respectively.
+*
+* The usage of ::read and ::append is straightforward:
+*
+* append(pi_buffer_t *b, TYPE_VALUE v) Appends the type value @p v to the
+* buffer @p b , extending the buffer as needed.
+*
+* TYPE_VALUE read(pi_buffer_t *b, unsigned int &offset) Read a value from
+* the buffer @p b at position @p offset and return it. The offset value
+* is increased by the number of bytes read from the buffer.
+*
+* To write a binary blob, a sequence of ::append calls constructs the
+* blob. To read the same blob, a sequence of ::read calls with the
+* @em same type parameters is sufficient.
+*
+* The calls may vary a little: the exact interface differs depending
+* on the needs of the type of data to be written to the blob.
+*/
+template<typename t> struct dlp { } ;
+
+template<> struct dlp<char>
+{
+ enum { size = 1 };
+
+ static void append(pi_buffer_t *b, char v)
+ {
+ pi_buffer_append(b,&v,size);
+ }
+
+ /**
+ * Returns next byte from buffer or 0 on error (0 is also a
+ * valid return value, though).
+ */
+ static char read(const pi_buffer_t *b, unsigned int &offset)
+ {
+ if (offset+size > b->used)
+ {
+ return 0;
+ }
+ char c = b->data[offset];
+ offset+=size;
+ return c;
+ }
+} ;
+
+template<> struct dlp<short>
+{
+ enum { size = 2 };
+
+ static void append(pi_buffer_t *b, short v)
+ {
+ char buf[size];
+ set_short(buf,v);
+ pi_buffer_append(b,buf,size);
+ }
+
+ /**
+ * Returns the next short (2 byte) value from the buffer, or
+ * -1 on error (which is also a valid return value).
+ */
+ static int read(const pi_buffer_t *b, unsigned int &offset)
+ {
+ if (offset+size > b->used)
+ {
+ return -1;
+ }
+ else
+ {
+ int r = get_short(b->data + offset);
+ offset+=size;
+ return r;
+ }
+ }
+
+ /**
+ * Overload to read from a data buffer instead of a real pi_buffer;
+ * does no bounds checking.
+ */
+ static int read(const unsigned char *b, unsigned int &offset)
+ {
+ int r = get_short(b+offset);
+ offset+=size;
+ return r;
+ }
+} ;
+
+template<> struct dlp<long>
+{
+ enum { size = 4 };
+
+ static void append(pi_buffer_t *b, int v)
+ {
+ char buf[size];
+ set_long(buf,v);
+ pi_buffer_append(b,buf,size);
+ }
+
+ /**
+ * Returns the next long (4 byte) value from the buffer or
+ * -1 on error (which is also a valid value).
+ */
+ static int read(const pi_buffer_t *b, unsigned int &offset)
+ {
+ if (offset+size > b->used)
+ {
+ return -1;
+ }
+ else
+ {
+ int r = get_long(b->data + offset);
+ offset+=size;
+ return r;
+ }
+ }
+
+ /**
+ * Overload to read a long value from a data buffer; does
+ * no bounds checking.
+ */
+ static int read(const unsigned char *b, unsigned int &offset)
+ {
+ int r = get_long(b+offset);
+ offset+=size;
+ return r;
+ }
+} ;
+
+template<> struct dlp<char *>
+{
+ // No size enum, doesn't make sense
+ // No append, use pi_buffer_append
+ /**
+ * Read a fixed-length string from the buffer @p b into data buffer
+ * @p v which has size (including terminating NUL) of @p s.
+ * Returns the number of bytes read (which will normally be @p s
+ * but will be less than @p s on error).
+ */
+ static int read(const pi_buffer_t *b,
+ unsigned int &offset,
+ unsigned char *v,
+ size_t s)
+ {
+ if ( s+offset > b->used )
+ {
+ s = b->allocated - offset;
+ }
+ memcpy(v, b->data + offset, s);
+ offset+=s;
+ return s;
+ }
+
+ /** Overload for signed char. */
+ inline static int read(const pi_buffer_t *b, unsigned int &offset, char *v, size_t s)
+ {
+ return read(b,offset,(unsigned char *)v,s);
+ }
+} ;
+
+}
+
+#endif
+
diff --git a/kpilot/lib/pilotAddress.cc b/kpilot/lib/pilotAddress.cc
new file mode 100644
index 000000000..418c705b4
--- /dev/null
+++ b/kpilot/lib/pilotAddress.cc
@@ -0,0 +1,636 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2007 by Adriaan de Groot <groot@kde.org>
+**
+** This is a C++ wrapper for the pilot's address database structures.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "options.h"
+
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <qnamespace.h>
+#include <qstringlist.h>
+
+#include "pilotAddress.h"
+
+static const char *default_address_category_names[] = {
+ "Unfiled",
+ "Business",
+ "Personal",
+ "Quicklist",
+ 0L
+} ;
+
+static const char *default_address_field_labels[] = {
+ "Last name",
+ "First name",
+ "Company",
+ "Work",
+ "Home",
+ "Fax",
+ "Other",
+ "E-mail",
+ "Addr(W)",
+ "City",
+ "State",
+ "Zip Code",
+ "Country",
+ "Title",
+ "Custom 1",
+ "Custom 2",
+ "Custom 3",
+ "Custom 4",
+ "Note",
+ 0L
+} ;
+
+void PilotAddressInfo::resetToDefault()
+{
+ FUNCTIONSETUP;
+ // Reset to all 0s
+ memset(&fInfo,0,sizeof(fInfo));
+ // Fill up default categories
+ for (unsigned int i=0; (i<4) && default_address_category_names[i]; ++i)
+ {
+ strncpy(fInfo.category.name[i],default_address_category_names[i],sizeof(fInfo.category.name[0]));
+ }
+ // Weird hack, looks like there's an extra copy of Unfiled
+ strncpy(fInfo.category.name[15],default_address_category_names[0],sizeof(fInfo.category.name[0]));
+
+ // And fill up the default labels.
+ for (unsigned int i=0; (i<19) && default_address_field_labels[i]; ++i)
+ {
+ strncpy(fInfo.labels[i],default_address_field_labels[i],sizeof(fInfo.labels[0]));
+ }
+}
+
+QString PilotAddressInfo::phoneLabel(EPhoneType i) const
+{
+ if (i<=eMobile)
+ {
+ return Pilot::fromPilot(info()->phoneLabels[i]);
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+PhoneSlot::PhoneSlot( const int v )
+{
+ i = entryPhone1;
+ operator=(v);
+}
+
+const PhoneSlot &PhoneSlot::operator=( const int &v )
+{
+ if ( (entryPhone1 <= v) && (v <= entryPhone5) )
+ {
+ i = v;
+ }
+ else
+ {
+ i = invalid;
+ }
+ return *this;
+}
+
+const PhoneSlot &PhoneSlot::operator++()
+{
+ if ( (i!=invalid) && (i<entryPhone5) )
+ {
+ ++i;
+ }
+ else
+ {
+ i = invalid;
+ }
+ return *this;
+}
+
+/* static */ const PhoneSlot PhoneSlot::begin()
+{
+ return PhoneSlot( entryPhone1 );
+}
+
+/* static */ const PhoneSlot PhoneSlot::end()
+{
+ return PhoneSlot( invalid );
+}
+
+unsigned int PhoneSlot::toOffset() const
+{
+ if ( isValid() )
+ {
+ return i-entryPhone1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+unsigned int PhoneSlot::toField() const
+{
+ if ( isValid() )
+ {
+ return i;
+ }
+ else
+ {
+ return entryPhone1;
+ }
+}
+
+PhoneSlot::operator QString() const
+{
+ return QString("%1,%2").arg(toOffset()).arg(toField());
+}
+
+#define MAXFIELDS 19
+
+PilotAddress::PilotAddress(PilotRecord *rec) :
+ PilotRecordBase(rec),
+ fAddressInfo()
+{
+ FUNCTIONSETUPL(4);
+ memset(&fAddressInfo,0,sizeof(fAddressInfo));
+
+ if (rec)
+ {
+ pi_buffer_t b;
+ b.data = (unsigned char *) rec->data();
+ b.allocated = b.used = rec->size();
+ unpack_Address(&fAddressInfo, &b, address_v1);
+ }
+ else
+ {
+ fAddressInfo.phoneLabel[0] = (int) PilotAddressInfo::eWork;
+ fAddressInfo.phoneLabel[1] = (int) PilotAddressInfo::eHome;
+ fAddressInfo.phoneLabel[2] = (int) PilotAddressInfo::eOther;
+ fAddressInfo.phoneLabel[3] = (int) PilotAddressInfo::eMobile;
+ fAddressInfo.phoneLabel[4] = (int) PilotAddressInfo::eEmail;
+ }
+}
+
+PilotAddress::PilotAddress(const PilotAddress & copyFrom) :
+ PilotRecordBase(copyFrom),
+ fAddressInfo()
+{
+ FUNCTIONSETUPL(4);
+ _copyAddressInfo(copyFrom.fAddressInfo);
+}
+
+PilotAddress & PilotAddress::operator = (const PilotAddress & copyFrom)
+{
+ FUNCTIONSETUPL(4);
+ PilotRecordBase::operator = (copyFrom);
+ _copyAddressInfo(copyFrom.fAddressInfo);
+ return *this;
+}
+
+bool PilotAddress::operator==(const PilotAddress &compareTo)
+{
+ FUNCTIONSETUPL(4);
+
+ // now compare all the fields stored in the fAddressInfo.entry array of char*[19]
+ for (int i=0; i<MAXFIELDS; i++) {
+ // if one is NULL, and the other non-empty, they are not equal for sure
+ if ( !getFieldP(i) && compareTo.getFieldP(i))
+ {
+ return false;
+ }
+ if ( getFieldP(i) && !compareTo.getFieldP(i))
+ {
+ return false;
+ }
+
+ // test for getField(i)!=... to prevent strcmp or NULL strings! None or both can be zero, but not a single one.
+ if ( (getFieldP(i) != compareTo.getFieldP(i)) && ( strcmp(getFieldP(i), compareTo.getFieldP(i)) ) )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void PilotAddress::_copyAddressInfo(const struct Address &copyFrom)
+{
+ FUNCTIONSETUPL(4);
+ fAddressInfo.showPhone = copyFrom.showPhone;
+
+ for (int labelLp = 0; labelLp < 5; labelLp++)
+ {
+ fAddressInfo.phoneLabel[labelLp] =
+ copyFrom.phoneLabel[labelLp];
+ }
+
+ for (unsigned int i = 0; i< MAXFIELDS; ++i)
+ {
+ if (copyFrom.entry[i])
+ {
+ fAddressInfo.entry[i] = qstrdup(copyFrom.entry[i]);
+ }
+ else
+ {
+ fAddressInfo.entry[i] = 0L;
+ }
+ }
+}
+
+
+PilotAddress::~PilotAddress()
+{
+ FUNCTIONSETUPL(4);
+ free_Address(&fAddressInfo);
+}
+
+QString PilotAddress::getTextRepresentation(const PilotAddressInfo *info, Qt::TextFormat richText) const
+{
+ QString text, tmp;
+
+ QString par = (richText==Qt::RichText) ?CSL1("<p>"): QString();
+ QString ps = (richText==Qt::RichText) ?CSL1("</p>"):CSL1("\n");
+ QString br = (richText==Qt::RichText) ?CSL1("<br/>"):CSL1("\n");
+
+ // title + name
+ text += par;
+ if (!getField(entryTitle).isEmpty())
+ {
+ text += rtExpand(getField(entryTitle), richText);
+ text += CSL1(" ");
+ }
+
+ tmp = richText ? CSL1("<b><big>%1 %2</big></b>") : CSL1("%1 %2");
+ QString firstName = getField(entryFirstname);
+ if (firstName.isEmpty())
+ {
+ // So replace placeholder for first name (%1) with empty
+ tmp = tmp.arg(QString());
+ }
+ else
+ {
+ tmp = tmp.arg(rtExpand(firstName,richText));
+ }
+ tmp=tmp.arg(rtExpand(getField(entryLastname), richText));
+ text += tmp;
+ text += ps;
+
+ // company
+ if (!getField(entryCompany).isEmpty())
+ {
+ text += par;
+ text += rtExpand(getField(entryCompany), richText);
+ text += ps;
+ }
+
+ // phone numbers (+ labels)
+ text += par;
+ for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
+ {
+ if (!getField(i.toField()).isEmpty())
+ {
+ if (richText)
+ {
+ if (getShownPhone() == i)
+ {
+ tmp=CSL1("<small>%1: </small><b>%2</b>");
+ }
+ else
+ {
+ tmp=CSL1("<small>%1: </small>%2");
+ }
+ }
+ else
+ {
+ tmp=CSL1("%1: %2");
+ }
+ if (info)
+ {
+ tmp=tmp.arg(info->phoneLabel( getPhoneType( i ) ));
+ }
+ else
+ {
+ tmp=tmp.arg(CSL1("Contact: "));
+ }
+ tmp=tmp.arg(rtExpand(getField(i.toField()), richText));
+ text += tmp;
+ text += br;
+ }
+ }
+ text += ps;
+
+ // address, city, state, country
+ text += par;
+ if (!getField(entryAddress).isEmpty())
+ {
+ text += rtExpand(getField(entryAddress), richText);
+ text += br;
+ }
+ if (!getField(entryCity).isEmpty())
+ {
+ text += rtExpand(getField(entryCity), richText);
+ text += CSL1(" ");
+ }
+ if (!getField(entryState).isEmpty())
+ {
+ text += rtExpand(getField(entryState), richText);
+ text += CSL1(" ");
+ }
+ if (!getField(entryZip).isEmpty())
+ {
+ text += rtExpand(getField(entryZip), richText);
+ }
+ text += br;
+ if (!getField(entryCountry).isEmpty())
+ {
+ text += rtExpand(getField(entryCountry), richText);
+ text += br;
+ }
+ text += ps;
+
+ // custom fields
+ text += par;
+ for (int i = entryCustom1; i <= entryCustom4; i++)
+ {
+ if (!getField(i).isEmpty())
+ {
+ text += rtExpand(getField(i), richText);
+ text += br;
+ }
+ }
+ text += ps;
+
+ // category
+ if (info)
+ {
+ QString categoryName = info->categoryName( category() );
+ if (!categoryName.isEmpty())
+ {
+ text += par;
+ text += rtExpand(categoryName, richText);
+ text += ps;
+ }
+ }
+
+ // note
+ if (!getField(entryNote).isEmpty())
+ {
+ text += richText?CSL1("<hr/>"):CSL1("-----------------------------\n");
+ text += par;
+ text += rtExpand(getField(entryNote), richText);
+ text += ps;
+ }
+
+ return text;
+}
+
+QStringList PilotAddress::getEmails() const
+{
+ QStringList list;
+
+ for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
+ {
+ PilotAddressInfo::EPhoneType t = getPhoneType( i );
+ if ( t == PilotAddressInfo::eEmail )
+ {
+ QString s = getField(i.toField());
+ if (!s.isEmpty())
+ {
+ list.append(s);
+ }
+ }
+ }
+
+ return list;
+}
+
+void PilotAddress::setEmails(const QStringList &list)
+{
+ FUNCTIONSETUPL(4);
+ QString test;
+
+ // clear all e-mails first
+ for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
+ {
+ PilotAddressInfo::EPhoneType t = getPhoneType( i );
+ if (t == PilotAddressInfo::eEmail)
+ {
+ setField(i.toField(), QString() );
+ }
+ }
+
+ for(QStringList::ConstIterator listIter = list.begin();
+ listIter != list.end(); ++listIter)
+ {
+ QString email = *listIter;
+ if (!setPhoneField(PilotAddressInfo::eEmail, email, NoFlags).isValid())
+ {
+ WARNINGKPILOT << "Email accounts overflowed, silently dropped." << endl;
+ }
+ }
+}
+
+QString PilotAddress::getField(int field) const
+{
+ if ( (entryLastname <= field) && (field <= entryNote) )
+ {
+ return Pilot::fromPilot(fAddressInfo.entry[field]);
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+PhoneSlot PilotAddress::_getNextEmptyPhoneSlot() const
+{
+ FUNCTIONSETUPL(4);
+ for (PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
+ {
+ const char *phoneField = getFieldP(i.toField());
+
+ if (!phoneField || !phoneField[0])
+ {
+ return i;
+ }
+ }
+ return PhoneSlot();
+}
+
+PhoneSlot PilotAddress::setPhoneField(PilotAddressInfo::EPhoneType type,
+ const QString &field,
+ PhoneHandlingFlags flags)
+{
+ FUNCTIONSETUPL(4);
+
+ const bool overwriteExisting = (flags == Replace);
+ PhoneSlot fieldSlot;
+ if (overwriteExisting)
+ {
+ fieldSlot = _findPhoneFieldSlot(type);
+ }
+
+ if ( !fieldSlot.isValid() )
+ {
+ fieldSlot = _getNextEmptyPhoneSlot();
+ }
+
+ // store the overflow phone
+ if ( !fieldSlot.isValid() )
+ {
+ DEBUGKPILOT << fname << ": Phone would overflow." << endl;
+ }
+ else // phone field 1 - 5; straight forward storage
+ {
+ setField(fieldSlot.toField(), field);
+ fAddressInfo.phoneLabel[fieldSlot.toOffset()] = (int) type;
+ }
+ return fieldSlot;
+}
+
+PhoneSlot PilotAddress::_findPhoneFieldSlot(PilotAddressInfo::EPhoneType t) const
+{
+ FUNCTIONSETUPL(4);
+ for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
+ {
+ if ( getPhoneType(i) == t )
+ {
+ return i;
+ }
+ }
+
+ return PhoneSlot();
+}
+
+QString PilotAddress::getPhoneField(PilotAddressInfo::EPhoneType type) const
+{
+ FUNCTIONSETUPL(4);
+ PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
+
+ if (fieldSlot.isValid())
+ {
+ return getField(fieldSlot.toField());
+ }
+
+ return QString();
+}
+
+PhoneSlot PilotAddress::getShownPhone() const
+{
+ // The slot is stored as an offset
+ return PhoneSlot(entryPhone1 + fAddressInfo.showPhone);
+}
+
+const PhoneSlot &PilotAddress::setShownPhone( const PhoneSlot &v )
+{
+ FUNCTIONSETUPL(4);
+ if (v.isValid())
+ {
+ fAddressInfo.showPhone = v.toOffset();
+ }
+ return v;
+}
+
+PhoneSlot PilotAddress::setShownPhone(PilotAddressInfo::EPhoneType type)
+{
+ FUNCTIONSETUPL(4);
+ PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
+
+ // Did we find a slot with the requested type?
+ if (!fieldSlot.isValid())
+ {
+ // No, so look for first non-empty phone slot
+ for ( fieldSlot = PhoneSlot::begin(); fieldSlot.isValid(); ++fieldSlot )
+ {
+ const char *p = getFieldP(fieldSlot.toField());
+ if (p && p[0])
+ {
+ break;
+ }
+ }
+ // If all of them are empty, then use first slot instead
+ if (!fieldSlot.isValid())
+ {
+ fieldSlot = PhoneSlot::begin();
+ }
+ }
+ setShownPhone(fieldSlot);
+ return fieldSlot;
+}
+
+PilotAddressInfo::EPhoneType PilotAddress::getPhoneType( const PhoneSlot &field ) const
+{
+ if ( field.isValid() )
+ {
+ return (PilotAddressInfo::EPhoneType) fAddressInfo.phoneLabel[field.toOffset()];
+ }
+ else
+ {
+ return PilotAddressInfo::eNone;
+ }
+}
+
+void PilotAddress::setField(int field, const QString &text)
+{
+ FUNCTIONSETUPL(4);
+ // This will have either been created with unpack_Address, and/or will
+ // be released with free_Address, so use malloc/free here:
+ if (fAddressInfo.entry[field])
+ {
+ free(fAddressInfo.entry[field]);
+ fAddressInfo.entry[field]=0L;
+ }
+ if (!text.isEmpty())
+ {
+ fAddressInfo.entry[field] = (char *) malloc(text.length() + 1);
+ Pilot::toPilot(text, fAddressInfo.entry[field], text.length()+1);
+ }
+ else
+ {
+ fAddressInfo.entry[field] = 0L;
+ }
+}
+
+PilotRecord *PilotAddress::pack() const
+{
+ FUNCTIONSETUPL(4);
+ int i;
+
+ pi_buffer_t *b = pi_buffer_new( sizeof(fAddressInfo) );
+ i = pack_Address(const_cast<Address_t *>(&fAddressInfo), b, address_v1);
+ if (i<0)
+ {
+ return 0L;
+ }
+ // pack_Address sets b->used
+ return new PilotRecord( b, this );
+}
diff --git a/kpilot/lib/pilotAddress.h b/kpilot/lib/pilotAddress.h
new file mode 100644
index 000000000..d6dd20e45
--- /dev/null
+++ b/kpilot/lib/pilotAddress.h
@@ -0,0 +1,339 @@
+#ifndef _KPILOT_PILOTADDRESS_H
+#define _KPILOT_PILOTADDRESS_H
+/* pilotAddress.h KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2007 by Adriaan de Groot <groot@kde.org>
+**
+** This is a wrapper for pilot-link's address structures.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <pi-macros.h>
+#include <pi-address.h>
+
+#include <qnamespace.h>
+
+#include "pilotRecord.h"
+#include "pilotAppInfo.h"
+
+/** Interpreted form of the AppInfo block in the address database. */
+typedef PilotAppInfo<
+ AddressAppInfo,
+ unpack_AddressAppInfo,
+ pack_AddressAppInfo> PilotAddressInfo_;
+
+/** This class exists @em only to clear up the type mess that
+* is the field-numbers-and-indexes for phone numbers in the
+* handheld records. The standard address record has 19 fields,
+* five of which are phone fields. Those are fields 3..7 and they
+* are referred to as fields Phone1 .. Phone5. Sometimes we
+* need to act as if the phone field numbers are indeed the field
+* numbers (3..7) and sometimes we need to use those same field
+* numbers to index into a C array (0 based!) so then we map
+* field number 3 (Phone1) to a 0 index.
+*
+* Also handles iteration nicely.
+*
+* A phone slot value may be invalid. If so, operations on it will
+* fail (yielding invalid again) and isValid() will return @c false.
+*/
+class PhoneSlot
+{
+friend class PilotAddress;
+protected:
+ /** Constructor. Use the specified value for the phone slot.
+ * @p v is a field number (3..8).
+ */
+ explicit PhoneSlot( const int v );
+
+ /** Assignment operator. Set the value of the slot to
+ * the specified value @p v . This may yield an invalid
+ * phone slot.
+ */
+ const PhoneSlot &operator=(const int &v);
+
+ /** Map the slot to an offset (for use in finding the phone type
+ * for a given slot).
+ * @return Offset of this slot within the phone fields.
+ */
+ unsigned int toOffset() const;
+
+ /** Map the slot to a field number. */
+ unsigned int toField() const;
+
+public:
+ static const int invalid = -1; ///< Value for invalid slots. */
+
+ /** Constructor. The slot is invalid. */
+ PhoneSlot()
+ {
+ i = invalid;
+ }
+
+ /** Comparison operator. */
+ bool operator ==( const PhoneSlot &v ) const
+ {
+ return v.i == i;
+ }
+
+ /** Iterator operation. Go to the next slot (or invalid when
+ * the range runs out).
+ */
+ const PhoneSlot &operator++();
+
+ /** Begin value of an iteration through the phone slots. */
+ static const PhoneSlot begin();
+
+ /** When the slot range runs out (past entryPhone5) it
+ * is invalid, so the end compares with that.
+ */
+ static const PhoneSlot end();
+
+ /** Valid slots are entryPhone1 (3) through entryPhone5 (7).
+ * @return @c true if the slot is valid.
+ */
+ bool isValid() const
+ {
+ return (entryPhone1 <= i) && (i <= entryPhone5);
+ }
+
+ operator QString() const;
+private:
+ int i;
+} ;
+
+
+class PilotAddressInfo : public PilotAddressInfo_
+{
+public:
+ PilotAddressInfo(PilotDatabase *d) : PilotAddressInfo_(d)
+ {
+ }
+
+ /** This resets the entire AppInfo block to one as it would be
+ * in an English-language handheld, with 3 categories and
+ * default field labels for everything.
+ */
+ void resetToDefault();
+
+ enum EPhoneType {
+ eWork=0,
+ eHome,
+ eFax,
+ eOther,
+ eEmail,
+ eMain,
+ ePager,
+ eMobile,
+ eNone=-1
+ } ;
+
+ QString phoneLabel(EPhoneType i) const;
+} ;
+
+/** @brief A wrapper class around the Address struct provided by pi-address.h
+ *
+ * This class allows the user to set and get address field values.
+ * For everything but phone fields, the user can simply pass the
+ * the pi-address enum for the index for setField() and getField() such
+ * as entryLastname.
+ *
+ * Phone fields are a bit trickier. The structure allows for 8 possible
+ * phone fields with 5 possible slots. That means there could be three
+ * fields that don't have available storage. The setPhoneField() method
+ * will attempt to store the extra fields in a custom field if there
+ * is an overflow.
+ *
+ * There are eight possible fields for 5 view slots:
+ * - fields: Work, Home, Fax, Other, Pager, Mobile, E-mail, Main
+ * - slots: entryPhone1, entryPhone2, entryPhone3, entryPhone4, entryPhone5
+ *
+ * Internally in the pilot-link library, the AddressAppInfo phone
+ * array stores the strings for the eight possible phone values.
+ * Their English string values are :
+ * - phone[0] = Work
+ * - phone[1] = Home
+ * - phone[2] = Fax
+ * - phone[3] = Other
+ * - phone[4] = E-mail
+ * - phone[5] = Main
+ * - phone[6] = Pager
+ * - phone[7] = Mobile
+ *
+ * Apparently, this order is kept for all languages, just with localized
+ * strings. The implementation of the internal methods will assume
+ * this order is kept. In other languages, main can replaced with
+ * Corporation.
+ */
+class KDE_EXPORT PilotAddress : public PilotRecordBase
+{
+public:
+ PilotAddress(PilotRecord *rec = 0L);
+ PilotAddress(const PilotAddress &copyFrom);
+ PilotAddress& operator=( const PilotAddress &r );
+ bool operator==(const PilotAddress &r);
+
+ virtual ~PilotAddress();
+
+ /** Returns a text representation of the address. If @p richText is true, the
+ * text will be formatted with Qt-HTML tags. The AppInfo structure @p info
+ * is used to figure out the phone labels; if it is NULL then bogus labels are
+ * used to identify phone types.
+ */
+ QString getTextRepresentation(const PilotAddressInfo *info, Qt::TextFormat richText) const;
+
+ /**
+ * @param text set the field value
+ * @param field int values associated with the enum defined in
+ * pi-address.h.
+ * The copied possible enum's are: (copied from pi-address.h on 1/12/01)
+ * enum { entryLastname, entryFirstname, entryCompany,
+ * entryPhone1, entryPhone2, entryPhone3, entryPhone4, entryPhone5,
+ * entryAddress, entryCity, entryState, entryZip, entryCountry,
+ * entryTitle, entryCustom1, entryCustom2, entryCustom3, entryCustom4,
+ * entryNote };
+ */
+ void setField(int field, const QString &text);
+ /** Set a field @p i to a given text value. Uses the phone slots only. */
+ void setField(const PhoneSlot &i, const QString &t)
+ {
+ if (i.isValid())
+ {
+ setField(i.toField(),t);
+ }
+ }
+
+ /** Returns the text value of a given field @p field (or QString::null
+ * if there is no such field).
+ */
+ QString getField(int field) const;
+ /** Returns the value of the phone field @p i . */
+ QString getField(const PhoneSlot &i) const
+ {
+ return i.isValid() ? getField(i.toField()) : QString();
+ }
+
+ /**
+ * Return list of all email addresses. This will search through our "phone"
+ * fields and will return only those which are e-mail addresses.
+ */
+ QStringList getEmails() const;
+ void setEmails(const QStringList &emails);
+
+ enum PhoneHandlingFlags
+ {
+ NoFlags=0, ///< No special handling
+ Replace ///< Replace existing entries of same type
+ } ;
+
+ /**
+ * @param type is the type of phone
+ * @param checkCustom4 flag if true, checks the entryCustom4 field
+ * for extra phone fields
+ * @return the field associated with the type
+ */
+ QString getPhoneField(PilotAddressInfo::EPhoneType type) const;
+
+ /**
+ * @param type is the type of phone
+ * @param field is value to store
+ * @param overflowCustom is true, and entryPhone1 to entryPhone5 is full
+ * it will use entryCustom4 field to store the field
+ * @param overwriteExisting is true, it will overwrite an existing record-type
+ * with the field, else it will always search for the first available slot
+ * @return index of the field that this information was set to
+ */
+ PhoneSlot setPhoneField(PilotAddressInfo::EPhoneType type, const QString &value, PhoneHandlingFlags flags);
+
+ /**
+ * Returns the slot of the phone number
+ * selected by the user to be shown in the
+ * overview of addresses.
+ *
+ * @return Slot of phone entry (between entryPhone1 and entryPhone5)
+ */
+ PhoneSlot getShownPhone() const;
+
+ /**
+ * Set the shown phone (the one preferred by the user for display
+ * on the handheld's overview page) to the @em type (not index)
+ * indicated. Looks through the phone entries of this record to
+ * find the first one one of this type.
+ *
+ * @return Slot of phone entry.
+ *
+ * @note Sets the shown phone to the first entry if no field of
+ * type @p phoneType can be found @em and no Home phone
+ * field (the fallback) can be found either.
+ */
+ PhoneSlot setShownPhone(PilotAddressInfo::EPhoneType phoneType);
+
+ /**
+ * Set the shown phone (the one preferred by the user for display
+ * on the handheld's overview page) to the given @p slot .
+ *
+ * @return @p v
+ */
+ const PhoneSlot &setShownPhone(const PhoneSlot &v);
+
+ /** Get the phone type (label) for a given field @p field
+ * in the record. The @p field must be within the
+ * phone range (entryPhone1 .. entryPhone5).
+ *
+ * @return Phone type for phone field @p field .
+ * @return @c eNone (fake phone type) if @p field is invalid.
+ */
+ PilotAddressInfo::EPhoneType getPhoneType(const PhoneSlot &field) const;
+
+ PilotRecord *pack() const;
+
+ const struct Address *address() const { return &fAddressInfo; } ;
+
+
+protected:
+ // Get the pointers in cases where no conversion to
+ // unicode is desired.
+ //
+ const char *getFieldP(int field) const
+ {
+ return fAddressInfo.entry[field];
+ }
+
+private:
+ void _copyAddressInfo(const struct Address &copyFrom);
+ PhoneSlot _getNextEmptyPhoneSlot() const;
+
+ /** @return entryPhone1 to entryPhone5 if the appTypeNum number is
+ * found in the phoneLabel array; return -1 if not found
+ */
+ PhoneSlot _findPhoneFieldSlot(PilotAddressInfo::EPhoneType t) const;
+
+ struct Address fAddressInfo;
+};
+
+
+
+
+#endif
diff --git a/kpilot/lib/pilotAppInfo.cc b/kpilot/lib/pilotAppInfo.cc
new file mode 100644
index 000000000..e8caf6234
--- /dev/null
+++ b/kpilot/lib/pilotAppInfo.cc
@@ -0,0 +1,77 @@
+/* pilotAppInfo.cc KPilot
+**
+** Copyright (C) 2005-2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "options.h"
+
+#include <stdio.h>
+
+#include "pilotAppInfo.h"
+
+PilotAppInfoBase::PilotAppInfoBase(PilotDatabase *d) :
+ fC( 0L ),
+ fLen(0),
+ fOwn(true)
+{
+ FUNCTIONSETUP;
+ int appLen = Pilot::MAX_APPINFO_SIZE;
+ unsigned char buffer[Pilot::MAX_APPINFO_SIZE];
+
+ if (!d || !d->isOpen())
+ {
+ WARNINGKPILOT << "Bad database pointer." << endl;
+ fLen = 0;
+ KPILOT_DELETE( fC );
+ return;
+ }
+
+ fC = new struct CategoryAppInfo;
+ fLen = appLen = d->readAppBlock(buffer,appLen);
+ unpack_CategoryAppInfo(fC, buffer, appLen);
+}
+
+PilotAppInfoBase::~PilotAppInfoBase()
+{
+ if (fOwn)
+ {
+ delete fC;
+ }
+}
+
+bool PilotAppInfoBase::setCategoryName(unsigned int i, const QString &s)
+{
+ if ( (i>=Pilot::CATEGORY_COUNT) || // bad category number
+ (!categoryInfo())) // Nowhere to write to
+ {
+ return false;
+ }
+
+ (void) Pilot::toPilot(s, categoryInfo()->name[i], Pilot::CATEGORY_SIZE - 1);
+ return true;
+}
+
+
diff --git a/kpilot/lib/pilotAppInfo.h b/kpilot/lib/pilotAppInfo.h
new file mode 100644
index 000000000..d5db9e358
--- /dev/null
+++ b/kpilot/lib/pilotAppInfo.h
@@ -0,0 +1,216 @@
+#ifndef _KPILOT_PILOTAPPINFO_H
+#define _KPILOT_PILOTAPPINFO_H
+/* pilotAppInfo.h KPilot
+**
+** Copyright (C) 2005-2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "pilotLinkVersion.h"
+
+#include "pilot.h"
+#include "pilotDatabase.h"
+
+/**
+* A database on the handheld has an "AppInfo" block at the beginning
+* with some database-specific information and a common part.
+* This base class deals with the common part, the categories.
+*
+* Most data in the handheld is stored in @em categories ; every
+* record in every database, for instance, has a category assigned
+* to it (perhaps "Unfiled", but that's just another category).
+*
+* Every database has a category table assigning labels to the
+* categories that exist. There are CATEGORY_COUNT (16) categories
+* available for each database; labels may vary per database.
+*
+* This class encapsulates the basic category table manipulations.
+*/
+class KDE_EXPORT PilotAppInfoBase
+{
+protected:
+ /** Initialize class members after reading header, to alias data elsewhere.
+ * Only for use by the (derived) template classes below.
+ */
+ void init(struct CategoryAppInfo *c, int len)
+ {
+ fC = c;
+ fLen = len ;
+ } ;
+
+public:
+ /** Constructor. This is for use by derived classes (using the template below
+ * only, and says that the category info in the base class aliases data in
+ * the derived class. Remember to call init()!
+ */
+ PilotAppInfoBase() : fC(0L), fLen(0), fOwn(false) { } ;
+
+ /** Constructor, intended for untyped access to the AppInfo only. This throws
+ * away everything but the category information. In this variety, the
+ * CategoryAppInfo structure is owned by the PilotAppInfoBase object.
+ */
+ PilotAppInfoBase(PilotDatabase *d);
+
+ /** Destructor. */
+ virtual ~PilotAppInfoBase();
+
+ /** Retrieve the most basic part of the AppInfo block -- the category
+ * information which is guaranteed to be the first 240-odd bytes of
+ * a database.
+ */
+ struct CategoryAppInfo *categoryInfo()
+ {
+ return fC;
+ } ;
+
+ /** Const version of the above function. */
+ inline const struct CategoryAppInfo *categoryInfo() const
+ {
+ return fC;
+ } ;
+
+ /** Returns the length of the (whole) AppInfo block. */
+ inline PI_SIZE_T length() const
+ {
+ return fLen;
+ } ;
+
+ /** @see findCategory(const QString &name, bool unknownIsUnfiled, struct CategoryAppInfo *info). */
+ inline int findCategory(const QString &name, bool unknownIsUnfiled = false) const
+ {
+ return Pilot::findCategory(fC,name,unknownIsUnfiled);
+ } ;
+
+ /** Gets a single category name. Returns QString::null if there is no
+ * such category number @p i . */
+ inline QString categoryName(unsigned int i) const
+ {
+ return Pilot::categoryName(fC,i);
+ }
+
+ /** Sets a category name. @return true if this succeeded. @return false
+ * on failure, e.g. the index @p i was out of range or the category name
+ * was invalid. Category names that are too long are truncated to 15 characters.
+ */
+ bool setCategoryName(unsigned int i, const QString &s);
+
+ /** For debugging, display all the category names */
+ inline void dump() const
+ {
+ Pilot::dumpCategories(fC);
+ };
+
+protected:
+ struct CategoryAppInfo *fC;
+ PI_SIZE_T fLen;
+
+ bool fOwn;
+} ;
+
+/** A template class for reading and interpreting AppInfo blocks;
+* the idea is that it handles all the boilerplate code for reading
+* the app block, converting it to the right kind, and then unpacking
+* it. Template parameters are the type (struct, from pilot-link probably)
+* of the interpreted appinfo, and the pack and unpack functions for it
+* (again, from pilot-link).
+*/
+template <typename appinfo,
+#if PILOT_LINK_IS(0,12,2)
+ /* There are additional consts introduced in 0.12.2 */
+ int(*unpack)(appinfo *, const unsigned char *, PI_SIZE_T),
+ int(*pack)(const appinfo *, unsigned char *, PI_SIZE_T)
+#else
+ int(*unpack)(appinfo *, unsigned char *, PI_SIZE_T),
+ int(*pack)(appinfo *, unsigned char *, PI_SIZE_T)
+#endif
+ >
+class PilotAppInfo : public PilotAppInfoBase
+{
+public:
+ /** Constructor. Read the appinfo from database @p d and
+ * interpret it.
+ */
+ PilotAppInfo(PilotDatabase *d) : PilotAppInfoBase()
+ {
+ int appLen = Pilot::MAX_APPINFO_SIZE;
+ unsigned char buffer[Pilot::MAX_APPINFO_SIZE];
+
+ memset(&fInfo,0,sizeof(fInfo));
+ if (d && d->isOpen())
+ {
+ appLen = d->readAppBlock(buffer,appLen);
+ (*unpack)(&fInfo, buffer, appLen);
+ // fInfo is just a struct, so we can point to it anyway.
+ init(&fInfo.category,appLen);
+ }
+ else
+ {
+ delete fC;
+ fC = 0L;
+ fLen = 0;
+ init(&fInfo.category,sizeof(fInfo));
+ }
+ } ;
+
+ PilotAppInfo()
+ {
+ memset(&fInfo,0,sizeof(fInfo));
+ init(&fInfo.category,sizeof(fInfo));
+ }
+
+
+ /** Write this appinfo block to the database @p d; returns
+ * the number of bytes written or -1 on failure. This
+ * function is robust when called with a NULL database @p d.
+ */
+ int writeTo(PilotDatabase *d)
+ {
+ unsigned char buffer[Pilot::MAX_APPINFO_SIZE];
+ if (!d || !d->isOpen())
+ {
+ return -1;
+ }
+ int appLen = (*pack)(&fInfo, buffer, length());
+ if (appLen > 0)
+ {
+ d->writeAppBlock(buffer,appLen);
+ }
+ return appLen;
+ } ;
+
+ /** Returns a (correctly typed) pointer to the interpreted
+ * appinfo block.
+ */
+ appinfo *info() { return &fInfo; } ;
+ /** Returns a const (correctly typed) pointer to the interpreted
+ * appinfo block.
+ */
+ const appinfo *info() const { return &fInfo; } ;
+
+protected:
+ appinfo fInfo;
+} ;
+
+
+#endif
diff --git a/kpilot/lib/pilotCard.h b/kpilot/lib/pilotCard.h
new file mode 100644
index 000000000..86d1f70c6
--- /dev/null
+++ b/kpilot/lib/pilotCard.h
@@ -0,0 +1,65 @@
+#ifndef _KPILOT_PILOTCARD_H
+#define _KPILOT_PILOTCARD_H
+/* pilotCard.h KPilot
+**
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This class is a wrapper around pilot-link's CardInfo structure
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef _PILOT_DLP_H_
+#include <pi-dlp.h>
+#endif
+
+class KPilotCard
+{
+public:
+ KPilotCard() { ::memset(&fCard,0,sizeof(struct CardInfo)); }
+ KPilotCard(const CardInfo* card) { fCard = *card; }
+
+ CardInfo *cardInfo() { return &fCard; }
+
+ /**
+ * Ensures the names are properly terminated. Needed incase we
+ * are syncing a new and bogus pilot.
+ */
+ void boundsCheck() {}
+
+ const int getCardIndex() const { return fCard.card; }
+ const int getCardVersion() const { return fCard.version; }
+ unsigned long getRomSize() const { return fCard.romSize; }
+ unsigned long getRamSize() const { return fCard.ramSize; }
+ unsigned long getRamFree() const { return fCard.ramFree; }
+ const char* getCardName() const { return fCard.name; }
+ const char* getCardManufacturer() const { return fCard.manufacturer; }
+
+private:
+ struct CardInfo fCard;
+};
+
+#endif
diff --git a/kpilot/lib/pilotDatabase.cc b/kpilot/lib/pilotDatabase.cc
new file mode 100644
index 000000000..fd568b703
--- /dev/null
+++ b/kpilot/lib/pilotDatabase.cc
@@ -0,0 +1,112 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2005-2006 Adriaan de Groot <groot@kde.org>
+**
+** This is the abstract base class for databases, which is used both
+** by local databases and by the serial databases held in the Pilot.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <time.h> // Needed by pilot-link include
+#include <pi-appinfo.h>
+
+#include <qstringlist.h>
+
+#include <kglobal.h>
+
+#include "pilotDatabase.h"
+#include "pilotRecord.h"
+
+static int creationCount = 0;
+static QStringList *createdNames = 0L;
+
+PilotDatabase::PilotDatabase(const QString &s) :
+ fDBOpen(false),
+ fName(s)
+{
+ FUNCTIONSETUP;
+ creationCount++;
+ if (!createdNames)
+ {
+ createdNames = new QStringList();
+ }
+ createdNames->append(s.isEmpty() ? CSL1("<empty>") : s);
+}
+
+/* virtual */ PilotDatabase::~PilotDatabase()
+{
+ FUNCTIONSETUP;
+ creationCount--;
+ if (createdNames)
+ {
+ createdNames->remove(fName.isEmpty() ? CSL1("<empty>") : fName);
+ }
+}
+
+/* static */ int PilotDatabase::instanceCount()
+{
+ FUNCTIONSETUP;
+ DEBUGKPILOT << fname << ": " << creationCount << " databases." << endl;
+ if (createdNames)
+ {
+ DEBUGKPILOT << fname << ": "
+ << createdNames->join(CSL1(",")) << endl;
+ }
+ return creationCount;
+}
+
+/* virtual */ Pilot::RecordIDList PilotDatabase::idList()
+{
+ Pilot::RecordIDList l;
+
+ for (unsigned int i = 0 ; ; i++)
+ {
+ PilotRecord *r = readRecordByIndex(i);
+ if (!r) break;
+ l.append(r->id());
+ delete r;
+ }
+
+ return l;
+}
+
+/* virtual */ Pilot::RecordIDList PilotDatabase::modifiedIDList()
+{
+ Pilot::RecordIDList l;
+
+ resetDBIndex();
+ while(1)
+ {
+ PilotRecord *r = readNextModifiedRec();
+ if (!r) break;
+ l.append(r->id());
+ delete r;
+ }
+
+ return l;
+}
+
diff --git a/kpilot/lib/pilotDatabase.h b/kpilot/lib/pilotDatabase.h
new file mode 100644
index 000000000..84c922ed4
--- /dev/null
+++ b/kpilot/lib/pilotDatabase.h
@@ -0,0 +1,272 @@
+#ifndef _KPILOT_PILOTDATABASE_H
+#define _KPILOT_PILOTDATABASE_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2005-2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "pilot.h"
+
+
+/** @file
+* This is the abstract base class for databases, which is used both
+* by local databases and by the serial databases held in the Pilot.
+*/
+
+
+/**
+ * Methods to access a database on the pilot.
+ *
+ * NOTE: It is the users responsibility
+ * to delete PilotRecords returned by
+ * PilotDatabase methods when finished with them!
+ */
+
+class KDE_EXPORT PilotDatabase
+{
+public:
+ PilotDatabase(const QString &name = QString::null);
+ virtual ~PilotDatabase();
+
+
+ QString name() const { return fName; } ;
+
+ /**
+ * Debugging information: tally how many databases are created
+ * or destroyed. Returns the count of currently existing databases.
+ */
+ static int instanceCount();
+
+ /* -------------------- Abstract interface for subclasses ----------------- */
+
+ /**
+ * Creates the database with the given creator, type and flags
+ * on the given card (default is RAM). If the database already
+ * exists, this function does nothing.
+ */
+ virtual bool createDatabase(long creator=0, long type=0,
+ int cardno=0, int flags=0, int version=0) = 0;
+
+ /**
+ * Deletes the database (by name, as given in the constructor,
+ * the database name is stored depending on the implementation
+ * of PilotLocalDatabase and PilotSerialDatabas)
+ */
+ virtual int deleteDatabase()=0;
+
+ /** Reads the application block info, returns size. */
+ virtual int readAppBlock(unsigned char* buffer, int maxLen) = 0;
+
+ /** Writes the application block info. */
+ virtual int writeAppBlock(unsigned char* buffer, int len) = 0;
+
+ /** Returns the number of records in the database.
+ * If the database is not open, return -1.
+ */
+ virtual unsigned int recordCount() const=0;
+
+ /** Returns a QValueList of all record ids in the database.
+ This implementation is really bad. */
+ virtual Pilot::RecordIDList idList();
+
+ /** Returns a list of all record ids that have been modified in the
+ database. This implementation is really bad. */
+ virtual Pilot::RecordIDList modifiedIDList();
+
+
+ /** Reads a record from database by id, returns record length */
+ virtual PilotRecord* readRecordById(recordid_t id) = 0;
+
+ /** Reads a record from database, returns the record length */
+ virtual PilotRecord* readRecordByIndex(int index) = 0;
+
+ /** Reads the next record from database in category 'category' */
+ virtual PilotRecord* readNextRecInCategory(int category) = 0;
+
+ /**
+ * Reads the next record from database that has the dirty flag set.
+ * If @p ind is non-NULL, *ind is set to the index of the current
+ * record (i.e. before the record pointer moves to the next
+ * modified record).
+ */
+ virtual PilotRecord* readNextModifiedRec(int *ind=NULL) = 0;
+
+ /**
+ * Writes a new record to database (if 'id' == 0, one will be
+ * assigned to newRecord)
+ */
+ virtual recordid_t writeRecord(PilotRecord* newRecord) = 0;
+
+ /**
+ * Deletes a record with the given recordid_t from the database,
+ * or all records, if @p all is set to true. The recordid_t will
+ * be ignored in this case.
+ *
+ * Return value is negative on error, 0 otherwise.
+ */
+ virtual int deleteRecord(recordid_t id, bool all=false) = 0;
+
+ /** Resets all records in the database to not dirty. */
+ virtual int resetSyncFlags() = 0;
+
+ /** Resets next record index to beginning */
+ virtual int resetDBIndex() = 0;
+
+ /** Purges all Archived/Deleted records from Palm Pilot database */
+ virtual int cleanup() = 0;
+
+ bool isOpen() const { return fDBOpen; }
+
+ /** Returns some sensible human-readable identifier for
+ * the database. Serial databases get Pilot:, local
+ * databases return the full path.
+ */
+ virtual QString dbPathName() const = 0;
+
+ /**
+ * Use this instead of RTTI to determine the type of a
+ * PilotDatabase, for those cases where it's important.
+ */
+ typedef enum { eNone=0,
+ eLocalDB=1,
+ eSerialDB=2 } DBType;
+ virtual DBType dbType() const = 0;
+
+ static inline bool isResource(struct DBInfo *info)
+ {
+ return (info->flags & dlpDBFlagResource);
+ }
+
+protected:
+ virtual void openDatabase() = 0;
+ virtual void closeDatabase() = 0;
+
+ void setDBOpen(bool yesno) { fDBOpen = yesno; }
+
+private:
+ bool fDBOpen;
+ QString fName;
+};
+
+/** A template class for reading and interpreting a database. This removes
+* the need for a lot of boilerplate code that does the conversions.
+* Parameters are two interpretation classes: one for the KDE side of
+* things (e.g. Event) and one that interprets the Pilot's records into
+* a more sensible structure (e.g. PilotDatebookEntry). The mapping from
+* the KDE type to the Pilot type and vice-versa is done by the mapper
+* class's convert() functions.
+*
+* To interpret a database as pilot-link interpretations (e.g. as
+* PilotDatebookEntry records, not as Events) use the NullMapper class
+* below in combination with a template instantiation with kdetype==pilottype.
+*
+* The database interpreter intentionally has an interface similar to
+* that of a PilotDatabase, but it isn't one.
+*/
+template <class kdetype, class pilottype, class mapper>
+class DatabaseInterpreter
+{
+private:
+ /** Interpret a PilotRecord as an object of type kdetype. */
+ kdetype *interpret(PilotRecord *r)
+ {
+ // NULL records return NULL kde objects.
+ if (!r) return 0;
+ // Interpret the binary blob as a pilot-link object.
+ pilottype *a = new pilottype(r);
+ // The record is now obsolete.
+ delete r;
+ // Interpretation failed.
+ if (!a) { return 0; }
+ // Now convert to KDE type.
+ kdetype *t = mapper::convert(a);
+ // The NULL mapper just returns the pointer a, so we
+ // need to check if anything has changed before deleting.
+ if ( (void *)t != (void *)a )
+ {
+ delete a;
+ }
+ return t;
+ }
+public:
+ /** Constructor. Interpret the database @p d. */
+ DatabaseInterpreter(PilotDatabase *d) : fDB(d) { } ;
+
+ /** Reads a record from database by @p id */
+ kdetype *readRecordById(recordid_t id)
+ {
+ return interpret(fDB->readRecordById(id));
+ }
+
+ /** Reads a record from database with index @p index */
+ kdetype *readRecordByIndex(int index)
+ {
+ return interpret(fDB->readRecordByIndex(index));
+ }
+
+ /** Reads the next record from database in category @p category */
+ kdetype *readNextRecInCategory(int category)
+ {
+ return interpret(fDB->readNextRecInCategory(category));
+ }
+
+ /**
+ * Reads the next record from database that has the dirty flag set.
+ * If @p ind is non-NULL, *ind is set to the index of the current
+ * record (i.e. before the record pointer moves to the next
+ * modified record).
+ */
+ kdetype *readNextModifiedRec(int *ind=NULL)
+ {
+ return interpret(fDB->readNextModifiedRec(ind));
+ }
+
+
+ /** Retrieve the database pointer; this is useful to just pass
+ * around DatabaseInterpreter objects as if they are databases,
+ * and then perform DB operations on the database it wraps.
+ */
+ PilotDatabase *db() const { return fDB; }
+
+protected:
+ PilotDatabase *fDB;
+} ;
+
+/** NULL mapper class; the conversions here don't @em do anything,
+* so you can use this when you only need 1 conversion step (from
+* PilotRecord to PilotDatebookEntry, for instance) instead of 2.
+*/
+template <class T>
+class NullMapper
+{
+public:
+ /** NULL Conversion function. */
+ static T *convert(T *t) { return t; }
+} ;
+
+#endif
diff --git a/kpilot/lib/pilotDateEntry.cc b/kpilot/lib/pilotDateEntry.cc
new file mode 100644
index 000000000..4fa93eac6
--- /dev/null
+++ b/kpilot/lib/pilotDateEntry.cc
@@ -0,0 +1,478 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This is a C++ wrapper for the Pilot's datebook structures.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <stdlib.h>
+
+#include <qdatetime.h>
+#include <qnamespace.h>
+#include <qregexp.h>
+
+#include <kglobal.h>
+
+#include "pilotDateEntry.h"
+
+static const char *default_date_category_names[] = {
+ "Unfiled",
+ "Business",
+ "Personal",
+ 0L
+} ;
+
+void PilotDateInfo::resetToDefault()
+{
+ FUNCTIONSETUP;
+ // Reset to all 0s
+ memset(&fInfo,0,sizeof(fInfo));
+ // Fill up default categories
+ for (unsigned int i=0; (i<4) && default_date_category_names[i]; ++i)
+ {
+ strncpy(fInfo.category.name[i],default_date_category_names[i],sizeof(fInfo.category.name[0]));
+ }
+
+ fInfo.startOfWeek = 0;
+
+}
+
+
+PilotDateEntry::PilotDateEntry():PilotRecordBase()
+{
+ ::memset(&fAppointmentInfo, 0, sizeof(struct Appointment));
+}
+
+/* initialize the entry from another one. If rec==NULL, this constructor does the same as PilotDateEntry()
+*/
+PilotDateEntry::PilotDateEntry(PilotRecord * rec) :
+ PilotRecordBase(rec)
+{
+ ::memset(&fAppointmentInfo, 0, sizeof(fAppointmentInfo));
+ if (rec)
+ {
+ // Construct a fake pi_buffer for unpack_Appointment.
+ // No ownership changes occur here.
+ pi_buffer_t b = { (unsigned char *) rec->data(), rec->size(), rec->size() } ;
+ unpack_Appointment(&fAppointmentInfo, &b, datebook_v1);
+ }
+ return;
+
+}
+
+void PilotDateEntry::_copyExceptions(const PilotDateEntry & e)
+{
+ if (e.fAppointmentInfo.exceptions > 0)
+ {
+ size_t blocksize = e.fAppointmentInfo.exceptions *
+ sizeof(struct tm);
+
+ fAppointmentInfo.exception = (struct tm *)::malloc(blocksize);
+
+ if (fAppointmentInfo.exception)
+ {
+ fAppointmentInfo.exceptions =
+ e.fAppointmentInfo.exceptions;
+ ::memcpy(fAppointmentInfo.exception,
+ e.fAppointmentInfo.exception, blocksize);
+ }
+ else
+ {
+ WARNINGKPILOT << "malloc() failed, exceptions not copied" << endl;
+ fAppointmentInfo.exceptions = 0;
+ }
+ }
+ else
+ {
+ fAppointmentInfo.exceptions = 0;
+ fAppointmentInfo.exception = 0L;
+ }
+}
+
+
+PilotDateEntry::PilotDateEntry(const PilotDateEntry & e) :
+ PilotRecordBase(e)
+{
+ ::memcpy(&fAppointmentInfo, &e.fAppointmentInfo,
+ sizeof(struct Appointment));
+ // See operator = for explanation
+ fAppointmentInfo.exception = 0L;
+ fAppointmentInfo.description = 0L;
+ fAppointmentInfo.note = 0L;
+
+ _copyExceptions(e);
+ setDescriptionP(e.fAppointmentInfo.description);
+ setNoteP(e.fAppointmentInfo.note);
+}
+
+
+PilotDateEntry & PilotDateEntry::operator = (const PilotDateEntry & e)
+{
+ if (this != &e) // Pointer equality!
+ {
+ KPILOT_FREE(fAppointmentInfo.exception);
+ KPILOT_FREE(fAppointmentInfo.description);
+ KPILOT_FREE(fAppointmentInfo.note);
+ ::memcpy(&fAppointmentInfo, &e.fAppointmentInfo,
+ sizeof(fAppointmentInfo));
+
+ // The original pointers were already freed; since we're now
+ // got the pointers from the new structure and we're going
+ // to use the standard set functions make sure that
+ // we don't free() the copies-of-pointers from e, which
+ // would be disastrous.
+ //
+ //
+ fAppointmentInfo.exception = 0L;
+ fAppointmentInfo.description = 0L;
+ fAppointmentInfo.note = 0L;
+
+ _copyExceptions(e);
+ setDescriptionP(e.fAppointmentInfo.description);
+ setNoteP(e.fAppointmentInfo.note);
+ }
+
+ return *this;
+} // end of assignment operator
+
+
+QString PilotDateEntry::getTextRepresentation(Qt::TextFormat richText)
+{
+ QString text, tmp;
+ QString par = (richText==Qt::RichText) ?CSL1("<p>"):QString::null;
+ QString ps = (richText==Qt::RichText) ?CSL1("</p>"):CSL1("\n");
+ QString br = (richText==Qt::RichText) ?CSL1("<br/>"):CSL1("\n");
+
+ // title + name
+ text += par;
+ tmp=richText?CSL1("<b><big>%1</big></b>"):CSL1("%1");
+ text += tmp.arg(rtExpand(getDescription(), richText));
+ text += ps;
+
+ QDateTime dt(readTm(getEventStart()));
+ QString startDate(dt.toString(Qt::LocalDate));
+ text+=par;
+ text+=i18n("Start date: %1").arg(startDate);
+ text+=ps;
+
+ if (isEvent())
+ {
+ text+=par;
+ text+=i18n("Whole-day event");
+ text+=ps;
+ }
+ else
+ {
+ dt=readTm(getEventEnd());
+ QString endDate(dt.toString(Qt::LocalDate));
+ text+=par;
+ text+=i18n("End date: %1").arg(endDate);
+ text+=ps;
+ }
+
+ if ( isAlarmEnabled() )
+ {
+ text+=par;
+ tmp=i18n("%1 is the duration, %2 is the time unit", "Alarm: %1 %2 before event starts").
+ arg(getAdvance());
+ switch (getAdvanceUnits())
+ {
+ case advMinutes: tmp=tmp.arg(i18n("minutes")); break;
+ case advHours: tmp=tmp.arg(i18n("hours")); break;
+ case advDays: tmp=tmp.arg(i18n("days")); break;
+ default: tmp=tmp.arg(QString::null); break;;
+ }
+ text+=tmp;
+ text+=ps;
+ }
+
+ if (getRepeatType() != repeatNone)
+ {
+ text+=par;
+ tmp=i18n("Recurrence: every %1 %2");
+ int freq = getRepeatFrequency();
+ tmp=tmp.arg(freq);
+
+ switch(getRepeatType())
+ {
+ case repeatDaily: tmp=tmp.arg(i18n("day(s)")); break;
+ case repeatWeekly: tmp=tmp.arg(i18n("week(s)")); break;
+ case repeatMonthlyByDay:
+ case repeatMonthlyByDate: tmp=tmp.arg(i18n("month(s)")); break;
+ case repeatYearly: tmp=tmp.arg(i18n("year(s)")); break;
+ default: tmp=tmp.arg(QString::null); break;
+ }
+ text+=tmp;
+ text+=br;
+
+ bool repeatsForever = getRepeatForever();
+ if (repeatsForever)
+ {
+ text+=i18n("Repeats indefinitely");
+ }
+ else
+ {
+ dt = readTm(getRepeatEnd()).date();
+ text+=i18n("Until %1").arg(dt.toString(Qt::LocalDate));
+ }
+ text+=br;
+
+ if (getRepeatType()==repeatMonthlyByDay) text+=i18n("Repeating on the i-th day of week j")+br;
+ if (getRepeatType()==repeatMonthlyByDate) text+=i18n("Repeating on the n-th day of the month")+br;
+ // TODO: show the dayArray when repeating weekly
+ /*QBitArray dayArray(7);
+ if (getRepeatType()==repeatWeekly) text+=i18n("Repeat day flags: %1").arg(getRepeatDays
+ const int *days = dateEntry->getRepeatDays();
+ // Rotate the days of the week, since day numbers on the Pilot and
+ // in vCal / Events are different.
+ if (days[0]) dayArray.setBit(6);
+ for (int i = 1; i < 7; i++)
+ {
+ if (days[i]) dayArray.setBit(i-1);
+ }*/
+ text+=ps;
+ }
+
+ if (getExceptionCount()>0 )
+ {
+ text+=par;
+ text+=i18n("Exceptions:")+br;
+ for (int i = 0; i < getExceptionCount(); i++)
+ {
+ QDate exdt=readTm(getExceptions()[i]).date();
+ text+=exdt.toString(Qt::LocalDate);
+ text+=br;
+ }
+ text+=ps;
+ }
+
+ if (!getNote().isEmpty())
+ {
+ text += richText?CSL1("<hr/>"):CSL1("-------------------------\n");
+ text+=par;
+ text+=richText?i18n("<b><em>Note:</em></b><br>"):i18n("Note:\n");
+ text+=rtExpand(getNote(), richText);
+ text+=ps;
+ }
+
+ return text;
+}
+
+QDateTime PilotDateEntry::dtStart() const
+{
+ FUNCTIONSETUP;
+ return readTm( getEventStart() );
+}
+
+QDateTime PilotDateEntry::dtEnd() const
+{
+ FUNCTIONSETUP;
+ return readTm( getEventEnd() );
+}
+
+QDateTime PilotDateEntry::dtRepeatEnd() const
+{
+ FUNCTIONSETUP;
+ return readTm( getRepeatEnd() );
+}
+
+unsigned int PilotDateEntry::alarmLeadTime() const
+{
+ FUNCTIONSETUP;
+ if (!isAlarmEnabled()) return 0;
+
+ int adv = getAdvance();
+ if ( adv < 0 )
+ {
+ return 0; // Not possible to enter on the pilot
+ }
+ unsigned int t = adv;
+ int u = getAdvanceUnits();
+
+
+ switch(u)
+ {
+ case advMinutes : t *= 60; break;
+ case advHours : t *= 3600; break;
+ case advDays : t *= 3600 * 24; break;
+ default: t = 0;
+ }
+
+ return t;
+}
+
+PilotRecord *PilotDateEntry::pack() const
+{
+ int i;
+
+ pi_buffer_t *b = pi_buffer_new( sizeof(fAppointmentInfo) );
+ i = pack_Appointment(const_cast<Appointment_t *>(&fAppointmentInfo), b, datebook_v1);
+ if (i<0)
+ {
+ // Generic error from the pack_*() functions.
+ return 0;
+ }
+
+ // pack_Appointment sets b->used
+ return new PilotRecord( b, this );
+}
+
+/* setExceptions sets a new set of exceptions. Note that
+ PilotDateEntry assumes ownership of the array and will
+ delete the old one. */
+void PilotDateEntry::setExceptions(struct tm *e) {
+ if (fAppointmentInfo.exception != e)
+ {
+ KPILOT_FREE(fAppointmentInfo.exception);
+ }
+ fAppointmentInfo.exception=e;
+}
+
+
+void PilotDateEntry::setDescriptionP(const char *desc, int l)
+{
+ FUNCTIONSETUP;
+ KPILOT_FREE(fAppointmentInfo.description);
+
+ if (desc && *desc)
+ {
+ if (-1 == l) l=::strlen(desc);
+ fAppointmentInfo.description =
+ (char *) ::malloc(l + 1);
+ if (fAppointmentInfo.description)
+ {
+ strlcpy(fAppointmentInfo.description, desc, l+1);
+ }
+ else
+ {
+ WARNINGKPILOT << "malloc() failed, description not set" << endl;
+ }
+ }
+ else
+ {
+ fAppointmentInfo.description = 0L;
+ }
+}
+
+void PilotDateEntry::setNoteP(const char *note, int l)
+{
+ FUNCTIONSETUP;
+ KPILOT_FREE(fAppointmentInfo.note);
+
+ if (note && *note)
+ {
+ if (-1 == l) l=::strlen(note);
+ fAppointmentInfo.note = (char *)::malloc(l + 1);
+ if (fAppointmentInfo.note)
+ {
+ strlcpy(fAppointmentInfo.note, note,l+1);
+ }
+ else
+ {
+ WARNINGKPILOT << "malloc() failed, note not set" << endl;
+ }
+ }
+ else
+ {
+ fAppointmentInfo.note = 0L;
+ }
+}
+
+void PilotDateEntry::setNote(const QString &s)
+{
+ QCString t = Pilot::toPilot(s);
+ setNoteP( t.data(),t.length() );
+}
+
+void PilotDateEntry::setLocation(const QString &s)
+{
+ QString note = Pilot::fromPilot(getNoteP());
+ QRegExp rxp = QRegExp("^[Ll]ocation:[^\n]+\n");
+
+ // per QString docs, this covers null and 0 length
+ if( s.isEmpty() )
+ {
+ note.replace(rxp,"");
+ }
+ else
+ {
+ QString location = "Location: " + s + "\n";
+ int pos = note.find(rxp);
+
+ if(pos >= 0)
+ {
+ note.replace( rxp, location );
+ }
+ else
+ {
+ note = location + note;
+ setNote( note );
+ }
+ }
+}
+
+QString PilotDateEntry::getLocation() const
+{
+ // Read the complete note here and not the filtered
+ // one from PilotDateEntry::getNote();
+ QString note = Pilot::fromPilot(getNoteP());
+ QRegExp rxp = QRegExp("^[Ll]ocation:[^\n]+\n");
+ int pos = note.find(rxp, 0);
+
+ if(pos >= 0)
+ {
+ QString location = rxp.capturedTexts().first();
+ rxp = QRegExp("^[Ll]ocation:[\\s|\t]*");
+ location.replace(rxp,"");
+ location.replace("\n", "");
+ return location;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+void PilotDateEntry::setDescription(const QString &s)
+{
+ QCString t = Pilot::toPilot(s);
+ setDescriptionP( t.data(),t.length() );
+}
+
+QString PilotDateEntry::getNote() const
+{
+ QString note = Pilot::fromPilot(getNoteP());
+ QRegExp rxp = QRegExp("^[Ll]ocation:[^\n]+\n");
+ note.replace(rxp, "" );
+ return note;
+}
+
+QString PilotDateEntry::getDescription() const
+{
+ return Pilot::fromPilot(getDescriptionP());
+}
+
diff --git a/kpilot/lib/pilotDateEntry.h b/kpilot/lib/pilotDateEntry.h
new file mode 100644
index 000000000..d9d5db2a6
--- /dev/null
+++ b/kpilot/lib/pilotDateEntry.h
@@ -0,0 +1,388 @@
+#ifndef _KPILOT_PILOTDATEENTRY_H
+#define _KPILOT_PILOTDATEENTRY_H
+/* pilotDateEntry.h -*- C++ -*- KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** See the .cc file for an explanation of what this file is for.
+*/
+
+/** @file pilotDateEntry.h defines a wrapper for datebook entries. */
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <pi-macros.h>
+#include <pi-datebook.h>
+
+#include <qbitarray.h>
+#include <qdatetime.h>
+#include <qnamespace.h>
+
+#include "pilotRecord.h"
+#include "pilotAppInfo.h"
+
+namespace KCal
+{
+class Event;
+}
+
+/** Interpreted form of the AppInfo block in the datebook database. */
+typedef PilotAppInfo<
+ AppointmentAppInfo,
+ unpack_AppointmentAppInfo,
+ pack_AppointmentAppInfo> PilotDateInfo_;
+
+
+class PilotDateInfo : public PilotDateInfo_
+{
+public:
+ PilotDateInfo(PilotDatabase *d) : PilotDateInfo_(d)
+ {
+ }
+
+ /** This resets the entire AppInfo block to one as it would be
+ * in an English-language handheld, with 3 categories and
+ * default field labels for everything.
+ */
+ void resetToDefault();
+
+};
+
+/** This class is a wrapper for pilot-link's datebook entries (struct Appointment). */
+class KDE_EXPORT PilotDateEntry : public PilotRecordBase
+{
+public:
+ /** Constructor. Zeroes out the appointment. */
+ PilotDateEntry();
+
+ /** Constructor. Interprets the given record as an appointment. */
+ PilotDateEntry(PilotRecord *rec);
+
+ /** Copy constructor. */
+ PilotDateEntry(const PilotDateEntry &e);
+
+ /** Destructor. */
+ ~PilotDateEntry()
+ {
+ free_Appointment(&fAppointmentInfo);
+ }
+
+ /** Assignment operator. */
+ PilotDateEntry& operator=(const PilotDateEntry &e);
+
+ /** Create a textual representation (human-readable) of this appointment.
+ * If @p richText is true, then the text representation uses qt style
+ * tags as well.
+ */
+ QString getTextRepresentation(Qt::TextFormat richText);
+
+ /** Is this appointment a "floating" appointment?
+ *
+ * Floating appointments are those that have a day assigned, but no time
+ * in that day (birthday appointments are like that). You can think of these
+ * as "events", which don't have a time associated with them for a given day,
+ * as opposed to a regular "appointment", which does normally have a time
+ * associated with it.
+ */
+ inline bool doesFloat() const
+ {
+ return fAppointmentInfo.event;
+ }
+
+ /** Is this a non-time-related event as opposed to an appointment that has a
+ * time associated with it?.
+ */
+ inline bool isEvent() const
+ {
+ return doesFloat();
+ }
+
+ /** Sets this appointment's floating status.
+ *
+ * Floating appointments are those that have a day assigned, but no time
+ * in that day (birthday appointments are like that). You can think of these
+ * as "events", which don't have a time associated with them for a given day,
+ * as opposed to a regular "appointment", which does normally have a time
+ * associated with it.
+ */
+ inline void setFloats(bool f)
+ {
+ fAppointmentInfo.event = (f ? 1 : 0) /* Force 1 or 0 */ ;
+ }
+
+ /** Get the start time of this appointment. See dtStart() for caveats. */
+ inline struct tm getEventStart() const { return fAppointmentInfo.begin; }
+
+ /** Get a pointer to the start time of this appointment. See dtStart() for caveats. */
+ inline const struct tm *getEventStart_p() const
+ {
+ return &fAppointmentInfo.begin;
+ }
+
+ /** Sets the start time of this appointment. */
+ inline void setEventStart(struct tm& start)
+ {
+ fAppointmentInfo.begin = start;
+ }
+
+ /** Get the start time of this appointment. For floating appointments, the
+ * time is undefined (perhaps 1 minute past midnight).
+ *
+ * Floating appointments are those that have a day assigned, but no time
+ * in that day (birthday appointments are like that).
+ */
+ QDateTime dtStart() const;
+
+ /** Get the end time of this appointment. See dtEnd() for caveats. */
+ inline struct tm getEventEnd() const
+ {
+ return fAppointmentInfo.end;
+ }
+
+ /** Get a pointer to the end time of this appointment. See dtEnd() for caveats. */
+ inline const struct tm *getEventEnd_p() const
+ {
+ return &fAppointmentInfo.end;
+ }
+
+ /** Set the end time of this appointment. */
+ inline void setEventEnd(struct tm& end)
+ {
+ fAppointmentInfo.end = end;
+ }
+
+ /** Get the end time of this appointment. For floating appointments, the
+ * time is undefined (perhaps 1 minute past midnight).
+ *
+ * Floating appointments are those that have a day assigned, but no time
+ * in that day (birthday appointments are like that).
+ */
+ QDateTime dtEnd() const;
+
+ /** Does this appointment have an alarm set? On the Pilot, an event
+ * may have an alarm (or not). If it has one, it is also enabled and
+ * causes the Pilot to beep (or whatever is set in the system preferences).
+ */
+ inline bool isAlarmEnabled() const
+ {
+ return fAppointmentInfo.alarm;
+ }
+
+ /** Set whether this appointment has an alarm. */
+ inline void setAlarmEnabled(bool b)
+ {
+ fAppointmentInfo.alarm = (b?1:0) /* Force to known int values */ ;
+ }
+
+ /** Get the numeric part of "alarm: __ (v) minutes" on the pilot -- you
+ * set the alarm time in two parts, a number and a unit type to use; unit
+ * types are minutes, hours, days and the number is whatever you like.
+ *
+ * If alarms are not enabled for this appointment, returns garbage.
+ *
+ * @see alarmLeadTime()
+ * @see dtAlarm()
+ */
+ inline int getAdvance() const
+ {
+ return fAppointmentInfo.advance;
+ }
+
+ /** Set the numeric part of the alarm setting. See getAdvance for details. */
+ inline void setAdvance(int advance)
+ {
+ fAppointmentInfo.advance = advance;
+ }
+
+ /** Returns the units part of the alarm time. See getAdvance . */
+ inline int getAdvanceUnits() const
+ {
+ return fAppointmentInfo.advanceUnits;
+ }
+
+ /** Sets the unites part of the alarm time. See getAdvance . */
+ inline void setAdvanceUnits(int units)
+ {
+ fAppointmentInfo.advanceUnits = units;
+ }
+
+ /** Returns the number of @em seconds "lead time" the alarm should sound
+ * before the actual appointment. This interprets the advance number and units.
+ * The value is always positive, 0 if no alarms are enabled.
+ */
+ unsigned int alarmLeadTime() const;
+
+ /** Returns the absolute date and time that the alarm should sound for
+ * this appointment.
+ */
+ inline QDateTime dtAlarm() const
+ {
+ return dtStart().addSecs(-alarmLeadTime());
+ }
+
+ // The following need set routines written
+ inline repeatTypes getRepeatType() const
+ {
+ return fAppointmentInfo.repeatType;
+ }
+ inline void setRepeatType(repeatTypes r)
+ {
+ fAppointmentInfo.repeatType = r;
+ }
+
+ inline int getRepeatForever() const
+ {
+ return fAppointmentInfo.repeatForever;
+ }
+ inline void setRepeatForever(int f = 1)
+ {
+ fAppointmentInfo.repeatForever = f;
+ }
+
+ inline struct tm getRepeatEnd() const
+ {
+ return fAppointmentInfo.repeatEnd;
+ }
+ inline void setRepeatEnd(struct tm tm)
+ {
+ fAppointmentInfo.repeatEnd = tm;
+ }
+
+ /** Returns the date and time that the repeat ends. If there is no repeat,
+ * returns an invalid date and time.
+ */
+ QDateTime dtRepeatEnd() const;
+
+ inline int getRepeatFrequency() const
+ {
+ return fAppointmentInfo.repeatFrequency;
+ }
+ inline void setRepeatFrequency(int f)
+ {
+ fAppointmentInfo.repeatFrequency = f;
+ }
+
+ inline DayOfMonthType getRepeatDay() const
+ {
+ return fAppointmentInfo.repeatDay;
+ }
+ inline void setRepeatDay(DayOfMonthType rd)
+ {
+ fAppointmentInfo.repeatDay = rd;
+ };
+
+ inline const int *getRepeatDays() const
+ {
+ return fAppointmentInfo.repeatDays;
+ }
+ inline void setRepeatDays(int *rd)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ fAppointmentInfo.repeatDays[i] = rd[i];
+ }
+ }
+ inline void setRepeatDays(QBitArray rba)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ fAppointmentInfo.repeatDays[i] = (rba[i] ? 1 : 0);
+ }
+ }
+
+ inline int getExceptionCount() const
+ {
+ return fAppointmentInfo.exceptions;
+ }
+ inline void setExceptionCount(int e)
+ {
+ fAppointmentInfo.exceptions = e;
+ }
+
+ inline const struct tm *getExceptions() const
+ {
+ return fAppointmentInfo.exception;
+ }
+ void setExceptions(struct tm *e);
+
+ /** Sets the description of the appointment. This is the short string
+ * entered in the day view on the handheld, and it is called the summary
+ * in libkcal.
+ */
+ void setDescription(const QString &);
+ /** Gets the description of the appointment. See setDescription for meaning. */
+ QString getDescription() const;
+
+ /** Sets the note for the appointment. The note is the long text entry
+ * that is possible - but clumsy - on the handheld. It is called the
+ * description in libkcal.
+ */
+ void setNote(const QString &);
+ /** Gets the note for this appointment. See setNote for meaning. */
+ QString getNote() const;
+
+ /**
+ * Sets the location for the appointment. For now it will be placed within
+ * the notes on the handheld. It will be placed on one line and starts with:
+ * Location: {location}. Everything on that line will be counted as location.
+ * TODO: Make distinguish between handhelds that support the location field
+ * and the ones that don't. (Shouldn't this be done in the pilot-link lib?)
+ */
+ void setLocation(const QString &);
+
+ /** Gets the location for this appointment. See setNote for meaning. */
+ QString getLocation() const;
+
+protected:
+ void setDescriptionP(const char* desc, int l=-1);
+ const char* getDescriptionP() const
+ {
+ return fAppointmentInfo.description;
+ }
+
+ void setNoteP(const char* note, int l=-1);
+ const char* getNoteP() const
+ {
+ return fAppointmentInfo.note;
+ }
+
+public:
+ bool isMultiDay() const
+ {
+ return ((fAppointmentInfo.repeatType == repeatDaily) &&
+ (fAppointmentInfo.repeatFrequency == 1) &&
+ ( !getRepeatForever() ) &&
+ !doesFloat() );
+ }
+
+ PilotRecord *pack() const;
+
+private:
+ struct Appointment fAppointmentInfo;
+ void _copyExceptions(const PilotDateEntry &e);
+};
+
+
+
+#endif
+
diff --git a/kpilot/lib/pilotLinkVersion.h b/kpilot/lib/pilotLinkVersion.h
new file mode 100644
index 000000000..1255baade
--- /dev/null
+++ b/kpilot/lib/pilotLinkVersion.h
@@ -0,0 +1,60 @@
+#ifndef _KPILOT_PILOTLINKVERSION_H
+#define _KPILOT_PILOTLINKVERSION_H
+/* KPilot
+**
+** Copyright (C) 2005 by Adriaan de Groot
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include <pi-version.h>
+
+/** @file
+* Checks the pilot-link version and defines some convenience macros.
+* The main point of this file is to complain if you have a version
+* of pilot-link before 0.12, which no longer work with KPilot.
+*/
+
+#ifndef PILOT_LINK_VERSION
+#error "You need at least pilot-link version 0.12.0"
+#endif
+
+
+#define PILOT_LINK_NUMBER ((10000*PILOT_LINK_VERSION) + \
+ (100*PILOT_LINK_MAJOR)+PILOT_LINK_MINOR)
+#define PILOT_LINK_0_10_0 (1000)
+#define PILOT_LINK_0_11_0 (1100)
+#define PILOT_LINK_0_11_8 (1108)
+#define PILOT_LINK_0_12_0 (1200)
+#define PILOT_LINK_0_12_1 (1201)
+
+#if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0
+#error "You need at least pilot-link version 0.12.0 for KPilot"
+#endif
+
+#define PI_SIZE_T size_t
+
+
+#endif
+
diff --git a/kpilot/lib/pilotLocalDatabase.cc b/kpilot/lib/pilotLocalDatabase.cc
new file mode 100644
index 000000000..9f2d5e8a4
--- /dev/null
+++ b/kpilot/lib/pilotLocalDatabase.cc
@@ -0,0 +1,762 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This defines an interface to Pilot databases on the local disk.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "options.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <iostream>
+
+#include <pi-file.h>
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qdatetime.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <ksavefile.h>
+
+#include "pilotRecord.h"
+#include "pilotLocalDatabase.h"
+
+typedef QValueVector<PilotRecord *> Records;
+
+class PilotLocalDatabase::Private : public Records
+{
+public:
+ static const int DEFAULT_SIZE = 128;
+ Private(int size=DEFAULT_SIZE) : Records(size) { resetIndex(); }
+ ~Private() { deleteRecords(); }
+
+ void deleteRecords()
+ {
+ for (unsigned int i=0; i<size(); i++)
+ {
+ delete at(i);
+ }
+ clear();
+ resetIndex();
+ }
+
+ void resetIndex()
+ {
+ current = 0;
+ pending = -1;
+ }
+
+ unsigned int current;
+ int pending;
+} ;
+
+PilotLocalDatabase::PilotLocalDatabase(const QString & path,
+ const QString & dbName, bool useDefaultPath) :
+ PilotDatabase(dbName),
+ fPathName(path),
+ fDBName(dbName),
+ fAppInfo(0L),
+ fAppLen(0),
+ d(0L)
+{
+ FUNCTIONSETUP;
+ fixupDBName();
+ openDatabase();
+
+ if (!isOpen() && useDefaultPath)
+ {
+ if (fPathBase && !fPathBase->isEmpty())
+ {
+ fPathName = *fPathBase;
+ }
+ else
+ {
+ fPathName = KGlobal::dirs()->saveLocation("data",
+ CSL1("kpilot/DBBackup/"));
+ }
+ fixupDBName();
+ openDatabase();
+ if (!isOpen())
+ {
+ fPathName=path;
+ }
+ }
+
+}
+
+PilotLocalDatabase::PilotLocalDatabase(const QString &dbName) :
+ PilotDatabase( QString() ),
+ fPathName( QString() ),
+ fDBName( QString() ),
+ fAppInfo(0L),
+ fAppLen(0),
+ d(0L)
+{
+ FUNCTIONSETUP;
+
+ int p = dbName.findRev( '/' );
+ if (p<0)
+ {
+ // No slash
+ fPathName = CSL1(".");
+ fDBName = dbName;
+ }
+ else
+ {
+ fPathName = dbName.left(p);
+ fDBName = dbName.mid(p+1);
+ }
+ openDatabase();
+}
+
+PilotLocalDatabase::~PilotLocalDatabase()
+{
+ FUNCTIONSETUP;
+
+ closeDatabase();
+ delete[]fAppInfo;
+ delete d;
+}
+
+// Changes any forward slashes to underscores
+void PilotLocalDatabase::fixupDBName()
+{
+ FUNCTIONSETUP;
+ fDBName = fDBName.replace(CSL1("/"),CSL1("_"));
+}
+
+bool PilotLocalDatabase::createDatabase(long creator, long type, int, int flags, int version)
+{
+ FUNCTIONSETUP;
+
+ // if the database is already open, we cannot create it again.
+ // How about completely resetting it? (i.e. deleting it and then
+ // creating it again)
+ if (isOpen())
+ {
+ DEBUGKPILOT << fname << ": Database " << fDBName
+ << " already open. Cannot recreate it." << endl;
+ return true;
+ }
+
+ DEBUGKPILOT << fname << ": Creating database " << fDBName << endl;
+
+ // Database names seem to be latin1.
+ Pilot::toPilot(fDBName, fDBInfo.name, sizeof(fDBInfo.name));
+ fDBInfo.creator=creator;
+ fDBInfo.type=type;
+ fDBInfo.more=0;
+ fDBInfo.flags=flags;
+ fDBInfo.miscFlags=0;
+ fDBInfo.version=version;
+ fDBInfo.modnum=0;
+ fDBInfo.index=0;
+ fDBInfo.createDate=(QDateTime::currentDateTime()).toTime_t();
+ fDBInfo.modifyDate=(QDateTime::currentDateTime()).toTime_t();
+ fDBInfo.backupDate=(QDateTime::currentDateTime()).toTime_t();
+
+ delete[] fAppInfo;
+ fAppInfo=0L;
+ fAppLen=0;
+
+ d = new Private;
+
+ // TODO: Do I have to open it explicitly???
+ setDBOpen(true);
+ return true;
+}
+
+int PilotLocalDatabase::deleteDatabase()
+{
+ FUNCTIONSETUP;
+ if (isOpen())
+ {
+ closeDatabase();
+ }
+
+ QString dbpath=dbPathName();
+ QFile fl(dbpath);
+ if (QFile::remove(dbPathName()))
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+
+
+// Reads the application block info
+int PilotLocalDatabase::readAppBlock(unsigned char *buffer, int size)
+{
+ FUNCTIONSETUP;
+
+ size_t m = kMin((size_t)size,(size_t)fAppLen);
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ memset(buffer,0,m);
+ return -1;
+ }
+
+ memcpy((void *) buffer, fAppInfo, m);
+ return fAppLen;
+}
+
+int PilotLocalDatabase::writeAppBlock(unsigned char *buffer, int len)
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return -1;
+ }
+ delete[]fAppInfo;
+ fAppLen = len;
+ fAppInfo = new char[fAppLen];
+
+ memcpy(fAppInfo, (void *) buffer, fAppLen);
+ return 0;
+}
+
+
+// returns the number of records in the database
+unsigned int PilotLocalDatabase::recordCount() const
+{
+ if (d && isOpen())
+ {
+ return d->size();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+// Returns a QValueList of all record ids in the database.
+QValueList<recordid_t> PilotLocalDatabase::idList()
+{
+ int idlen=recordCount();
+ QValueList<recordid_t> idlist;
+ if (idlen<=0)
+ {
+ return idlist;
+ }
+
+ // now create the QValue list from the idarr:
+ for (int i=0; i<idlen; i++)
+ {
+ idlist.append((*d)[i]->id());
+ }
+
+ return idlist;
+}
+
+// Reads a record from database by id, returns record length
+PilotRecord *PilotLocalDatabase::readRecordById(recordid_t id)
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "Database '" << fDBName << " not open!" << endl;
+ return 0L;
+ }
+
+ d->pending = -1;
+
+ for (unsigned int i = 0; i < d->size(); i++)
+ {
+ if ((*d)[i]->id() == id)
+ {
+ PilotRecord *newRecord = new PilotRecord((*d)[i]);
+ d->current = i;
+ return newRecord;
+ }
+ }
+ return 0L;
+}
+
+// Reads a record from database, returns the record
+PilotRecord *PilotLocalDatabase::readRecordByIndex(int index)
+{
+ FUNCTIONSETUP;
+
+ if (index < 0)
+ {
+ DEBUGKPILOT << fname << ": Index " << index << " is bogus." << endl;
+ return 0L;
+ }
+
+ d->pending = -1;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0L;
+ }
+
+ DEBUGKPILOT << fname << ": Index=" << index << " Count=" << recordCount() << endl;
+
+ if ( (unsigned int)index >= recordCount() )
+ {
+ return 0L;
+ }
+ PilotRecord *newRecord = new PilotRecord((*d)[index]);
+ d->current = index;
+
+ return newRecord;
+}
+
+// Reads the next record from database in category 'category'
+PilotRecord *PilotLocalDatabase::readNextRecInCategory(int category)
+{
+ FUNCTIONSETUP;
+ d->pending = -1;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0L;
+ }
+
+ while ((d->current < d->size())
+ && ((*d)[d->current]->category() != category))
+ {
+ d->current++;
+ }
+
+ if (d->current >= d->size())
+ return 0L;
+ PilotRecord *newRecord = new PilotRecord((*d)[d->current]);
+
+ d->current++; // so we skip it next time
+ return newRecord;
+}
+
+const PilotRecord *PilotLocalDatabase::findNextNewRecord()
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0L;
+ }
+ DEBUGKPILOT << fname << ": looking for new record from " << d->current << endl;
+ // Should this also check for deleted?
+ while ((d->current < d->size())
+ && ((*d)[d->current]->id() != 0 ))
+ {
+ d->current++;
+ }
+
+ if (d->current >= d->size())
+ return 0L;
+
+ d->pending = d->current; // Record which one needs the new id
+ d->current++; // so we skip it next time
+ return (*d)[d->pending];
+}
+
+PilotRecord *PilotLocalDatabase::readNextModifiedRec(int *ind)
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0L;
+ }
+
+ d->pending = -1;
+ // Should this also check for deleted?
+ while ((d->current < d->size())
+ && !((*d)[d->current]->isModified()) && ((*d)[d->current]->id()>0 ))
+ {
+ d->current++;
+ }
+
+ if (d->current >= d->size())
+ {
+ return 0L;
+ }
+ PilotRecord *newRecord = new PilotRecord((*d)[d->current]);
+ if (ind)
+ {
+ *ind=d->current;
+ }
+
+ d->pending = d->current; // Record which one needs the new id
+ d->current++; // so we skip it next time
+ return newRecord;
+}
+
+// Writes a new ID to the record specified the index. Not supported on Serial connections
+recordid_t PilotLocalDatabase::updateID(recordid_t id)
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0;
+ }
+ if (d->pending < 0)
+ {
+ WARNINGKPILOT << "Last call was NOT readNextModifiedRec()" << endl;
+ return 0;
+ }
+ (*d)[d->pending]->setID(id);
+ d->pending = -1;
+ return id;
+}
+
+// Writes a new record to database (if 'id' == 0, it is assumed that this is a new record to be installed on pilot)
+recordid_t PilotLocalDatabase::writeRecord(PilotRecord * newRecord)
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return 0;
+ }
+
+ d->pending = -1;
+ if (!newRecord)
+ {
+ WARNINGKPILOT << "Record to be written is invalid!" << endl;
+ return 0;
+ }
+
+ // Instead of making the app do it, assume that whenever a record is
+ // written to the database it is dirty. (You can clean up the database with
+ // resetSyncFlags().) This will make things get copied twice during a hot-sync
+ // but shouldn't cause any other major headaches.
+ newRecord->setModified( true );
+
+ // First check to see if we have this record:
+ if (newRecord->id() != 0)
+ {
+ for (unsigned int i = 0; i < d->size(); i++)
+ if ((*d)[i]->id() == newRecord->id())
+ {
+ delete (*d)[i];
+
+ (*d)[i] = new PilotRecord(newRecord);
+ return 0;
+ }
+ }
+ // Ok, we don't have it, so just tack it on.
+ d->append( new PilotRecord(newRecord) );
+ return newRecord->id();
+}
+
+// Deletes a record with the given recordid_t from the database, or all records, if all is set to true. The recordid_t will be ignored in this case
+int PilotLocalDatabase::deleteRecord(recordid_t id, bool all)
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT <<"DB not open"<<endl;
+ return -1;
+ }
+ d->resetIndex();
+ if (all)
+ {
+ d->deleteRecords();
+ d->clear();
+ return 0;
+ }
+ else
+ {
+ Private::Iterator i;
+ for ( i=d->begin() ; i!=d->end(); ++i)
+ {
+ if ((*i) && (*i)->id() == id) break;
+ }
+ if ( (i!=d->end()) && (*i) && (*i)->id() == id)
+ {
+ d->erase(i);
+ }
+ else
+ {
+ // Record with this id does not exist!
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+// Resets all records in the database to not dirty.
+int PilotLocalDatabase::resetSyncFlags()
+{
+ FUNCTIONSETUP;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return -1;
+ }
+ d->pending = -1;
+ for (unsigned int i = 0; i < d->size(); i++)
+ {
+ (*d)[i]->setModified( false );
+ }
+ return 0;
+}
+
+// Resets next record index to beginning
+int PilotLocalDatabase::resetDBIndex()
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return -1;
+ }
+ d->resetIndex();
+ return 0;
+}
+
+// Purges all Archived/Deleted records from Palm Pilot database
+int PilotLocalDatabase::cleanup()
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open!" << endl;
+ return -1;
+ }
+ d->resetIndex();
+
+ /* Not the for loop one might expect since when we erase()
+ * a record the iterator changes too.
+ */
+ Private::Iterator i = d->begin();
+ while ( i!=d->end() )
+ {
+ if ( (*i)->isDeleted() || (*i)->isArchived() )
+ {
+ delete (*i);
+ i = d->erase(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // Don't have to do anything. Will be taken care of by closeDatabase()...
+ // Changed!
+ return 0;
+}
+
+QString PilotLocalDatabase::dbPathName() const
+{
+ FUNCTIONSETUP;
+ QString tempName(fPathName);
+ QString slash = CSL1("/");
+
+ if (!tempName.endsWith(slash)) tempName += slash;
+ tempName += getDBName();
+ tempName += CSL1(".pdb");
+ return tempName;
+}
+
+void PilotLocalDatabase::openDatabase()
+{
+ FUNCTIONSETUP;
+
+ pi_file *dbFile;
+
+ setDBOpen(false);
+
+ dbFile = pi_file_open( QFile::encodeName(dbPathName()) );
+ if (dbFile == 0L)
+ {
+ QString path = dbPathName();
+ DEBUGKPILOT << fname << ": Failed to open " << path << endl;
+ return;
+ }
+
+
+ PI_SIZE_T size = 0;
+ void *tmpBuffer;
+ pi_file_get_info(dbFile, &fDBInfo);
+ pi_file_get_app_info(dbFile, &tmpBuffer, &size);
+ fAppLen = size;
+ fAppInfo = new char[fAppLen];
+ memcpy(fAppInfo, tmpBuffer, fAppLen);
+
+ int count;
+ pi_file_get_entries(dbFile, &count);
+ if (count >= 0)
+ {
+ KPILOT_DELETE(d);
+ d = new Private(count);
+ }
+
+ int attr, cat;
+ recordid_t id;
+ unsigned int i = 0;
+ while (pi_file_read_record(dbFile, i,
+ &tmpBuffer, &size, &attr, &cat, &id) == 0)
+ {
+ pi_buffer_t *b = pi_buffer_new(size);
+ memcpy(b->data,tmpBuffer,size);
+ b->used = size;
+ (*d)[i] = new PilotRecord(b, attr, cat, id);
+ i++;
+ }
+ pi_file_close(dbFile); // We done with it once we've read it in.
+
+ KSaveFile::backupFile( dbPathName() );
+
+ setDBOpen(true);
+}
+
+void PilotLocalDatabase::closeDatabase()
+{
+ FUNCTIONSETUP;
+ pi_file *dbFile;
+
+ if (!isOpen())
+ {
+ DEBUGKPILOT << fname << ": Database " << fDBName
+ << " is not open. Cannot close and write it"
+ << endl;
+ return;
+ }
+
+ QString newName = dbPathName() + CSL1(".new");
+ QString path = dbPathName();
+ DEBUGKPILOT << fname
+ << ": Creating temp file " << newName
+ << " for the database file " << path << endl;
+
+ dbFile = pi_file_create(QFile::encodeName(newName),&fDBInfo);
+ pi_file_set_app_info(dbFile, fAppInfo, fAppLen);
+
+ for (unsigned int i = 0; i < d->size(); i++)
+ {
+ // How did a NULL pointer sneak in here?
+ if (!(*d)[i])
+ {
+ continue;
+ }
+
+ if (((*d)[i]->id() == 0) && ((*d)[i]->isDeleted()))
+ {
+ // Just ignore it
+ }
+ else
+ {
+ pi_file_append_record(dbFile,
+ (*d)[i]->data(),
+ (*d)[i]->size(),
+ (*d)[i]->attributes(), (*d)[i]->category(),
+ (*d)[i]->id());
+ }
+ }
+
+ pi_file_close(dbFile);
+ QFile::remove(dbPathName());
+ rename((const char *) QFile::encodeName(newName),
+ (const char *) QFile::encodeName(dbPathName()));
+ setDBOpen(false);
+}
+
+
+QString *PilotLocalDatabase::fPathBase = 0L;
+
+void PilotLocalDatabase::setDBPath(const QString &s)
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname
+ << ": Setting default DB path to "
+ << s
+ << endl;
+
+ if (!fPathBase)
+ {
+ fPathBase = new QString(s);
+ }
+ else
+ {
+ *fPathBase = s;
+ }
+}
+
+/* virtual */ PilotDatabase::DBType PilotLocalDatabase::dbType() const
+{
+ return eLocalDB;
+}
+
+
+/* static */ bool PilotLocalDatabase::infoFromFile( const QString &path, DBInfo *d )
+{
+ FUNCTIONSETUP;
+
+ pi_file *f = 0L;
+
+ if (!d)
+ {
+ return false;
+ }
+ if (!QFile::exists(path))
+ {
+ return false;
+ }
+
+ QCString fileName = QFile::encodeName( path );
+ f = pi_file_open( fileName );
+ if (!f)
+ {
+ WARNINGKPILOT << "Can't open " << path << endl;
+ return false;
+ }
+
+ pi_file_get_info(f,d);
+ pi_file_close(f);
+
+ return true;
+}
+
diff --git a/kpilot/lib/pilotLocalDatabase.h b/kpilot/lib/pilotLocalDatabase.h
new file mode 100644
index 000000000..c33455800
--- /dev/null
+++ b/kpilot/lib/pilotLocalDatabase.h
@@ -0,0 +1,201 @@
+#ifndef _KPILOT_PILOTLOCALDATABASE_H
+#define _KPILOT_PILOTLOCALDATABASE_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "pilotDatabase.h"
+
+/** @file
+* Defines the PilotLocalDatabase class, for databases stored
+* on disk (as opposed to in a handheld).
+*/
+
+/**
+* PilotLocalDatabase represents databases in the same binary format
+* as on the handheld but which are stored on local disk.
+*/
+class KDE_EXPORT PilotLocalDatabase : public PilotDatabase
+{
+public:
+ /**
+ * Opens the local database. If the database cannot be found at the
+ * given position, a default path is used
+ * ($KDEHOME/share/apps/kpilot/DBBackup)
+ * and if the file is found there, it is opened. In some cases this should
+ * not be done, so the parameter useDefaultPath controls this behavior.
+ * If it is set to true, the default path is used if the file cannot be
+ * found in the explicitly given location. If it is set to false and
+ * the database cannot be found, no database is opened. It can then be
+ * created explicitly at the specified location.
+ */
+ PilotLocalDatabase( const QString& path,
+ const QString& name, bool useDefaultPath=true);
+
+ /**
+ * Opens the local database. This is primarily for testing
+ * purposes; only tries the given path.
+ */
+ PilotLocalDatabase(const QString &name);
+
+ virtual ~PilotLocalDatabase();
+
+ /** Creates the database with the given creator, type and flags on
+ * the given card (default is RAM). If the database already exists,
+ * this function does nothing.
+ */
+ virtual bool createDatabase(long creator=0,
+ long type=0, int cardno=0, int flags=0, int version=0);
+
+
+
+ /** Deletes the database (by name, as given in the constructor
+ * and stored in the fDBName field. )
+ */
+ virtual int deleteDatabase();
+
+ // Reads the application block info
+ virtual int readAppBlock(unsigned char* buffer, int maxLen);
+ // Writes the application block info.
+ virtual int writeAppBlock(unsigned char* buffer, int len);
+ // returns the number of records in the database, 0 if not open
+ virtual unsigned int recordCount() const;
+ // Returns a QValueList of all record ids in the database.
+ virtual QValueList<recordid_t> idList();
+ // Reads a record from database by id, returns record
+ virtual PilotRecord* readRecordById(recordid_t id);
+ // Reads a record from database, returns the record
+ virtual PilotRecord* readRecordByIndex(int index);
+ // Reads the next record from database in category 'category'
+ virtual PilotRecord* readNextRecInCategory(int category);
+ /**
+ * Returns the next "new" record, ie. the next record
+ * that has not been synced yet. These records all have ID=0, so are
+ * not easy to find with the other methods. The record is the one
+ * contained in the database, not a copy like the read*() functions
+ * give you -- so be careful with it. Don't delete it, in any case.
+ * Casting it to non-const and marking it deleted is OK, though,
+ * which is mostly its intended use.
+ */
+ const PilotRecord *findNextNewRecord();
+
+ /**
+ * Reads the next record from database that has the dirty flag set.
+ * ind (if a valid pointer is given) will receive the index of the
+ * returned record.
+ */
+ virtual PilotRecord* readNextModifiedRec(int *ind=0L);
+ // Writes a new record to database (if 'id' == 0, none is assigned, either)
+ virtual recordid_t writeRecord(PilotRecord* newRecord);
+ /**
+ * Deletes a record with the given recordid_t from the database,
+ * or all records, if all is set to true. The recordid_t will be
+ * ignored in this case. Return value is negative on error, 0 otherwise.
+ */
+ virtual int deleteRecord(recordid_t id, bool all=false);
+ // Resets all records in the database to not dirty.
+ virtual int resetSyncFlags();
+ // Resets next record index to beginning
+ virtual int resetDBIndex();
+ // Purges all Archived/Deleted records from Palm Pilot database
+ virtual int cleanup();
+
+
+ /** Update the ID of the current record in the database with
+ * the specified @param id . This is allowed only after
+ * reading or writing a modified or new record.
+ */
+ virtual recordid_t updateID(recordid_t id);
+
+
+ /** Return the name of the database (as it would be on the handheld). */
+ QString getDBName() const { return fDBName; }
+
+ /**
+ * Returns the full path of the current database, based on
+ * the path and dbname passed to the constructor, and including
+ * the .pdb extension.
+ */
+ virtual QString dbPathName() const;
+
+ /**
+ * Accessor functions for the application info block.
+ */
+ int appInfoSize() const
+ { if (isOpen()) return fAppLen; else return -1; } ;
+ char *appInfo() { return fAppInfo; } ;
+
+ const struct DBInfo &getDBInfo() const { return fDBInfo; }
+ void setDBInfo(const struct DBInfo &dbi) {fDBInfo=dbi; }
+
+ virtual DBType dbType() const;
+
+ /** Reads local file @p path and fills in the DBInfo
+ * structure @p d with the DBInfo from the file.
+ *
+ * @return @c false if d is NULL
+ * @return @c false if the file @p path does not exist
+ * @return @c true if reading the DBInfo succeeds
+ *
+ * @note Relatively expensive operation, since the pilot-link
+ * library doesn't provide a cheap way of getting this
+ * information.
+ */
+ static bool infoFromFile( const QString &path, DBInfo *d );
+
+protected:
+ // Changes any forward slashes to underscores
+ void fixupDBName();
+ virtual void openDatabase();
+ virtual void closeDatabase();
+
+private:
+ struct DBInfo fDBInfo;
+ QString fPathName,fDBName;
+ char* fAppInfo;
+ size_t fAppLen;
+
+ class Private;
+ Private *d;
+
+public:
+ /**
+ * For databases opened by name only (constructor 2 -- which is the
+ * preferred one, too) try this path first before the default path.
+ * Set statically so it's shared for all local databases.
+ */
+ static void setDBPath(const QString &);
+ /**
+ * Accessor for the extra search path.
+ */
+ static const QString &getDBPath() { return *fPathBase; } ;
+private:
+ static QString *fPathBase;
+};
+
+#endif
diff --git a/kpilot/lib/pilotMemo.cc b/kpilot/lib/pilotMemo.cc
new file mode 100644
index 000000000..0ce09f8a7
--- /dev/null
+++ b/kpilot/lib/pilotMemo.cc
@@ -0,0 +1,135 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+**
+** This is a C++ wrapper for the Pilot's Memo Pad structures.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <qnamespace.h>
+
+#include "pilotMemo.h"
+#include "pilotDatabase.h"
+
+
+
+PilotMemo::PilotMemo(const PilotRecord * rec) : PilotRecordBase(rec)
+{
+ FUNCTIONSETUP;
+ fText = Pilot::fromPilot((const char *)(rec->data()),rec->size());
+}
+
+PilotRecord *PilotMemo::pack()
+{
+ FUNCTIONSETUPL(4);
+ int i;
+
+ int len = fText.length() + 8;
+ struct Memo buf;
+ buf.text = new char[len];
+
+ // put our text into buf
+ i = Pilot::toPilot(fText, buf.text, len);
+
+ pi_buffer_t *b = pi_buffer_new(len);
+ i = pack_Memo(&buf, b, memo_v1);
+
+ DEBUGKPILOT << fname << ": original text: [" << fText
+ << "], buf.text: [" << buf.text
+ << "], b->data: [" << b->data << "]" << endl;
+
+ if (i<0)
+ {
+ // Generic error from the pack_*() functions.
+ delete[] buf.text;
+ return 0;
+ }
+
+ // pack_Appointment sets b->used
+ PilotRecord *r = new PilotRecord(b, this);
+ delete[] buf.text;
+ return r;
+}
+
+
+QString PilotMemo::getTextRepresentation(Qt::TextFormat richText)
+{
+ if (richText==Qt::RichText)
+ {
+ return i18n("<i>Title:</i> %1<br>\n<i>MemoText:</i><br>%2").
+ arg(rtExpand(getTitle(), richText)).arg(rtExpand(text(), richText));
+ }
+ else
+ {
+ return i18n("Title: %1\nMemoText:\n%2").arg(getTitle()).arg(text());
+ }
+}
+
+
+QString PilotMemo::getTitle() const
+{
+ if (fText.isEmpty()) return QString::null;
+
+ int memoTitleLen = fText.find('\n');
+ if (-1 == memoTitleLen) memoTitleLen=fText.length();
+ return fText.left(memoTitleLen);
+}
+
+QString PilotMemo::shortTitle() const
+{
+ FUNCTIONSETUP;
+ QString t = QString(getTitle()).simplifyWhiteSpace();
+
+ if (t.length() < 32)
+ return t;
+ t.truncate(40);
+
+ int spaceIndex = t.findRev(' ');
+
+ if (spaceIndex > 32)
+ {
+ t.truncate(spaceIndex);
+ }
+
+ t += CSL1("...");
+
+ return t;
+}
+
+QString PilotMemo::sensibleTitle() const
+{
+ FUNCTIONSETUP;
+ QString s = getTitle();
+
+ if (!s.isEmpty())
+ {
+ return s;
+ }
+ else
+ {
+ return i18n("[unknown]");
+ }
+}
+
diff --git a/kpilot/lib/pilotMemo.h b/kpilot/lib/pilotMemo.h
new file mode 100644
index 000000000..6897a0417
--- /dev/null
+++ b/kpilot/lib/pilotMemo.h
@@ -0,0 +1,105 @@
+#ifndef _KPILOT_PILOTMEMO_H
+#define _KPILOT_PILOTMEMO_H
+/* pilotMemo.h KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+**
+** See the .cc file for an explanation of what this file is for.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qnamespace.h>
+#include <qstring.h>
+
+#include <pi-memo.h>
+
+#include "pilotRecord.h"
+#include "pilotAppInfo.h"
+
+class KDE_EXPORT PilotMemo : public PilotRecordBase
+{
+public:
+ /**
+ * Constructor. Create an empty memo.
+ */
+ PilotMemo(void) : PilotRecordBase() { } ;
+
+ /**
+ * Constructor. Create a memo in the Unfiled category with
+ * text @p s .
+ */
+ PilotMemo(const QString &s) : PilotRecordBase()
+ {
+ setText(s);
+ } ;
+
+ /**
+ * Constructor. Create a memo with the category and
+ * attributes of the given record @p rec, and extract
+ * the text from that record as if it comes from the MemoDB.
+ */
+ PilotMemo(const PilotRecord* rec);
+
+ /**
+ * Constructor. Create a memo with category and
+ * attributes from the argument @p r, and set the
+ * text of the memo from string @p s.
+ */
+ PilotMemo(const PilotRecordBase *r, const QString &s) :
+ PilotRecordBase(r)
+ {
+ setText(s);
+ }
+
+ ~PilotMemo() { } ;
+
+ virtual QString getTextRepresentation(Qt::TextFormat richText);
+ QString text(void) const { return fText; } ;
+ void setText(const QString &text) { fText = text.left(MAX_MEMO_LEN); } ;
+ QString getTitle(void) const ;
+ PilotRecord* pack();
+
+ static const int MAX_MEMO_LEN=8192;
+
+ /**
+ * Return a "short but sensible" title. getTitle() returns the
+ * first line of the memo, which may be very long
+ * and inconvenient. shortTitle() returns about 30
+ * characters.
+ */
+ QString shortTitle() const;
+
+ /**
+ * Returns a (complete) title if there is one and [unknown]
+ * otherwise.
+ */
+ QString sensibleTitle() const;
+
+private:
+ QString fText;
+
+};
+
+typedef PilotAppInfo<struct MemoAppInfo,unpack_MemoAppInfo, pack_MemoAppInfo> PilotMemoInfo;
+
+#endif
diff --git a/kpilot/lib/pilotRecord.cc b/kpilot/lib/pilotRecord.cc
new file mode 100644
index 000000000..44ae4f7be
--- /dev/null
+++ b/kpilot/lib/pilotRecord.cc
@@ -0,0 +1,132 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This is a wrapper for pilot-link's general
+** Pilot database structures. These records are
+*** just collections of bits. See PilotAppCategory
+** for interpreting the bits in a meaningful way.
+**
+** As a crufty hack, the non-inline parts of
+** PilotAppCategory live in this file as well.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+#include "options.h"
+
+#include <string.h>
+
+#include <qregexp.h>
+
+#include <kglobal.h>
+#include <kcharsets.h>
+
+#include "pilot.h"
+#include "pilotRecord.h"
+
+
+
+/* virtual */ QString PilotRecordBase::textRepresentation() const
+{
+ return CSL1("[ %1,%2,%3 ]") . arg(attributes(),category(),id());
+}
+
+/* virtual */ QString PilotRecord::textRepresentation() const
+{
+ return CSL1("[ %1,%2 ]")
+ .arg(PilotRecordBase::textRepresentation())
+ .arg(size());
+}
+
+
+
+/* static */ int PilotRecord::fAllocated = 0;
+/* static */ int PilotRecord::fDeleted = 0;
+
+/* static */ void PilotRecord::allocationInfo()
+{
+ FUNCTIONSETUP;
+ DEBUGKPILOT << fname
+ << ": Allocated " << fAllocated
+ << " Deleted " << fDeleted << endl;
+}
+
+PilotRecord::PilotRecord(void *data, int len, int attrib, int cat, recordid_t uid) :
+ PilotRecordBase(attrib,cat,uid),
+ fData(0L),
+ fLen(len),
+ fBuffer(0L)
+{
+ FUNCTIONSETUPL(4);
+ fData = new char[len];
+
+ memcpy(fData, data, len);
+
+ fAllocated++;
+}
+
+PilotRecord::PilotRecord(PilotRecord * orig) :
+ PilotRecordBase( orig->attributes(), orig->category(), orig->id() ) ,
+ fBuffer(0L)
+{
+ FUNCTIONSETUPL(4);
+ fData = new char[orig->size()];
+
+ memcpy(fData, orig->data(), orig->size());
+ fLen = orig->size();
+ fAllocated++;
+}
+
+PilotRecord & PilotRecord::operator = (PilotRecord & orig)
+{
+ FUNCTIONSETUP;
+ if (fBuffer)
+ {
+ pi_buffer_free(fBuffer);
+ fBuffer=0L;
+ fData=0L;
+ }
+
+ if (fData)
+ delete[]fData;
+ fData = new char[orig.size()];
+
+ memcpy(fData, orig.data(), orig.size());
+ fLen = orig.size();
+ setAttributes( orig.attributes() );
+ setCategory( orig.category() );
+ setID( orig.id() );
+ return *this;
+}
+
+void PilotRecord::setData(const char *data, int len)
+{
+ FUNCTIONSETUP;
+ if (fData)
+ delete[]fData;
+ fData = new char[len];
+
+ memcpy(fData, data, len);
+ fLen = len;
+}
+
diff --git a/kpilot/lib/pilotRecord.h b/kpilot/lib/pilotRecord.h
new file mode 100644
index 000000000..a3812b333
--- /dev/null
+++ b/kpilot/lib/pilotRecord.h
@@ -0,0 +1,355 @@
+#ifndef _KPILOT_PILOTRECORD_H
+#define _KPILOT_PILOTRECORD_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "pilot.h"
+
+/**
+* @file This file defines the lowest- denominator representation(s)
+*of the bits used in a Pilot-based database record.
+*/
+
+
+/**
+* All entries in the Handheld -- whether interpreted or binary blobs --
+* have some common characteristics, viz. an ID number, a category,
+* and some attributes defined by the handheld. PilotRecordBase is
+* a common base class collecting methods to manipulate those
+* common characteristics.
+*/
+class KDE_EXPORT PilotRecordBase
+{
+public:
+ /** Constructor. Initialize the characteristics to the
+ * given values.
+ *
+ * @param attrib Attributes (bitfield) for this entry.
+ * @param cat Category for this entry. Should be in the
+ * range 0 <= cat < Pilot::CATEGORY_COUNT . Using an
+ * invalid category means 0 (unfiled) is used.
+ * @param id Unique ID for this entry. May be 0 (non-unique) as well.
+ */
+ PilotRecordBase(int attrib=0, int cat=0, recordid_t id=0) :
+ fAttrib(attrib),fCat(0),fID(id)
+ {
+ setCategory(cat);
+ }
+
+ /** Constructor. Initializes the characteristics from
+ * the values the given record @p b has. When @p b is
+ * NULL (which is allowed), everything is assumed zero.
+ * No ownership is transferred.
+ *
+ * @param b Record to take characteristics from.
+ */
+ PilotRecordBase( const PilotRecordBase *b ) :
+ fAttrib( b ? b->attributes() : 0 ),
+ fCat( 0 ),
+ fID( b ? b->id() : 0 )
+ {
+ if (b)
+ {
+ setCategory( b->category() );
+ }
+ }
+
+ /** Destructor. Nothing to do for it. */
+ virtual ~PilotRecordBase() { } ;
+
+ /** Attributes of this record (deleted, secret, ...);
+ * it's a bitfield.
+ */
+ inline int attributes() const
+ {
+ return fAttrib;
+ }
+
+ /** Set the attributes of this record. */
+ inline void setAttributes(int attrib)
+ {
+ fAttrib = attrib;
+ }
+
+ /** Returns the category number [ 0 .. Pilot::CATEGORY_COUNT-1]
+ * of this record.
+ */
+ inline int category() const
+ {
+ return fCat;
+ }
+
+ /** Sets the category number [ 0 .. Pilot::CATEGORY_COUNT-1]
+ * of this record.
+ * Trying to set an illegal category number files this one under
+ * "Unfiled" (which is 0).
+ */
+ inline void setCategory(int cat)
+ {
+ if ( (cat<0) || (cat>=(int)Pilot::CATEGORY_COUNT))
+ {
+ cat=0;
+ }
+ fCat = cat;
+ }
+
+ /** Sets the category number by looking up the string @p label
+ * in the category table @p info . Leaves the category unchanged
+ * if no match is found and returns @c false.
+ *
+ * @param info AppInfo structure containing the labels (in handheld
+ * native encoding).
+ * @param label The label to look for.
+ *
+ * @return @c true on success, @c false on failure
+ */
+ bool setCategory(const struct CategoryAppInfo *info, const QString &label)
+ {
+ if (!info)
+ {
+ return false;
+ }
+
+ int cat = Pilot::findCategory( info, label, false );
+ if ( (cat<0) || (cat>=(int)Pilot::CATEGORY_COUNT) )
+ {
+ return false;
+ }
+ else
+ {
+ setCategory( cat );
+ return true;
+ }
+ }
+
+ /** Returns the record ID for this record. Record IDs are unique for a given
+ * handheld and database.
+ */
+ inline recordid_t id() const
+ {
+ return fID;
+ }
+
+ /** Sets the record ID for this record. Use with caution -- you ca confuse
+ * the handheld by doing weird things here.
+ */
+ void setID(recordid_t id)
+ {
+ fID = id;
+ }
+
+ /** Accessor for one bit of the record's attributes. Is this record marked
+ * deleted (on the handheld) ? Deleted records are not removed from the
+ * database until a HotSync is done (which normally calls purge deleted
+ * or so to really get rid of the records from storage.
+ */
+ inline bool isDeleted() const
+ {
+ return fAttrib & dlpRecAttrDeleted;
+ }
+
+ /** Accessor for one bit of the record's attributes. Is this record secret?
+ * Secret records are not displayed on the desktop by default.
+ */
+ inline bool isSecret() const
+ {
+ return fAttrib & dlpRecAttrSecret;
+ }
+
+ /** Accessor for one bit of the record's attributes. Is this record a
+ * to-be-archived record? When a record is deleted, it may be marked
+ * as "archive on PC" which means the PC should keep a copy. The
+ * PC data correspondng to an archived-but-deleted record must not
+ * be deleted.
+ */
+ inline bool isArchived() const
+ {
+ return fAttrib & dlpRecAttrArchived;
+ }
+
+ /** Accessor for one bit of the record's attributes. Is this record modified?
+ * Modified records are those that have been modified since the last HotSync.
+ */
+ inline bool isModified() const
+ {
+ return fAttrib & dlpRecAttrDirty;
+ }
+
+#define SETTER(a) {\
+ if (d) { fAttrib |= a; } \
+ else { fAttrib &= ~a; } }
+
+ /** Mark a record as deleted (or not).*/
+ inline void setDeleted(bool d=true) SETTER(dlpRecAttrDeleted)
+
+ /** Mark a record as secret (or not). */
+ inline void setSecret(bool d=true) SETTER(dlpRecAttrSecret)
+
+ /** Mark a record as archived (or not). */
+ inline void setArchived(bool d=true) SETTER(dlpRecAttrArchived)
+
+ /** Mark a record as modified (or not). */
+ inline void setModified(bool d=true) SETTER(dlpRecAttrDirty)
+
+#undef SETTER
+
+ /** Returns a text representation of this record. */
+ virtual QString textRepresentation() const;
+
+private:
+ int fAttrib, fCat;
+ recordid_t fID;
+} ;
+
+/** An "uninterpreted" representation of the bits comprising a HH record.
+* This binary blob only exposes the data via the data() and size() functions,
+* and also exposes the common characteristics of all entries.
+*/
+class KDE_EXPORT PilotRecord : public PilotRecordBase
+{
+public:
+ /** Constructor. Using the given @p data and @p length, create
+ * a record. Give it the additional attributes and category numbers;
+ * the UID is a HH unique ID for identifying records.
+ *
+ * This constructor makes a copy of the data buffer (and owns that buffer).
+ */
+ PilotRecord(void* data, int length, int attrib, int cat, recordid_t uid) KDE_DEPRECATED;
+
+ /** Constructor. Using the given buffer @p buf (which carries its
+ * own data and length), create a record. Otherwise much like the
+ * above constructor @em except that this record assumes ownership
+ * of the buffer, and doesn't make an additional copy
+ * (In practice, this just saves copying around extra buffers).
+ */
+ PilotRecord(pi_buffer_t *buf, int attrib, int cat, recordid_t uid) :
+ PilotRecordBase(attrib,cat,uid),
+ fData((char *)buf->data),
+ fLen(buf->used),
+ fBuffer(buf)
+ {
+ fAllocated++;
+ }
+
+ /** Constructor. Like the above, only take the attributes, category
+ * and id from the given @p entry.
+ */
+ PilotRecord( pi_buffer_t *buf, const PilotRecordBase *entry ) :
+ PilotRecordBase( entry ),
+ fData((char *)buf->data),
+ fLen(buf->used),
+ fBuffer(buf)
+ {
+ fAllocated++;
+ }
+
+ /** Destructor. Dispose of the buffers in the right form. */
+ virtual ~PilotRecord()
+ {
+ if (fBuffer)
+ {
+ pi_buffer_free(fBuffer);
+ }
+ else
+ {
+ delete [] fData;
+ }
+ fDeleted++;
+ }
+
+ /** Constructor. Copies the data from the @p orig record. */
+ PilotRecord(PilotRecord* orig);
+
+ /** Retrieve the data buffer for this record. Note that trying
+ * to change this data is fraught with peril -- especially trying
+ * to lengthen it.
+ *
+ * @see setData
+ */
+ char *data() const
+ {
+ if (fBuffer)
+ {
+ return (char *)(fBuffer->data);
+ }
+ else
+ {
+ return fData;
+ }
+ }
+
+ /** Returns the size of the data for this record. */
+ int size() const
+ {
+ if (fBuffer) return fBuffer->used; else
+ return fLen;
+ }
+
+ /** Returns the data buffer associated with this record. */
+ const pi_buffer_t *buffer() const { return fBuffer; }
+
+ /** Set the data for this record. Frees old data. Assumes
+ * ownership of the passed in buffer @p b.
+ */
+ void setData(pi_buffer_t *b)
+ {
+ if (fBuffer) { pi_buffer_free(fBuffer); }
+ else { delete[] fData; } ;
+ fData = (char *)b->data;
+ fLen = b->used;
+ fBuffer = b;
+ }
+
+ /** Assignment operator. Makes a copy of the @p orig record. */
+ PilotRecord& operator=(PilotRecord& orig);
+
+ /** Sets the data for this record. Makes a copy of the data buffer. */
+ void setData(const char* data, int len);
+
+ /** Returns a text representation of this record. */
+ virtual QString textRepresentation() const;
+
+private:
+ char* fData;
+ int fLen;
+ pi_buffer_t *fBuffer;
+
+public:
+ /**
+ * This is an interface for tracking down memory leaks
+ * in the use of PilotRecords (for those without valgrind).
+ * Count the number of allocations and deallocations.
+ */
+ static void allocationInfo();
+private:
+ static int fAllocated,fDeleted;
+};
+
+#endif
diff --git a/kpilot/lib/pilotSerialDatabase.cc b/kpilot/lib/pilotSerialDatabase.cc
new file mode 100644
index 000000000..a38898d1d
--- /dev/null
+++ b/kpilot/lib/pilotSerialDatabase.cc
@@ -0,0 +1,432 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** Databases approached through DLP / Pilot-link look different,
+** so this file defines an API for them.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+#include "options.h"
+
+#include <time.h>
+#include <iostream>
+
+#include <pi-dlp.h>
+
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "pilotRecord.h"
+#include "pilotSerialDatabase.h"
+#include "kpilotdevicelink.h"
+
+PilotSerialDatabase::PilotSerialDatabase(KPilotDeviceLink *l,
+ const QString &dbName) :
+ PilotDatabase(dbName),
+ fDBName( dbName ),
+ fDBHandle(-1),
+ fDBSocket(l->pilotSocket())
+{
+ FUNCTIONSETUP;
+ openDatabase();
+}
+
+PilotSerialDatabase::PilotSerialDatabase( KPilotDeviceLink *l, const DBInfo *info ) :
+ PilotDatabase( info ? Pilot::fromPilot( info->name ) : QString::null ),
+ fDBName( QString::null ),
+ fDBHandle( -1 ),
+ fDBSocket( l->pilotSocket() )
+{
+ // Rather unclear why both the base class and this one have separate names.
+ fDBName = name();
+ setDBOpen(false);
+ if (fDBName.isEmpty() || !info)
+ {
+ WARNINGKPILOT << "Bad database name requested." << endl;
+ return;
+ }
+
+ int db;
+ if (dlp_OpenDB(fDBSocket, 0, dlpOpenReadWrite, info->name, &db) < 0)
+ {
+ WARNINGKPILOT << "Cannot open database on handheld." << endl;
+ return;
+ }
+ setDBHandle(db);
+ setDBOpen(true);
+}
+
+PilotSerialDatabase::~PilotSerialDatabase()
+{
+ FUNCTIONSETUP;
+ closeDatabase();
+}
+
+QString PilotSerialDatabase::dbPathName() const
+{
+ QString s = CSL1("Pilot:");
+ s.append(fDBName);
+ return s;
+}
+
+// Reads the application block info
+int PilotSerialDatabase::readAppBlock(unsigned char *buffer, int maxLen)
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return -1;
+ }
+ pi_buffer_t *buf = pi_buffer_new(maxLen);
+ int r = dlp_ReadAppBlock(fDBSocket, getDBHandle(), 0 /* offset */, maxLen, buf);
+ if (r>=0)
+ {
+ memcpy(buffer, buf->data, KMAX(maxLen, r));
+ }
+ pi_buffer_free(buf);
+ return r;
+}
+
+// Writes the application block info.
+int PilotSerialDatabase::writeAppBlock(unsigned char *buffer, int len)
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return -1;
+ }
+ return dlp_WriteAppBlock(fDBSocket, getDBHandle(), buffer, len);
+}
+
+ // returns the number of records in the database
+unsigned int PilotSerialDatabase::recordCount() const
+{
+ int idlen;
+ // dlp_ReadOpenDBInfo returns the number of bytes read and sets idlen to the # of recs
+ if (isOpen() && dlp_ReadOpenDBInfo(fDBSocket, getDBHandle(), &idlen)>0)
+ {
+ return idlen;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+// Returns a QValueList of all record ids in the database.
+QValueList<recordid_t> PilotSerialDatabase::idList()
+{
+ QValueList<recordid_t> idlist;
+ int idlen=recordCount();
+ if (idlen<=0) return idlist;
+
+ recordid_t *idarr=new recordid_t[idlen];
+ int idlenread;
+ int r = dlp_ReadRecordIDList (fDBSocket, getDBHandle(), 0, 0, idlen, idarr, &idlenread);
+
+ if ( (r<0) || (idlenread<1) )
+ {
+ WARNINGKPILOT << "Failed to read ID list from database." << endl;
+ return idlist;
+ }
+
+ // now create the QValue list from the idarr:
+ for (idlen=0; idlen<idlenread; idlen++)
+ {
+ idlist.append(idarr[idlen]);
+ }
+ delete[] idarr;
+ return idlist;
+}
+
+
+// Reads a record from database by id, returns record length
+PilotRecord *PilotSerialDatabase::readRecordById(recordid_t id)
+{
+ FUNCTIONSETUPL(3);
+ int index, attr, category;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return 0L;
+ }
+ if (id>0xFFFFFF)
+ {
+ WARNINGKPILOT << "Encountered an invalid record id "
+ << id << endl;
+ return 0L;
+ }
+ pi_buffer_t *b = pi_buffer_new(InitialBufferSize);
+ if (dlp_ReadRecordById(fDBSocket,getDBHandle(),id,b,&index,&attr,&category) >= 0)
+ {
+ return new PilotRecord(b, attr, category, id);
+ }
+ return 0L;
+}
+
+// Reads a record from database, returns the record length
+PilotRecord *PilotSerialDatabase::readRecordByIndex(int index)
+{
+ FUNCTIONSETUPL(3);
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return 0L;
+ }
+
+ int attr, category;
+ recordid_t id;
+ PilotRecord *rec = 0L;
+
+ pi_buffer_t *b = pi_buffer_new(InitialBufferSize);
+ if (dlp_ReadRecordByIndex(fDBSocket, getDBHandle(), index,
+ b, &id, &attr, &category) >= 0)
+ {
+ rec = new PilotRecord(b, attr, category, id);
+ }
+
+
+ return rec;
+}
+
+// Reads the next record from database in category 'category'
+PilotRecord *PilotSerialDatabase::readNextRecInCategory(int category)
+{
+ FUNCTIONSETUP;
+ int index, attr;
+ recordid_t id;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return 0L;
+ }
+ pi_buffer_t *b = pi_buffer_new(InitialBufferSize);
+ if (dlp_ReadNextRecInCategory(fDBSocket, getDBHandle(),
+ category,b,&id,&index,&attr) >= 0)
+ return new PilotRecord(b, attr, category, id);
+ return 0L;
+}
+
+// Reads the next record from database that has the dirty flag set.
+PilotRecord *PilotSerialDatabase::readNextModifiedRec(int *ind)
+{
+ FUNCTIONSETUP;
+ int index, attr, category;
+ recordid_t id;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return 0L;
+ }
+ pi_buffer_t *b = pi_buffer_new(InitialBufferSize);
+ if (dlp_ReadNextModifiedRec(fDBSocket, getDBHandle(), b, &id, &index, &attr, &category) >= 0)
+ {
+ if (ind) *ind=index;
+ return new PilotRecord(b, attr, category, id);
+ }
+ return 0L;
+}
+
+// Writes a new record to database (if 'id' == 0 or id>0xFFFFFF, one will be assigned and returned in 'newid')
+recordid_t PilotSerialDatabase::writeRecord(PilotRecord * newRecord)
+{
+ FUNCTIONSETUP;
+ recordid_t newid;
+ int success;
+
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return 0;
+ }
+ // Do some sanity checking to prevent invalid UniqueIDs from being written
+ // to the handheld (RecordIDs are only 3 bytes!!!). Under normal conditions
+ // this check should never yield true, so write out an error to indicate
+ // someone messed up full time...
+ if (newRecord->id()>0xFFFFFF)
+ {
+ WARNINGKPILOT << "Encountered an invalid record id "
+ << newRecord->id() << ", resetting it to zero." << endl;
+ newRecord->setID(0);
+ }
+ success =
+ dlp_WriteRecord(fDBSocket, getDBHandle(),
+ newRecord->attributes(), newRecord->id(),
+ newRecord->category(), newRecord->data(),
+ newRecord->size(), &newid);
+ if ( (newRecord->id() != newid) && (newid!=0) )
+ newRecord->setID(newid);
+ return newid;
+}
+
+// Deletes a record with the given recordid_t from the database, or all records, if all is set to true. The recordid_t will be ignored in this case
+int PilotSerialDatabase::deleteRecord(recordid_t id, bool all)
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT <<"DB not open"<<endl;
+ return -1;
+ }
+ return dlp_DeleteRecord(fDBSocket, getDBHandle(), all?1:0, id);
+}
+
+
+// Resets all records in the database to not dirty.
+int PilotSerialDatabase::resetSyncFlags()
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return -1;
+ }
+ return dlp_ResetSyncFlags(fDBSocket, getDBHandle());
+}
+
+// Resets next record index to beginning
+int PilotSerialDatabase::resetDBIndex()
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return -1;
+ }
+ return dlp_ResetDBIndex(fDBSocket, getDBHandle());
+}
+
+// Purges all Archived/Deleted records from Palm Pilot database
+int PilotSerialDatabase::cleanup()
+{
+ FUNCTIONSETUP;
+ if (!isOpen())
+ {
+ WARNINGKPILOT << "DB not open" << endl;
+ return -1;
+ }
+ return dlp_CleanUpDatabase(fDBSocket, getDBHandle());
+}
+
+void PilotSerialDatabase::openDatabase()
+{
+ FUNCTIONSETUP;
+ int db;
+
+ setDBOpen(false);
+
+ QString s = getDBName();
+ if (s.isEmpty())
+ {
+ WARNINGKPILOT << "Bad DB name, " << s << " string given." << endl;
+ return;
+ }
+
+ QCString encodedName = QFile::encodeName(s);
+ if (encodedName.isEmpty())
+ {
+ WARNINGKPILOT << "Bad DB name, "
+ << (encodedName.isNull() ? "null" : "empty")
+ << " string given."
+ << endl;
+ return;
+ }
+
+ char encodedNameBuffer[PATH_MAX];
+ strlcpy(encodedNameBuffer,(const char *)encodedName,PATH_MAX);
+
+ DEBUGKPILOT << fname << ": opening database: ["
+ << encodedNameBuffer << "]" << endl;
+
+ if (dlp_OpenDB(fDBSocket, 0, dlpOpenReadWrite,
+ encodedNameBuffer, &db) < 0)
+ {
+ WARNINGKPILOT << "Cannot open database on handheld." << endl;
+ return;
+ }
+ setDBHandle(db);
+ setDBOpen(true);
+}
+
+bool PilotSerialDatabase::createDatabase(long creator, long type, int cardno, int flags, int version)
+{
+ FUNCTIONSETUP;
+ int db;
+
+ // if the database is already open, we cannot create it again. How about completely resetting it? (i.e. deleting it and the createing it again)
+ if (isOpen()) return true;
+ // The latin1 seems ok, database names are latin1.
+ int res=dlp_CreateDB(fDBSocket,
+ creator, type, cardno, flags, version,
+ Pilot::toPilot(getDBName()), &db);
+ if (res<0) {
+ WARNINGKPILOT << "Cannot create database " << getDBName() << " on the handheld" << endl;
+ return false;
+ }
+ // TODO: Do I have to open it explicitly???
+ setDBHandle(db);
+ setDBOpen(true);
+ return true;
+}
+
+void PilotSerialDatabase::closeDatabase()
+{
+ FUNCTIONSETUP;
+ if (!isOpen() )
+ {
+ return;
+ }
+
+ DEBUGKPILOT << fname << ": Closing DB handle #" << getDBHandle() << endl;
+ dlp_CloseDB(fDBSocket, getDBHandle());
+ DEBUGKPILOT << fname << ": after closing" << endl;
+ setDBOpen(false);
+}
+
+int PilotSerialDatabase::deleteDatabase()
+{
+ FUNCTIONSETUP;
+
+ if (isOpen()) closeDatabase();
+
+ return dlp_DeleteDB(fDBSocket, 0, Pilot::toPilot(fDBName));
+}
+
+
+
+/* virtual */ PilotDatabase::DBType PilotSerialDatabase::dbType() const
+{
+ return eSerialDB;
+}
+
diff --git a/kpilot/lib/pilotSerialDatabase.h b/kpilot/lib/pilotSerialDatabase.h
new file mode 100644
index 000000000..51a6ba26d
--- /dev/null
+++ b/kpilot/lib/pilotSerialDatabase.h
@@ -0,0 +1,144 @@
+#ifndef _KPILOT_PILOTSERIALDATABASE_H
+#define _KPILOT_PILOTSERIALDATABASE_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+** See the .cc file for an explanation of what this file is for.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+
+#include "pilotDatabase.h"
+#include "pilotRecord.h"
+
+/** @file
+* Database class for a database on the pilot connected
+* via the serial port (ie: hot-sync cradle)
+*/
+
+class KPilotDeviceLink;
+
+/**
+* PilotSerialDatabase represents databases stored on the handheld
+* and accessed through the SLP / DLP protocol.
+*/
+class KDE_EXPORT PilotSerialDatabase : public PilotDatabase
+{
+friend class KPilotDeviceLink;
+protected:
+ PilotSerialDatabase( KPilotDeviceLink *l, const QString &dbName );
+ PilotSerialDatabase( KPilotDeviceLink *l, const DBInfo *info );
+
+public:
+ virtual ~PilotSerialDatabase();
+
+ /** Reads the application block info, returns size */
+ virtual int readAppBlock(unsigned char* buffer, int maxLen);
+ /** Writes the application block info. */
+ virtual int writeAppBlock(unsigned char* buffer, int len);
+ /** returns the number of records in the database, 0 if not open */
+ virtual unsigned int recordCount() const;
+ /** Returns a QValueList of all record ids in the database. */
+ virtual QValueList<recordid_t> idList();
+ /** Reads a record from database by id, returns record length */
+ virtual PilotRecord* readRecordById(recordid_t id);
+ /** Reads a record from database, returns the record length */
+ virtual PilotRecord* readRecordByIndex(int index);
+ /** Reads the next record from database in category 'category' */
+ virtual PilotRecord* readNextRecInCategory(int category);
+ /**
+ * Reads the next record from database that has the dirty flag set.
+ * ind (if a valid pointer is given) will receive the index of the
+ * returned record.
+ */
+ virtual PilotRecord* readNextModifiedRec(int *ind=NULL);
+
+ /**
+ * Writes a new record to database (if 'id' == 0, one will be
+ * assigned to newRecord)
+ */
+ virtual recordid_t writeRecord(PilotRecord* newRecordb);
+
+ /**
+ * Deletes a record with the given recordid_t from the database,
+ * or all records, if all is set to true. The recordid_t will be
+ * ignored in this case. Return value is negative on error, 0 otherwise.
+ */
+ virtual int deleteRecord(recordid_t id, bool all=false);
+ /** Resets all records in the database to not dirty. */
+ virtual int resetSyncFlags();
+ /** Resets next record index to beginning */
+ virtual int resetDBIndex();
+ /** Purges all Archived/Deleted records from Palm Pilot database */
+ virtual int cleanup();
+
+ virtual QString dbPathName() const;
+
+ /**
+ * Deletes the database (by name, as given in the constructor and
+ * stored in the fDBName field).
+ */
+ virtual int deleteDatabase();
+
+ /**
+ * Creates the database with the given creator, type and flags on
+ * the given card (default is RAM). If the database already exists,
+ * this function does nothing.
+ */
+ virtual bool createDatabase(long creator=0,
+ long type=0, int cardno=0, int flags=0, int version=0);
+ QString getDBName() { return fDBName; }
+
+
+ virtual DBType dbType() const;
+
+protected:
+ virtual void openDatabase();
+ virtual void closeDatabase();
+ /** Returns the file handle used to communicate with the handheld.
+ * This is an internal value to be passed to DLP functions.
+ */
+ int getDBHandle() const
+ {
+ return fDBHandle;
+ }
+
+
+private:
+ void setDBHandle(int handle) { fDBHandle = handle; }
+
+ QString fDBName;
+ int fDBHandle;
+ int fDBSocket;
+ // Pilot-link 0.12 allocates buffers as needed and resizes them.
+ // Start with a buffer that is _probably_ big enough for most
+ // PIM records, but much smaller than the 64k that we use otherwise.
+ // Might want to add algorithm for trying to optimize the initial
+ // allocation for a given database.
+ static const int InitialBufferSize = 2048;
+};
+
+#endif
diff --git a/kpilot/lib/pilotSysInfo.h b/kpilot/lib/pilotSysInfo.h
new file mode 100644
index 000000000..4deb4f1b4
--- /dev/null
+++ b/kpilot/lib/pilotSysInfo.h
@@ -0,0 +1,144 @@
+#ifndef _KPILOT_SYSINFO_H
+#define _KPILOT_SYSINFO_H
+/* sysInfo.h KPilot
+**
+** Copyright (C) 2003 by Reinhold Kainhofer
+**
+** Wrapper for pilot-link's SysInfo Structure
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <pi-version.h>
+#include <pi-dlp.h>
+
+
+
+class KPilotSysInfo
+{
+public:
+ /** Constructor. Create an empty SysInfo structure. */
+ KPilotSysInfo()
+ {
+ ::memset(&fSysInfo,0,sizeof(struct SysInfo));
+ }
+
+ /** Constructor. Copy an existing pilot-link SysInfo structure.
+ * Ownership is not changed. @p sys_info may be NULL.
+ */
+ KPilotSysInfo(const SysInfo *sys_info)
+ {
+ ::memset(&fSysInfo,0,sizeof(struct SysInfo));
+ if (sys_info)
+ {
+ fSysInfo = *sys_info;
+ }
+ }
+
+ /** Access to the raw SysInfo structure. */
+ SysInfo *sysInfo()
+ {
+ return &fSysInfo;
+ }
+
+ /** Get the ROM version of the handheld. This is a pilot-link
+ * long value (4 bytes) with major, minor, bugfix version
+ * numbers encoded in the value.
+ */
+ const unsigned long getRomVersion() const
+ {
+ return fSysInfo.romVersion;
+ }
+
+ /** Get the locale number of the handheld.
+ * @note I do not know what this means.
+ */
+ const unsigned long getLocale() const
+ {
+ return fSysInfo.locale;
+ }
+ /** Set the locale number of the handheld.
+ * @note I do not know what this means.
+ */
+ void setLocale(unsigned long newval)
+ {
+ fSysInfo.locale=newval;
+ }
+
+ /** Get the length (in bytes) of the ProductID string. */
+ const int getProductIDLength() const
+ {
+ return fSysInfo.prodIDLength;
+ }
+ /** Get the ProductID string from the handheld. This is
+ * guaranteed to be NUL terminated.
+ */
+ const char* getProductID() const
+ {
+ return fSysInfo.prodID;
+ }
+
+ /** Accessor for the major version of the DLP protocol in use. */
+ const unsigned short getMajorVersion() const
+ {
+ return fSysInfo.dlpMajorVersion;
+ }
+ /** Accessor for the minor version of the DLP protocol in use. */
+ const unsigned short getMinorVersion() const
+ {
+ return fSysInfo.dlpMinorVersion;
+ }
+
+ /** Accessor for the major compatibility version of the handheld.
+ * @note I do not know what this means.
+ */
+ const unsigned short getCompatMajorVersion() const
+ {
+ return fSysInfo.compatMajorVersion;
+ }
+ /** Accessor for the minor compatibility version of the handheld.
+ * @note I do not know what this means.
+ */
+ const unsigned short getCompatMinorVersion() const
+ {
+ return fSysInfo.compatMinorVersion;
+ }
+
+
+ /** Returns the maximum record size that the handheld supports.
+ * Normally this is 65524 or so (which means that larger values
+ * don't necessarily @em fit in a short).
+ */
+ const unsigned short getMaxRecSize() const
+ {
+ return fSysInfo.maxRecSize;
+ }
+
+private:
+ struct SysInfo fSysInfo;
+};
+
+#endif
diff --git a/kpilot/lib/pilotTodoEntry.cc b/kpilot/lib/pilotTodoEntry.cc
new file mode 100644
index 000000000..f4c5596fb
--- /dev/null
+++ b/kpilot/lib/pilotTodoEntry.cc
@@ -0,0 +1,270 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This is a C++ wrapper for the todo-list entry structures.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+#include "options.h"
+
+
+#include <stdlib.h>
+
+#include <qdatetime.h>
+#include <qnamespace.h>
+
+#include <kglobal.h>
+#include <kdebug.h>
+
+
+#include "pilotTodoEntry.h"
+
+
+PilotTodoEntry::PilotTodoEntry() :
+ fDescriptionSize(0),
+ fNoteSize(0)
+{
+ FUNCTIONSETUP;
+ ::memset(&fTodoInfo, 0, sizeof(struct ToDo));
+}
+
+PilotTodoEntry::PilotTodoEntry(PilotRecord * rec) :
+ PilotRecordBase(rec),
+ fDescriptionSize(0),
+ fNoteSize(0)
+{
+ ::memset(&fTodoInfo, 0, sizeof(struct ToDo));
+ if (rec)
+ {
+ pi_buffer_t b;
+ b.data = (unsigned char *) rec->data();
+ b.allocated = b.used = rec->size();
+ unpack_ToDo(&fTodoInfo, &b, todo_v1);
+ if (fTodoInfo.description)
+ {
+ // Assume size of buffer allocated is just large enough;
+ // count trailing NUL as well.
+ fDescriptionSize = strlen(fTodoInfo.description)+1;
+ }
+ if (fTodoInfo.note)
+ {
+ // Same
+ fNoteSize = strlen(fTodoInfo.note)+1;
+ }
+ }
+
+}
+
+
+PilotTodoEntry::PilotTodoEntry(const PilotTodoEntry & e) :
+ PilotRecordBase( &e ),
+ fDescriptionSize(0),
+ fNoteSize(0)
+{
+ FUNCTIONSETUP;
+ ::memcpy(&fTodoInfo, &e.fTodoInfo, sizeof(fTodoInfo));
+ // See PilotDateEntry::operator = for details
+ fTodoInfo.description = 0L;
+ fTodoInfo.note = 0L;
+
+ setDescriptionP(e.getDescriptionP());
+ setNoteP(e.getNoteP());
+}
+
+
+PilotTodoEntry & PilotTodoEntry::operator = (const PilotTodoEntry & e)
+{
+ if (this != &e)
+ {
+ KPILOT_FREE(fTodoInfo.description);
+ KPILOT_FREE(fTodoInfo.note);
+
+ ::memcpy(&fTodoInfo, &e.fTodoInfo, sizeof(fTodoInfo));
+ // See PilotDateEntry::operator = for details
+ fTodoInfo.description = 0L;
+ fTodoInfo.note = 0L;
+ fDescriptionSize = 0;
+ fNoteSize = 0;
+
+ setDescriptionP(e.getDescriptionP());
+ setNoteP(e.getNoteP());
+
+ }
+
+ return *this;
+}
+
+QString PilotTodoEntry::getTextRepresentation(Qt::TextFormat richText)
+{
+ QString text, tmp;
+ QString par = (richText==Qt::RichText) ?CSL1("<p>"): QString();
+ QString ps = (richText==Qt::RichText) ?CSL1("</p>"):CSL1("\n");
+ QString br = (richText==Qt::RichText) ?CSL1("<br/>"):CSL1("\n");
+
+ // title + name
+ text += par;
+ tmp= (richText==Qt::RichText) ?CSL1("<b><big>%1</big></b>"):CSL1("%1");
+ text += tmp.arg(rtExpand(getDescription(), richText));
+ text += ps;
+
+ text += par;
+ if (getComplete())
+ text += i18n("Completed");
+ else
+ text += i18n("Not completed");
+ text += ps;
+
+ if (!getIndefinite())
+ {
+ QDate dt(readTm(getDueDate()).date());
+ QString dueDate(dt.toString(Qt::LocalDate));
+ text+=par;
+ text+=i18n("Due date: %1").arg(dueDate);
+ text+=ps;
+ }
+
+ text+=par;
+ text+=ps;
+
+ text+=par;
+ text+=i18n("Priority: %1").arg(getPriority());
+ text+=ps;
+
+ if (!getNote().isEmpty())
+ {
+ text += (richText==Qt::RichText) ?CSL1("<hr/>"):CSL1("-------------------------\n");
+ text+=par;
+ text+= (richText==Qt::RichText) ?i18n("<b><em>Note:</em></b><br>"):i18n("Note:\n");
+ text+=rtExpand(getNote(), richText);
+ text+=ps;
+ }
+
+ return text;
+}
+
+PilotRecord *PilotTodoEntry::pack() const
+{
+ int i;
+
+ pi_buffer_t *b = pi_buffer_new( sizeof(fTodoInfo) );
+ i = pack_ToDo(const_cast<ToDo_t *>(&fTodoInfo), b, todo_v1);
+ if (i<0)
+ {
+ return 0;
+ }
+ // pack_ToDo sets b->used
+ return new PilotRecord( b, this );
+}
+
+void PilotTodoEntry::setDescription(const QString &desc)
+{
+ if (desc.length() < fDescriptionSize)
+ {
+ Pilot::toPilot(desc, fTodoInfo.description, fDescriptionSize);
+ }
+ else
+ {
+ setDescriptionP(Pilot::toPilot(desc),desc.length());
+ }
+}
+
+void PilotTodoEntry::setDescriptionP(const char *desc, int len)
+{
+ KPILOT_FREE(fTodoInfo.description);
+ if (desc && *desc)
+ {
+ if (-1 == len)
+ {
+ len=::strlen(desc);
+ }
+
+ fDescriptionSize = len+1;
+ fTodoInfo.description = (char *)::malloc(len + 1);
+ if (fTodoInfo.description)
+ {
+ strncpy(fTodoInfo.description, desc, len);
+ fTodoInfo.description[len] = 0;
+ }
+ else
+ {
+ WARNINGKPILOT << "malloc() failed, description not set"
+ << endl;
+ }
+ }
+ else
+ {
+ fTodoInfo.description = 0L;
+ }
+}
+
+QString PilotTodoEntry::getDescription() const
+{
+ return Pilot::fromPilot(getDescriptionP());
+}
+
+void PilotTodoEntry::setNote(const QString &note)
+{
+ if (note.length() < fNoteSize)
+ {
+ Pilot::toPilot(note, fTodoInfo.note, fNoteSize);
+ }
+ else
+ {
+ setNoteP(Pilot::toPilot(note),note.length());
+ }
+}
+
+void PilotTodoEntry::setNoteP(const char *note, int len)
+{
+ KPILOT_FREE(fTodoInfo.note);
+ if (note && *note)
+ {
+ if (-1 == len)
+ {
+ len=::strlen(note);
+ }
+
+ fNoteSize = len+1;
+ fTodoInfo.note = (char *)::malloc(len + 1);
+ if (fTodoInfo.note)
+ {
+ strncpy(fTodoInfo.note, note, len);
+ fTodoInfo.note[len] = 0;
+ }
+ else
+ {
+ WARNINGKPILOT << "malloc() failed, note not set" << endl;
+ }
+ }
+ else
+ {
+ fTodoInfo.note = 0L;
+ }
+}
+
+QString PilotTodoEntry::getNote() const
+{
+ return Pilot::fromPilot(getNoteP());
+}
+
diff --git a/kpilot/lib/pilotTodoEntry.h b/kpilot/lib/pilotTodoEntry.h
new file mode 100644
index 000000000..3735771b6
--- /dev/null
+++ b/kpilot/lib/pilotTodoEntry.h
@@ -0,0 +1,166 @@
+#ifndef _KPILOT_PILOTTODOENTRY_H
+#define _KPILOT_PILOTTODOENTRY_H
+/* pilotTodoEntry.h -*- C++ -*- KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This is a wrapper around the pilot-link Memo structure. It is
+** the interpreted form of a Pilot database record.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <time.h>
+
+#include <pi-macros.h>
+#include <pi-todo.h>
+
+#include <qnamespace.h>
+#include <qstring.h>
+
+#include "pilotRecord.h"
+#include "pilotAppInfo.h"
+
+/** @file This file defines structures wrapped around the ToDo database
+* on the Pilot, based on pilot-link's ToDo stuff.
+*/
+
+/** A decoded ToDo item. */
+class KDE_EXPORT PilotTodoEntry : public PilotRecordBase
+{
+public:
+ /** Create an empty ToDo item. All attributes are 0. */
+ PilotTodoEntry();
+
+ /**
+ * Constructor. Create a ToDo item and fill it with data from the
+ * uninterpreted record @p rec. The record may be NULL, in which
+ * case the todo is empty and its category and ID are zero, as in
+ * the constructor above.
+ */
+ PilotTodoEntry(PilotRecord * rec);
+
+ /** Copy an existing ToDo item. */
+ PilotTodoEntry(const PilotTodoEntry &e);
+
+ /** Delete a ToDo item. */
+ ~PilotTodoEntry()
+ {
+ free_ToDo(&fTodoInfo);
+ }
+
+ /** Return a string for the ToDo item. If @param richText is true, then
+ * use qt style markup to make the string clearer when displayed.
+ */
+ QString getTextRepresentation(Qt::TextFormat richText);
+
+ /** Assign an existing ToDo item to this one. */
+ PilotTodoEntry& operator=(const PilotTodoEntry &e);
+
+ /** Accessor for the Due Date of the ToDo item. */
+ struct tm getDueDate() const { return fTodoInfo.due; }
+
+ /** Set the Due Date for the ToDo item. */
+ void setDueDate(struct tm& d)
+ {
+ fTodoInfo.due = d;
+ }
+
+ /** Return the indefinite status of the ToDo (? that is, whether it
+ * had a Due Date that is relevant or not). Return values are 0
+ * (not indefinite) or non-0.
+ */
+ int getIndefinite() const
+ {
+ return fTodoInfo.indefinite;
+ }
+
+ /** Set whether the ToDo is indefinite or not. */
+ void setIndefinite(int i)
+ {
+ fTodoInfo.indefinite = i;
+ }
+
+ /** Return the priority of the ToDo item. The priority ranges
+ * from 1-5 on the handheld, so this needs to be mapped (perhaps)
+ * onto KOrganizer's priority levels.
+ */
+ int getPriority() const
+ {
+ return fTodoInfo.priority;
+ }
+
+ /** Set the priority of the ToDo. */
+ void setPriority(int p)
+ {
+ fTodoInfo.priority = p;
+ }
+
+ /** Return whether the ToDo is complete (done, finished) or not. */
+ int getComplete() const
+ {
+ return fTodoInfo.complete;
+ }
+
+ /** Set whether the ToDo is done. */
+ void setComplete(int c)
+ {
+ fTodoInfo.complete = c;
+ }
+
+ /** Get the ToDo item's description (which is the title shown on
+ * the handheld, and the item's Title in KDE). This uses the default codec.
+ */
+ QString getDescription() const;
+ /** Set the ToDo item's description. */
+ void setDescription(const QString &);
+
+ /** Get the ToDo item's note (the longer text, not immediately accessible
+ * on the handheld). This uses the default codec.
+ */
+ QString getNote() const;
+
+ /** Set the ToDo item's note. */
+ void setNote(const QString &note);
+
+ /** Returns the label for the category this ToDo item is in. */
+ QString getCategoryLabel() const;
+
+ PilotRecord *pack() const;
+
+protected:
+ const char *getDescriptionP() const { return fTodoInfo.description; } ;
+ void setDescriptionP(const char *, int len=-1) ;
+ const char *getNoteP() const { return fTodoInfo.note; } ;
+ void setNoteP(const char *, int len=-1) ;
+
+private:
+ struct ToDo fTodoInfo;
+ unsigned int fDescriptionSize, fNoteSize;
+};
+
+typedef PilotAppInfo<ToDoAppInfo,unpack_ToDoAppInfo, pack_ToDoAppInfo> PilotToDoInfo;
+
+
+#endif
+
diff --git a/kpilot/lib/pilotUser.h b/kpilot/lib/pilotUser.h
new file mode 100644
index 000000000..6ba46b007
--- /dev/null
+++ b/kpilot/lib/pilotUser.h
@@ -0,0 +1,128 @@
+#ifndef _KPILOT_PILOTUSER_H
+#define _KPILOT_PILOTUSER_H
+/* pilotUser.h KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+**
+** Wrapper for the PilotUser struct from pilot-link, which describes
+** the user-data set in the Pilot.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <pi-dlp.h>
+
+#include "pilot.h"
+
+class KPilotUser
+{
+public:
+ /** Constructor. Create an empty PilotUser structure. */
+ KPilotUser()
+ {
+ ::memset(&fUser,0,sizeof(struct PilotUser));
+ }
+ /** Constructor. Use the given PilotUser structure.
+ * This creates a copy; no ownership is transferred.
+ */
+ KPilotUser(const PilotUser *user)
+ {
+ fUser = *user;
+ }
+
+ /** Accessor for the whole PilotUser structure. */
+ PilotUser *data()
+ {
+ return &fUser;
+ }
+
+ /** @return The username set on the handheld. */
+ QString name() const
+ {
+ return Pilot::fromPilot( fUser.username );
+ }
+ /** Set the user name to the given @p name , truncated
+ * if necessary to the size of the field on the handheld.
+ */
+ void setName( const QString &name )
+ {
+ Pilot::toPilot( name, fUser.username, sizeof(fUser.username) );
+ }
+
+ /** @return The length of the password on the handheld,
+ * in bytes.
+ */
+ const int passwordLength() const
+ {
+ return fUser.passwordLength;
+ }
+
+ /** @return the ID (4 bytes) of the last PC to sync this handheld.
+ * This is intended to help identify when the use has
+ * changed PCs and needs a new full sync.
+ */
+ unsigned long getLastSyncPC() const
+ {
+ return fUser.lastSyncPC;
+ }
+ /** Set the ID of the PC syncing the handheld to @p pc . This
+ * should be unique in some way (perhaps IP addresses can be
+ * used this way, or hostnames).
+ */
+ void setLastSyncPC(unsigned long pc)
+ {
+ fUser.lastSyncPC = pc;
+ }
+
+ /** @return the timestamp that the handheld was last synced
+ * successfully.
+ */
+ time_t getLastSuccessfulSyncDate()
+ {
+ return fUser.successfulSyncDate;
+ }
+ /** Set the timestamp for a successful sync. */
+ void setLastSuccessfulSyncDate(time_t when)
+ {
+ fUser.successfulSyncDate = when;
+ }
+
+ /** @return the timestamp of the last sync attempt. */
+ time_t getLastSyncDate()
+ {
+ return fUser.lastSyncDate;
+ }
+ /** Set the timestamp of the sync attempt. */
+ void setLastSyncDate(time_t when)
+ {
+ fUser.lastSyncDate = when;
+ }
+
+private:
+ struct PilotUser fUser;
+};
+
+#endif
diff --git a/kpilot/lib/plugin.cc b/kpilot/lib/plugin.cc
new file mode 100644
index 000000000..e9bcc9221
--- /dev/null
+++ b/kpilot/lib/plugin.cc
@@ -0,0 +1,760 @@
+/* KPilot
+**
+** Copyright (C) 2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+** This file defines the base class of all KPilot conduit plugins configuration
+** dialogs. This is necessary so that we have a fixed API to talk to from
+** inside KPilot.
+**
+** The factories used by KPilot plugins are also documented here.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <stdlib.h>
+
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qtextview.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+#include <kaboutapplication.h>
+#include <kactivelabel.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <klibloader.h>
+#include <kmessagebox.h>
+#include <kservice.h>
+#include <kservicetype.h>
+#include <kstandarddirs.h>
+
+#include "pilotSerialDatabase.h"
+#include "pilotLocalDatabase.h"
+
+#include "plugin.moc"
+
+ConduitConfigBase::ConduitConfigBase(QWidget *parent,
+ const char *name) :
+ QObject(parent,name),
+ fModified(false),
+ fWidget(0L),
+ fConduitName(i18n("Unnamed"))
+{
+ FUNCTIONSETUP;
+}
+
+ConduitConfigBase::~ConduitConfigBase()
+{
+ FUNCTIONSETUP;
+}
+
+/* slot */ void ConduitConfigBase::modified()
+{
+ fModified=true;
+ emit changed(true);
+}
+
+/* virtual */ QString ConduitConfigBase::maybeSaveText() const
+{
+ FUNCTIONSETUP;
+
+ return i18n("<qt>The <i>%1</i> conduit's settings have been changed. Do you "
+ "want to save the changes before continuing?</qt>").arg(this->conduitName());
+}
+
+/* virtual */ bool ConduitConfigBase::maybeSave()
+{
+ FUNCTIONSETUP;
+
+ if (!isModified()) return true;
+
+ int r = KMessageBox::questionYesNoCancel(fWidget,
+ maybeSaveText(),
+ i18n("%1 Conduit").arg(this->conduitName()), KStdGuiItem::save(), KStdGuiItem::discard());
+ if (r == KMessageBox::Cancel) return false;
+ if (r == KMessageBox::Yes) commit();
+ return true;
+}
+
+QWidget *ConduitConfigBase::aboutPage(QWidget *parent, KAboutData *ad)
+{
+ FUNCTIONSETUP;
+
+ QWidget *w = new QWidget(parent, "aboutpage");
+
+ QString s;
+ QLabel *text;
+ KIconLoader *l = KGlobal::iconLoader();
+ const KAboutData *p = ad ? ad : KGlobal::instance()->aboutData();
+
+ QGridLayout *grid = new QGridLayout(w, 5, 4, SPACING);
+
+ grid->addColSpacing(0, SPACING);
+ grid->addColSpacing(4, SPACING);
+
+
+ QPixmap applicationIcon =
+ l->loadIcon(QString::fromLatin1(p->appName()),
+ KIcon::Desktop,
+ 64, KIcon::DefaultState, 0L,
+ true);
+
+ if (applicationIcon.isNull())
+ {
+ applicationIcon = l->loadIcon(QString::fromLatin1("kpilot"),
+ KIcon::Desktop);
+ }
+
+ text = new QLabel(w);
+ // Experiment with a long non-<qt> string. Use that to find
+ // sensible widths for the columns.
+ //
+ text->setText(i18n("Send questions and comments to kdepim-users@kde.org"));
+ text->adjustSize();
+
+ int linewidth = text->size().width();
+ int lineheight = text->size().height();
+
+ // Use the label to display the applciation icon
+ text->setText(QString::null);
+ text->setPixmap(applicationIcon);
+ text->adjustSize();
+ grid->addWidget(text, 0, 1);
+
+
+ KActiveLabel *linktext = new KActiveLabel(w);
+ grid->addRowSpacing(1,kMax(100,6*lineheight));
+ grid->addRowSpacing(2,kMax(100,6*lineheight));
+ grid->addColSpacing(2,SPACING+linewidth/2);
+ grid->addColSpacing(3,SPACING+linewidth/2);
+ grid->setRowStretch(1,50);
+ grid->setRowStretch(2,50);
+ grid->setColStretch(2,50);
+ grid->setColStretch(3,50);
+ linktext->setMinimumSize(linewidth,kMax(260,60+12*lineheight));
+ linktext->setFixedHeight(kMax(260,60+12*lineheight));
+ linktext->setVScrollBarMode(QScrollView::Auto/*AlwaysOn*/);
+ text = new QLabel(w);
+ grid->addMultiCellWidget(text,0,0,2,3);
+ grid->addMultiCellWidget(linktext,1,2,1,3);
+
+ // Now set the program and copyright information.
+ s = CSL1("<qt><h3>");
+ s += p->programName();
+ s += ' ';
+ s += p->version();
+ s += CSL1("</h3>");
+ s += p->copyrightStatement() + CSL1("<br></qt>");
+ text->setText(s);
+
+ linktext->append(p->shortDescription() + CSL1("<br>"));
+
+ if (!p->homepage().isEmpty())
+ {
+ s = QString::null;
+ s += CSL1("<a href=\"%1\">").arg(p->homepage());
+ s += p->homepage();
+ s += CSL1("</a><br>");
+ linktext->append(s);
+ }
+
+ s = QString::null;
+ s += i18n("Send questions and comments to <a href=\"mailto:%1\">%2</a>.")
+ .arg( CSL1("kdepim-users@kde.org") )
+ .arg( CSL1("kdepim-users@kde.org") );
+ s += ' ';
+ s += i18n("Send bug reports to <a href=\"mailto:%1\">%2</a>.")
+ .arg(p->bugAddress())
+ .arg(p->bugAddress());
+ s += ' ';
+ s += i18n("For trademark information, see the "
+ "<a href=\"help:/kpilot/trademarks.html\">KPilot User's Guide</a>.");
+ s += CSL1("<br>");
+ linktext->append(s);
+ linktext->append(QString::null);
+
+
+
+ QValueList<KAboutPerson> pl = p->authors();
+ QValueList<KAboutPerson>::ConstIterator i;
+
+ s = i18n("<b>Authors:</b> ");
+
+ QString comma = CSL1(", ");
+
+ unsigned int count=1;
+ for (i=pl.begin(); i!=pl.end(); ++i)
+ {
+ s.append(CSL1("%1 (<i>%2</i>)%3")
+ .arg((*i).name())
+ .arg((*i).task())
+ .arg(count<pl.count() ? comma : QString::null)
+ );
+ count++;
+ }
+ linktext->append(s);
+
+ s = QString::null;
+ pl = p->credits();
+ if (pl.count()>0)
+ {
+ count=1;
+ s.append(i18n("<b>Credits:</b> "));
+ for (i=pl.begin(); i!=pl.end(); ++i)
+ {
+ s.append(CSL1("%1 (<i>%2</i>)%3")
+ .arg((*i).name())
+ .arg((*i).task())
+ .arg(count<pl.count() ? comma : QString::null)
+ );
+ count++;
+ }
+ }
+ linktext->append(s);
+ linktext->ensureVisible(0,0);
+
+ w->adjustSize();
+
+ return w;
+}
+
+/* static */ void ConduitConfigBase::addAboutPage(QTabWidget *tw,
+ KAboutData *ad)
+{
+ FUNCTIONSETUP;
+
+ Q_ASSERT(tw);
+
+ QWidget *w = aboutPage(tw,ad);
+ QSize sz = w->size();
+
+ if (sz.width() < tw->size().width())
+ {
+ sz.setWidth(tw->size().width());
+ }
+ if (sz.height() < tw->size().height())
+ {
+ sz.setHeight(tw->size().height());
+ }
+
+ tw->resize(sz);
+ tw->addTab(w, i18n("About"));
+ tw->adjustSize();
+}
+
+
+
+ConduitAction::ConduitAction(KPilotLink *p,
+ const char *name,
+ const QStringList &args) :
+ SyncAction(p,name),
+ fDatabase(0L),
+ fLocalDatabase(0L),
+ fCtrHH(0L),
+ fCtrPC(0L),
+ fSyncDirection(args),
+ fConflictResolution(SyncAction::eAskUser),
+ fFirstSync(false)
+{
+ FUNCTIONSETUP;
+
+ QString cResolution(args.grep(QRegExp(CSL1("--conflictResolution \\d*"))).first());
+ if (cResolution.isEmpty())
+ {
+ fConflictResolution=(SyncAction::ConflictResolution)
+ cResolution.replace(QRegExp(CSL1("--conflictResolution (\\d*)")), CSL1("\\1")).toInt();
+ }
+
+ for (QStringList::ConstIterator it = args.begin();
+ it != args.end();
+ ++it)
+ {
+ DEBUGKPILOT << fname << ": " << *it << endl;
+ }
+
+ DEBUGKPILOT << fname << ": Direction=" << fSyncDirection.name() << endl;
+ fCtrHH = new CUDCounter(i18n("Handheld"));
+ fCtrPC = new CUDCounter(i18n("PC"));
+}
+
+/* virtual */ ConduitAction::~ConduitAction()
+{
+ FUNCTIONSETUP;
+
+ KPILOT_DELETE(fDatabase);
+ KPILOT_DELETE(fLocalDatabase);
+
+ KPILOT_DELETE(fCtrHH);
+ KPILOT_DELETE(fCtrPC);
+}
+
+bool ConduitAction::openDatabases(const QString &name, bool *retrieved)
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname
+ << ": Trying to open database "
+ << name << endl;
+ DEBUGKPILOT << fname
+ << ": Mode="
+ << (syncMode().isTest() ? "test " : "")
+ << (syncMode().isLocal() ? "local " : "")
+ << endl ;
+
+ KPILOT_DELETE(fLocalDatabase);
+
+ QString localPathName = PilotLocalDatabase::getDBPath() + name;
+
+ // we always want to use the conduits/ directory for our local
+ // databases. this keeps our backups and data that our conduits use
+ // for record keeping separate
+ localPathName.replace(CSL1("DBBackup/"), CSL1("conduits/"));
+
+ DEBUGKPILOT << fname << ": localPathName: [" << localPathName
+ << "]" << endl;
+
+ PilotLocalDatabase *localDB = new PilotLocalDatabase( localPathName );
+
+ if (!localDB)
+ {
+ WARNINGKPILOT << "Could not initialize object for local copy of database \""
+ << name
+ << "\"" << endl;
+ if (retrieved) *retrieved = false;
+ return false;
+ }
+
+ // if there is no backup db yet, fetch it from the palm, open it and set the full sync flag.
+ if (!localDB->isOpen() )
+ {
+ QString dbpath(localDB->dbPathName());
+ KPILOT_DELETE(localDB);
+ DEBUGKPILOT << fname
+ << ": Backup database " << dbpath
+ << " not found." << endl;
+ struct DBInfo dbinfo;
+
+// TODO Extend findDatabase() with extra overload?
+ if (deviceLink()->findDatabase(Pilot::toPilot( name ), &dbinfo)<0 )
+ {
+ WARNINGKPILOT << "Could not get DBInfo for " << name << endl;
+ if (retrieved) *retrieved = false;
+ return false;
+ }
+
+ DEBUGKPILOT << fname
+ << ": Found Palm database: " << dbinfo.name <<endl
+ << fname << ": type = " << dbinfo.type
+ << " creator = " << dbinfo.creator
+ << " version = " << dbinfo.version
+ << " index = " << dbinfo.index << endl;
+ dbinfo.flags &= ~dlpDBFlagOpen;
+
+ // make sure the dir for the backup db really exists!
+ QFileInfo fi(dbpath);
+ QString path(QFileInfo(dbpath).dir(true).absPath());
+ if (!path.endsWith(CSL1("/"))) path.append(CSL1("/"));
+ if (!KStandardDirs::exists(path))
+ {
+ DEBUGKPILOT << fname << ": Trying to create path for database: <"
+ << path << ">" << endl;
+ KStandardDirs::makeDir(path);
+ }
+ if (!KStandardDirs::exists(path))
+ {
+ DEBUGKPILOT << fname << ": Database directory does not exist." << endl;
+ if (retrieved) *retrieved = false;
+ return false;
+ }
+
+ if (!deviceLink()->retrieveDatabase(dbpath, &dbinfo) )
+ {
+ WARNINGKPILOT << "Could not retrieve database "
+ << name << " from the handheld." << endl;
+ if (retrieved) *retrieved = false;
+ return false;
+ }
+ localDB = new PilotLocalDatabase( localPathName );
+ if (!localDB || !localDB->isOpen())
+ {
+ WARNINGKPILOT << "local backup of database " << name << " could not be initialized." << endl;
+ if (retrieved) *retrieved = false;
+ return false;
+ }
+ if (retrieved) *retrieved=true;
+ }
+ fLocalDatabase = localDB;
+
+ fDatabase = deviceLink()->database( name );
+
+ if (!fDatabase)
+ {
+ WARNINGKPILOT << "Could not open database \""
+ << name
+ << "\" on the pilot."
+ << endl;
+ }
+ else
+ {
+ fCtrHH->setStartCount(fDatabase->recordCount());
+ }
+
+ return (fDatabase && fDatabase->isOpen() &&
+ fLocalDatabase && fLocalDatabase->isOpen() );
+}
+
+
+bool ConduitAction::changeSync(SyncMode::Mode m)
+{
+ FUNCTIONSETUP;
+
+ if ( fSyncDirection.isSync() && SyncMode::eFullSync == m)
+ {
+ fSyncDirection.setMode(m);
+ return true;
+ }
+ return false;
+}
+
+void ConduitAction::finished()
+{
+ FUNCTIONSETUP;
+
+ if (fDatabase && fCtrHH)
+ fCtrHH->setEndCount(fDatabase->recordCount());
+
+ if (fCtrHH && fCtrPC)
+ {
+ addSyncLogEntry(fCtrHH->moo() +"\n",false);
+ DEBUGKPILOT << fname << ": " << fCtrHH->moo() << endl;
+ addSyncLogEntry(fCtrPC->moo() +"\n",false);
+ DEBUGKPILOT << fname << ": " << fCtrPC->moo() << endl;
+
+ // STEP2 of making sure we don't delete our little user's
+ // precious data...
+ // sanity checks for handheld...
+ int hhVolatility = fCtrHH->percentDeleted() +
+ fCtrHH->percentUpdated() +
+ fCtrHH->percentCreated();
+
+ int pcVolatility = fCtrPC->percentDeleted() +
+ fCtrPC->percentUpdated() +
+ fCtrPC->percentCreated();
+
+ // TODO: allow user to configure this...
+ // this is a percentage...
+ int allowedVolatility = 70;
+
+ QString caption = i18n("Large Changes Detected");
+ // args are already i18n'd
+ QString query = i18n("The %1 conduit has made a "
+ "large number of changes to your %2. Do you want "
+ "to allow this change?\nDetails:\n\t%3");
+
+ if (hhVolatility > allowedVolatility)
+ {
+ query = query.arg(fConduitName)
+ .arg(fCtrHH->type()).arg(fCtrHH->moo());
+
+ DEBUGKPILOT << fname << ": Yikes, lots of volatility "
+ << "caught. Check with user: [" << query
+ << "]." << endl;
+
+ /*
+ int rc = questionYesNo(query, caption,
+ QString::null, 0 );
+ if (rc == KMessageBox::Yes)
+ {
+ // TODO: add commit and rollback code.
+ // note: this will require some thinking,
+ // since we have to undo changes to the
+ // pilot databases, changes to the PC
+ // resources, changes to the mappings files
+ // (record id mapping, etc.)
+ }
+ */
+ }
+
+
+ }
+
+}
+
+
+ConduitProxy::ConduitProxy(KPilotLink *p,
+ const QString &name,
+ const SyncAction::SyncMode &m) :
+ ConduitAction(p,name.latin1(),m.list()),
+ fDesktopName(name)
+{
+ FUNCTIONSETUP;
+}
+
+/* virtual */ bool ConduitProxy::exec()
+{
+ FUNCTIONSETUP;
+
+ // query that service
+ KSharedPtr < KService > o = KService::serviceByDesktopName(fDesktopName);
+ if (!o)
+ {
+ WARNINGKPILOT << "Can't find desktop file for conduit "
+ << fDesktopName
+ << endl;
+ addSyncLogEntry(i18n("Could not find conduit %1.").arg(fDesktopName));
+ return false;
+ }
+
+
+ // load the lib
+ fLibraryName = o->library();
+ DEBUGKPILOT << fname
+ << ": Loading desktop "
+ << fDesktopName
+ << " with lib "
+ << fLibraryName
+ << endl;
+
+ KLibrary *library = KLibLoader::self()->library(
+ QFile::encodeName(fLibraryName));
+ if (!library)
+ {
+ WARNINGKPILOT << "Can't load library "
+ << fLibraryName
+ << " - "
+ << KLibLoader::self()->lastErrorMessage()
+ << endl;
+ addSyncLogEntry(i18n("Could not load conduit %1.").arg(fDesktopName));
+ return false;
+ }
+
+ unsigned long version = PluginUtility::pluginVersion(library);
+ if ( Pilot::PLUGIN_API != version )
+ {
+ WARNINGKPILOT << "Library "
+ << fLibraryName
+ << " has version "
+ << version
+ << endl;
+ addSyncLogEntry(i18n("Conduit %1 has wrong version (%2).").arg(fDesktopName).arg(version));
+ return false;
+ }
+
+ KLibFactory *factory = library->factory();
+ if (!factory)
+ {
+ WARNINGKPILOT << "Can't find factory in library "
+ << fLibraryName
+ << endl;
+ addSyncLogEntry(i18n("Could not initialize conduit %1.").arg(fDesktopName));
+ return false;
+ }
+
+ QStringList l = syncMode().list();
+
+ DEBUGKPILOT << fname << ": Flags: " << syncMode().name() << endl;
+
+ QObject *object = factory->create(fHandle,name(),"SyncAction",l);
+
+ if (!object)
+ {
+ WARNINGKPILOT << "Can't create SyncAction." << endl;
+ addSyncLogEntry(i18n("Could not create conduit %1.").arg(fDesktopName));
+ return false;
+ }
+
+ fConduit = dynamic_cast<ConduitAction *>(object);
+
+ if (!fConduit)
+ {
+ WARNINGKPILOT << "Can't cast to ConduitAction." << endl;
+ addSyncLogEntry(i18n("Could not create conduit %1.").arg(fDesktopName));
+ return false;
+ }
+
+ addSyncLogEntry(i18n("[Conduit %1]").arg(fDesktopName));
+
+ // Handle the syncDone signal properly & unload the conduit.
+ QObject::connect(fConduit,SIGNAL(syncDone(SyncAction *)),
+ this,SLOT(execDone(SyncAction *)));
+ // Proxy all the log and error messages.
+ QObject::connect(fConduit,SIGNAL(logMessage(const QString &)),
+ this,SIGNAL(logMessage(const QString &)));
+ QObject::connect(fConduit,SIGNAL(logError(const QString &)),
+ this,SIGNAL(logError(const QString &)));
+ QObject::connect(fConduit,SIGNAL(logProgress(const QString &,int)),
+ this,SIGNAL(logProgress(const QString &,int)));
+
+ QTimer::singleShot(0,fConduit,SLOT(execConduit()));
+ return true;
+}
+
+void ConduitProxy::execDone(SyncAction *p)
+{
+ FUNCTIONSETUP;
+
+ if (p!=fConduit)
+ {
+ WARNINGKPILOT << "Unknown conduit @"
+ << (void *) p
+ << " finished."
+ << endl;
+ emit syncDone(this);
+ return;
+ }
+
+ // give our worker a chance to sanity check the results...
+ fConduit->finished();
+
+ addSyncLogEntry(CSL1("\n"),false); // Put bits of the conduit logs on separate lines
+
+ KPILOT_DELETE(p);
+
+ emit syncDone(this);
+}
+
+
+namespace PluginUtility
+{
+
+QString findArgument(const QStringList &a, const QString &arg)
+{
+ FUNCTIONSETUP;
+
+ QString search;
+
+ if (arg.startsWith( CSL1("--") ))
+ {
+ search = arg;
+ }
+ else
+ {
+ search = CSL1("--") + arg;
+ }
+ search.append( CSL1("=") );
+
+
+ QStringList::ConstIterator end = a.end();
+ for (QStringList::ConstIterator i = a.begin(); i != end; ++i)
+ {
+ if ((*i).startsWith( search ))
+ {
+ QString s = (*i).mid(search.length());
+ return s;
+ }
+ }
+
+ return QString::null;
+}
+
+/* static */ bool isRunning(const QCString &n)
+{
+ DCOPClient *dcop = KApplication::kApplication()->dcopClient();
+ QCStringList apps = dcop->registeredApplications();
+ return apps.contains(n);
+}
+
+
+/* static */ unsigned long pluginVersion(const KLibrary *lib)
+{
+ QString symbol = CSL1("version_");
+ symbol.append(lib->name());
+
+ if (!lib->hasSymbol(symbol.latin1())) return 0;
+
+ unsigned long *p = (unsigned long *)(lib->symbol(symbol.latin1()));
+ return *p;
+}
+
+
+/* static */ QString pluginVersionString(const KLibrary *lib)
+{
+ QString symbol= CSL1("id_");
+ symbol.append(lib->name());
+
+ if (!lib->hasSymbol(symbol.latin1())) return QString::null;
+
+ return QString::fromLatin1(*((const char **)(lib->symbol(symbol.latin1()))));
+}
+
+
+}
+
+
+CUDCounter::CUDCounter(QString s) :
+ fC(0),fU(0),fD(0),fStart(0),fEnd(0),fType(s)
+{
+}
+
+void CUDCounter::created(unsigned int c)
+{
+ fC += c;
+}
+
+void CUDCounter::updated(unsigned int c)
+{
+ fU += c;
+}
+
+void CUDCounter::deleted(unsigned int c)
+{
+ fD += c;
+}
+
+void CUDCounter::setStartCount(unsigned int t)
+{
+ fStart = t;
+}
+
+void CUDCounter::setEndCount(unsigned int t)
+{
+ fEnd = t;
+}
+
+QString CUDCounter::moo() const
+{
+ QString result = fType + ": " +
+ i18n("Start: %1. End: %2. ").arg(fStart).arg(fEnd);
+
+ if (fC > 0) result += i18n("%1 new. ").arg(fC);
+ if (fU > 0) result += i18n("%1 changed. ").arg(fU);
+ if (fD > 0) result += i18n("%1 deleted. ").arg(fD);
+
+ if ( (fC+fU+fD) <= 0) result += i18n("No changes made. ");
+
+ return result;
+}
+
+
diff --git a/kpilot/lib/plugin.h b/kpilot/lib/plugin.h
new file mode 100644
index 000000000..b825b899f
--- /dev/null
+++ b/kpilot/lib/plugin.h
@@ -0,0 +1,476 @@
+#ifndef _KPILOT_PLUGIN_H
+#define _KPILOT_PLUGIN_H
+/* KPilot
+**
+** Copyright (C) 2001 by Dan Pilone
+** Copyright (C) 2002-2004,2006 Adriaan de Groot <groot@kde.org>
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qstringlist.h>
+
+#include "syncAction.h"
+
+/** @file
+* This file defines the base class of all KPilot conduit plugins configuration
+* dialogs. This is necessary so that we have a fixed API to talk to from
+* inside KPilot.
+*
+* The factories used by KPilot plugins are also documented here.
+*/
+
+
+class QTabWidget;
+class KAboutData;
+class KLibrary;
+
+class PilotDatabase;
+
+namespace Pilot
+{
+ /**
+ * As the API for conduits may change in the course of time,
+ * identify them and refuse to load incompatible API versions.
+ * Bump this number every release to the current YYYYMMDD
+ * value.
+ */
+ static const unsigned int PLUGIN_API = 20061118;
+}
+
+/**
+* ConduitConfigBase is for configuration purposes.
+*
+* ConduitConfigBase: this is an object (with a widget!) that is embedded
+* in a dialog. This is the currently preferred form for configuration,
+* and it's what is used in the KPilot conduit configuration dialog.
+* The factory is asked for a "ConduitConfigBase" object.
+*
+* NB. The reason that this is a QObject which needs to create a
+* separate widget - instead of a QWidget subclass - has to do with
+* layouting. If you make the widget with designer then the easiest
+* thing to do is to use a grid layout there. Making ConduitConfigBase
+* a QWidget subclass would require an additional layout here, which
+* seems a little foolish.
+*
+*/
+class KDE_EXPORT ConduitConfigBase : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Constructor. Creates a conduit configuration support object
+ * with the given parent @p parent and name (optional) @p n.
+ */
+ ConduitConfigBase(QWidget *parent=0L, const char *n=0L);
+
+ /** Destructor. */
+ virtual ~ConduitConfigBase();
+
+ /**
+ * This function is called to check whether the configuration
+ * of the conduit has changed -- and hence, whether the user
+ * needs to be prompted. By default, this just returns
+ * fModified, but you can do more complicated things.
+ */
+ virtual bool isModified() const
+ {
+ return fModified;
+ } ;
+
+ /** Accessor for the actual widget for the configuration. */
+ QWidget *widget() const
+ {
+ return fWidget;
+ } ;
+
+ /**
+ * Load or save the config widget's settings in the given
+ * KConfig object; leave the group unchanged. load() and
+ * commit() should both call unmodified() to indicate that
+ * the current settings match the on-disk ones.
+ */
+ virtual void commit() = 0;
+ virtual void load() = 0;
+ /**
+ * Called when the object is to be hidden again and might
+ * need to save changed settings. Should prompt the user
+ * and call commit() if needed. Override this function only
+ * if you need a very different kind of prompt window.
+ *
+ * Returns false if the change is to be canceled. Returns
+ * true otherwise, whether or not the changes were saved.
+ */
+ virtual bool maybeSave();
+
+ QString conduitName() const { return fConduitName; } ;
+
+ /**
+ * This is the function that does the work of adding an about
+ * page to a tabwidget. It is made public and static so that
+ * it can be used elsewhere wherever tabwidgets appear.
+ *
+ * The about tab is created using aboutPage(). The new about
+ * widget is added to the tab widget @p w with the heading
+ * "About".
+ *
+ * @param w The tab widget to which the about page is added.
+ * @param data The KAboutData that is used.
+ *
+ */
+ static void addAboutPage(QTabWidget *w,
+ KAboutData *data=0L);
+
+ /**
+ * This creates the actual about widget. Again, public & static so
+ * you can slap in an about widget wherever.
+ *
+ * An about widget is created that shows the contributors to
+ * the application, along with copyright information and the
+ * application's icon. This widget can be used pretty much
+ * anywhere. Copied from KAboutDialog, mostly.
+ *
+ * @param parent The widget that holds the about widget.
+ * @param data The KAboutData that is used to populate the widget.
+ */
+ static QWidget *aboutPage(QWidget *parent, KAboutData *data=0L);
+
+protected:
+ /**
+ * This function provides the string for the prompt used
+ * in maybeSave(). Override it to change the text.
+ */
+ virtual QString maybeSaveText() const;
+
+ void unmodified() { fModified=false; } ;
+
+ bool fModified;
+ QWidget *fWidget;
+ QString fConduitName;
+
+
+protected slots:
+ void modified();
+signals:
+ void changed(bool);
+
+} ;
+
+
+/**
+* Create-Update-Delete tracking of the plugin,
+* used for reporting purposes (in a consistent manner). The intent
+* is that this class is used by the conduit as it is syncing data.
+* For this to be useful (and be used properly), the conduit needs
+* to tell us how many creates, updates, and deletes it has made to
+* a data store (PC or HH). It also needs to tell us how many
+* records it started with and how many records it has at the
+* conclusion of its processing. Using this information, we can
+* report on it consistently as well as analyze the activity taken
+* by the conduit and offer rollback functionality if we think the
+* conduit has behaved improperly.
+*/
+class KDE_EXPORT CUDCounter
+{
+public:
+ /** Create new counter initialized to 0, and be told what
+ * kind of CUD we're counting (PC or Handheld, etc.) */
+ CUDCounter(QString s);
+
+ /** Track the creation of @p c items */
+ void created(unsigned int c=1);
+ /** Track updates to @p u items */
+ void updated(unsigned int u=1);
+ /** Track the destruction of @p d items */
+ void deleted(unsigned int d=1);
+ /** How many @p t items did we start with? */
+ void setStartCount(unsigned int t);
+ /** How many @p t items did we end with? */
+ void setEndCount(unsigned int t);
+
+ unsigned int countCreated() { return fC; }
+ unsigned int countUpdated() { return fU; }
+ unsigned int countDeleted() { return fD; }
+ unsigned int countStart() { return fStart; }
+ unsigned int countEnd() { return fEnd; }
+
+ /** percentage of changes. unfortunately, we have to rely on our
+ * developers (hi, self!) to correctly set total number of records
+ * conduits start with, so add a little protection...
+ */
+ unsigned int percentCreated() { return (fEnd > 0 ? fC/fEnd : 0); }
+ unsigned int percentUpdated() { return (fEnd > 0 ? fU/fEnd : 0); }
+ unsigned int percentDeleted() { return (fStart > 0 ? fD/fStart : 0); }
+
+ /** Measurement Of Objects -- report numbers of
+ * objects created, updated, deleted. This
+ * string is already i18n()ed.
+ */
+ QString moo() const;
+
+ /** Type of counter(Handheld or PC). This string is already
+ * i18n()ed.
+ */
+ QString type() const { return fType; }
+private:
+ /** keep track of Creates, Updates, Deletes, and Total
+ * number of records so we can detect abnormal behavior and
+ * hopefully prevent data loss.
+ */
+ unsigned int fC,fU,fD,fStart,fEnd;
+
+ /** What kind of CUD are we keeping track of so we can
+ * moo() it out later? (PC, Handheld, etc.)
+ */
+ QString fType;
+} ;
+
+
+/**
+* The SyncActions created by the factory should obey at least
+* the argument test, indicating a dry run. The device link is
+* the link where the sync should run -- don't get the pilotPort()
+* until the sync runs!
+*
+* setConfig() will be called before the sync starts so that the
+* conduit can read/write metadata and local settings.
+*/
+
+class KDE_EXPORT ConduitAction : public SyncAction
+{
+Q_OBJECT
+
+public:
+ ConduitAction(KPilotLink *,
+ const char *name=0L,
+ const QStringList &args = QStringList());
+ virtual ~ConduitAction();
+
+ /** ConduitAction is done doing work. Allow it to sanity-check the
+ * results
+ */
+ void finished();
+
+ QString conduitName() const { return fConduitName; } ;
+
+ /** Retrieve the sync mode set for this action. */
+ const SyncMode &syncMode() const { return fSyncDirection; };
+
+ /**
+ * A full sync happens for eFullSync, eCopyPCToHH and eCopyHHToPC. It
+ * completely ignores all modified flags and walks through all records
+ * in the database.
+ */
+ bool isFullSync() const
+ {
+ return fFirstSync || fSyncDirection.isFullSync() ;
+ }
+
+ /**
+ * A first sync (i.e. database newly fetched from the handheld )
+ * does not check for deleted records, but understands them as
+ * added on the other side. The flag is set by the conduits
+ * when opening the local database, or the calendar/addressbook
+ * (if it is empty). This also implies a full sync.
+ */
+ bool isFirstSync() const
+ {
+ return fFirstSync || fSyncDirection.isFirstSync() ;
+ }
+
+protected:
+ /** Retrieve the conflict resolution setting for this action. */
+ ConflictResolution getConflictResolution() const
+ { return fConflictResolution; };
+
+ /** Try to change the sync mode from what it is now to the mode @p m.
+ * This may fail (ie. changing a backup to a restore is not kosher) and
+ * changeSync() will return false then.
+ */
+ bool changeSync(SyncMode::Mode m);
+
+ // Set the conflict resolution, except if the resolution
+ // form is UseGlobalSetting, in which case nothing changes
+ // (assumes then that the resolution form is already set
+ // according to that global setting).
+ //
+ void setConflictResolution(ConflictResolution res)
+ {
+ if (SyncAction::eUseGlobalSetting != res)
+ fConflictResolution=res;
+ }
+
+ void setFirstSync(bool first) { fFirstSync=first; } ;
+
+ PilotDatabase *fDatabase;
+ PilotDatabase *fLocalDatabase; // Guaranteed to be a PilotLocalDatabase
+
+ /**
+ * Open both the local copy of database @p dbName
+ * and the version on the Pilot. Return true only
+ * if both opens succeed. If the local copy of the database
+ * does not exist, it is retrieved from the handheld. In this
+ * case, retrieved is set to true, otherwise it is left alone
+ * (i.e. retains its value and is not explicitly set to false).
+ *
+ * @param dbName database name to open.
+ * @param retrieved indicator whether the database had to be loaded
+ * from the handheld.
+ */
+ bool openDatabases(const QString &dbName, bool*retrieved=0L);
+
+ /**
+ * Name of the conduit; might be changed by subclasses. Should
+ * normally be set in the constructor.
+ */
+ QString fConduitName;
+
+ /** Every plugin has 2 CUDCounters--one for keeping track of
+ * changes made to PC data and one for keeping track of Palm data. */
+ CUDCounter *fCtrHH;
+ CUDCounter *fCtrPC;
+
+private:
+ SyncMode fSyncDirection;
+ ConflictResolution fConflictResolution;
+
+ bool fFirstSync;
+} ;
+
+/**
+* The ConduitProxy action delays loading the plugin for a conduit until the conduit
+* actually executes; the proxy then loads the file, creates a SyncAction for the conduit
+* and runs that. Once the conduit has finished, the proxy unloads everything
+* and emits syncDone().
+*/
+class ConduitProxy : public ConduitAction
+{
+Q_OBJECT
+
+public:
+ ConduitProxy(KPilotLink *,
+ const QString &desktopName,
+ const SyncAction::SyncMode &m);
+
+protected:
+ virtual bool exec();
+protected slots:
+ void execDone(SyncAction *);
+
+protected:
+ QString fDesktopName;
+ QString fLibraryName;
+ ConduitAction *fConduit;
+} ;
+
+/** A namespace containing only static helper methods. */
+namespace PluginUtility
+{
+ /** Searches the argument list for --foo=bar and returns bar, QString::null if not found.
+ * Don't include the -- in the argname. */
+ QString findArgument(const QStringList &a, const QString argname);
+
+ /**
+ * This function attempts to detect whether or not the given
+ * application is running. If it is, true is returned, otherwise
+ * false.
+ *
+ * The current approach is to ask the DCOP server if the application
+ * has registered.
+ */
+ bool isRunning(const QCString &appName);
+
+ /**
+ * Check a given library for its version, returning 0 if no
+ * version symbol is found.
+ */
+ unsigned long pluginVersion(const KLibrary *);
+ QString pluginVersionString(const KLibrary *);
+}
+
+/**
+* All KPilot conduits should subclass KLibFactory like this.
+*
+* Boilerplate for inheritance:
+*
+* <pre>
+* class KPilotPlugin : public KLibFactory
+* {
+* Q_OBJECT
+*
+* public:
+* KPilotPlugin(QObject * = 0L,const char * = 0L) ;
+* virtual ~KPilotPlugin();
+* </pre>
+*
+* You don't @em have to provide about information for the plugin,
+* but it's useful, particularly for the about box in a conduit.
+*
+*
+* <pre>
+* static KAboutData *about() { return fAbout; } ;
+* </pre>
+*
+*
+* This is what it's all about: creating objects for the plugin.
+* One classname that @em must be supported is ConduitConfig,
+* which is defined above. The other is SyncAction.
+*
+*
+* <pre>
+* protected:
+* virtual QObject* createObject( QObject* parent = 0,
+* const char* name = 0,
+* const char* classname = "QObject",
+* const QStringList &args = QStringList() );
+* </pre>
+*
+* More boilerplate, and support for an instance and about data, used
+* by about() above.
+*
+* <pre>
+* KInstance *fInstance;
+* static KAboutData *fAbout;
+* } ;
+* </pre>
+*
+*
+*
+* The implementation of a conduit needs an init_conduit_name() function,
+* just like any KLibLoader library that uses factories.
+*
+* The createObject() function needs to support at least two creation
+* calls: "ConduitConfigBase" and "SyncAction".
+* "ConduitConfigBase" should return a subclass of ConduitConfigBase,
+* "SyncAction" a subclass of SyncAction.
+*
+* Finally, a conduit should have a symbol version_conduit_name,
+* that returns a long; much like the init_conduit_name() function. This
+* should return the version of the plugin API (KPILOT_PLUGIN_VERSION)
+* the conduit was compiled against. Additionally, a plugin may have a
+* id_conduit_name, which should be a const char *.
+*
+*/
+
+#endif
diff --git a/kpilot/lib/pluginfactory.h b/kpilot/lib/pluginfactory.h
new file mode 100644
index 000000000..8eecc5584
--- /dev/null
+++ b/kpilot/lib/pluginfactory.h
@@ -0,0 +1,98 @@
+#ifndef _KPILOT_PLUGINFACTORY_H
+#define _KPILOT_PLUGINFACTORY_H
+/* KPilot
+**
+** Copyright (C) 2005-2006 by Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qwidget.h>
+
+#include <kdebug.h>
+#include <klibloader.h>
+
+#include "options.h"
+
+/** @file Defines a template class for factories for KPilot's conduits. */
+
+class KPilotLink;
+
+
+
+/** Template class that defines a conduit's factory. */
+
+template <class Widget, class Action> class ConduitFactory : public KLibFactory
+{
+public:
+ ConduitFactory(QObject *parent = 0, const char *name = 0) :
+ KLibFactory(parent,name)
+ { fInstance = new KInstance(name); } ;
+ virtual ~ConduitFactory()
+ { delete fInstance; } ;
+
+protected:
+ virtual QObject *createObject(
+ QObject* parent = 0,
+ const char* name = 0,
+ const char* classname = "QObject",
+ const QStringList &args = QStringList() )
+ {
+ if (qstrcmp(classname,"ConduitConfigBase")==0)
+ {
+ QWidget *w = dynamic_cast<QWidget *>(parent);
+ if (w) return new Widget(w,name);
+ else
+ {
+ WARNINGKPILOT << "Could not cast parent to widget." << endl;
+ return 0L;
+ }
+ }
+
+ if (qstrcmp(classname,"SyncAction")==0)
+ {
+ KPilotLink *d = 0L;
+ if (parent) d = dynamic_cast<KPilotLink *>(parent);
+
+ if (d || !parent)
+ {
+ if (!parent)
+ {
+ kdDebug() << k_funcinfo << ": Using NULL device." << endl;
+ }
+ return new Action(d,name,args);
+ }
+ else
+ {
+ WARNINGKPILOT << "Could not cast parent to KPilotLink" << endl;
+ return 0L;
+ }
+ }
+ return 0L;
+ }
+
+ KInstance *fInstance;
+} ;
+
+#endif
+
diff --git a/kpilot/lib/recordConduit.cc b/kpilot/lib/recordConduit.cc
new file mode 100644
index 000000000..f11b3b573
--- /dev/null
+++ b/kpilot/lib/recordConduit.cc
@@ -0,0 +1,1145 @@
+/* KPilot
+**
+** Copyright (C) 2004 by Reinhold Kainhofer
+** Based on the addressbook conduit:
+** Copyright (C) 2000,2001 by Dan Pilone
+** Copyright (C) 2000 Gregory Stern
+** Copyright (C) 2002-2003 by Reinhold Kainhofer
+**
+** This conduit is the base class for all record-based conduits.
+** all the sync logic is included in this class, and all child classes
+** just have to implement some specific copying and conflict resolution
+** methods.
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org.
+*/
+
+
+
+#include "options.h"
+
+#include <qtimer.h>
+#include <qfile.h>
+
+#include "pilotAppCategory.h"
+#include "pilotSerialDatabase.h"
+#include "pilotLocalDatabase.h"
+#include "recordConduit.h"
+
+
+// Something to allow us to check what revision
+// the modules are that make up a binary distribution.
+//
+//
+extern "C"
+{
+long version_record_conduit = Pilot::PLUGIN_API;
+}
+
+
+/* virtual */ bool RecordConduitBase::exec()
+{
+ FUNCTIONSETUP;
+ fState = Initialize;
+
+ setFirstSync(false);
+
+ bool retrieved = false;
+ if (!openDatabases( fDBName, &retrieved))
+ {
+ emit logError(i18n("Unable to open the %1 database on the handheld.").arg( fDBName ) );
+ return false;
+ }
+ if (retrieved) setFirstSync(true);
+
+ if (isFirstSync()) fIDList=fDatabase->idList();
+ else fIDList=fDatabase->modifiedIDList();
+ fIDListIterator = fIDList.begin();
+
+ fTimer = new QTimer(this);
+ connect(fTimer,SIGNAL(timeout()),this,SLOT(process()));
+ fTimer->start(0,false); // Fire as often as possible to prompt processing
+ return true;
+}
+
+/* virtual */ void RecordConduitBase::process()
+{
+ FUNCTIONSETUP;
+ SyncProgress p = Error;
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": From state " << name(fState) << endl;
+#endif
+
+ switch(fState)
+ {
+ case Initialize :
+ p = loadPC();
+ break;
+ case PalmToPC :
+ p = palmRecToPC();
+ break;
+ case PCToPalm :
+ p = pcRecToPalm();
+ break;
+ case Cleanup :
+ p = cleanup();
+ break;
+ }
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": Step returned " << name(p) << endl;
+#endif
+
+ switch(p)
+ {
+ case Error :
+ fTimer->stop();
+ delayDone();
+ return;
+ case NotDone :
+ // Return so we get called again.
+ return;
+ case Done :
+ // Get on with it.
+ break;
+ }
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": Step is done, moving to next state." << endl;
+#endif
+
+ // Here the previous call was done.
+ switch(fState)
+ {
+ case Initialize :
+ switch (syncMode().mode())
+ {
+ case SyncMode::eRestore :
+ case SyncMode::eCopyPCToHH : /* These two don't copy Palm records to the PC */
+ fState = PCToPalm;
+ break;
+ default :
+ fState = PalmToPC;
+ }
+ break;
+ case PalmToPC :
+ switch (syncMode().mode())
+ {
+ case SyncMode::eBackup :
+ case SyncMode::eCopyHHToPC : /* These modes don't copy PC records back */
+ fState = Cleanup;
+ break;
+ default :
+ fState = PCToPalm;
+ }
+ break;
+ case PCToPalm :
+ fState = Cleanup;
+ break;
+ case Cleanup :
+ fTimer->stop();
+ delayDone();
+ // No change in state, timer stopped and we're done.
+ break;
+ }
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": Next state is " << name(fState) << endl;
+#endif
+
+}
+
+
+QString RecordConduitBase::name(RecordConduitBase::SyncProgress s)
+{
+ switch(s)
+ {
+ case RecordConduitBase::NotDone:
+ return CSL1("NotDone");
+ case RecordConduitBase::Done:
+ return CSL1("Done");
+ case RecordConduitBase::Error:
+ return CSL1("Error");
+ }
+}
+
+
+QString RecordConduitBase::name(RecordConduitBase::States s)
+{
+ switch(s)
+ {
+ case RecordConduitBase::Initialize:
+ return CSL1("Initialize");
+ case RecordConduitBase::PalmToPC:
+ return CSL1("Handheld-to-PC");
+ case RecordConduitBase::PCToPalm:
+ return CSL1("PC-to-Handheld");
+ case RecordConduitBase::Cleanup:
+ return CSL1("Cleanup");
+ }
+}
+
+
+#if 0
+/** make that entry on the pc archived (i.e. deleted on the handheld,
+ * while present on the pc, but not synced to the handheld */
+bool RecordConduit::PCData::makeArchived( RecordConduit::PCEntry *pcEntry )
+{
+ if ( pcEntry ) {
+ pcEntry->makeArchived();
+ setChanged( true );
+ return true;
+ } else return false;
+}
+
+
+/* Builds the map which links record ids to uid's of PCEntry. This is the slow implementation,
+ * that should always work. subclasses should reimplement it to speed things up.
+*/
+bool RecordConduit::PCData::mapContactsToPilot( QMap<recordid_t,QString> &idContactMap )
+{
+ FUNCTIONSETUP;
+
+ idContactMap.clear();
+
+ Iterator it = begin();
+ PCEntry *ent;
+ while ( !atEnd( it ) ) {
+ ent = *it;
+ recordid_t id( ent->recid() );
+ if ( id != 0 ) {
+ idContactMap.insert( id, ent->uid() );
+ }
+ ++it;
+ }
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": Loaded " << idContactMap.size() <<
+ " Entries on the pc and mapped them to records on the handheld. " << endl;
+#endif
+ return true;
+}
+
+
+
+/*********************************************************************
+ C O N S T R U C T O R
+ *********************************************************************/
+
+
+
+bool RecordConduit::mArchiveDeleted = false;
+
+RecordConduit::RecordConduit(QString name, KPilotDeviceLink * o, const char *n, const QStringList & a):
+ ConduitAction(o, n, a),
+ mPCData(0), mPalmIndex(0),
+ mEntryMap(), mSyncedIds(), mAllIds()
+{
+ FUNCTIONSETUP;
+ fConduitName = name;
+}
+
+
+
+RecordConduit::~RecordConduit()
+{
+ if ( mPCData ) KPILOT_DELETE(mPCData);
+}
+
+
+
+
+
+
+/*********************************************************************
+ S Y N C S T R U C T U R E
+ *********************************************************************/
+
+
+
+/* virtual */ bool RecordConduit::exec()
+{
+ FUNCTIONSETUP;
+
+ if ( !_prepare() ) return false;
+
+ fFirstSync = false;
+ // Database names probably in latin1.
+ if( !openDatabases( dbName(), &fFirstSync ) )
+ {
+ emit logError(i18n("Unable to open the %1 database on the handheld.").arg( dbName() ) );
+ return false;
+ }
+ _getAppInfo();
+ if( !mPCData->loadData() )
+ {
+ emit logError( i18n("Unable to open %1.").arg( mPCData->description() ) );
+ return false;
+ }
+ // get the addresseMap which maps Pilot unique record(address) id's to
+ // a Abbrowser Addressee; allows for easy lookup and comparisons
+ if ( mPCData->isEmpty() )
+ fFirstSync = true;
+ else
+ mPCData->mapContactsToPilot( mEntryMap );
+ fFirstSync = fFirstSync || ( mPCData->isEmpty() );
+
+ // perform syncing from palm to abbrowser
+ // iterate through all records in palm pilot
+ mPalmIndex = 0;
+
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": fullsync=" << isFullSync() << ", firstSync=" << isFirstSync() << endl;
+ DEBUGKPILOT << fname << ": "
+ << "syncDirection=" << getSyncDirection() << ", "
+// << "archive = " << AbbrowserSettings::archiveDeleted()
+ << endl;
+ DEBUGKPILOT << fname << ": conflictRes="<< getConflictResolution() << endl;
+// DEBUGKPILOT << fname << ": PilotStreetHome=" << AbbrowserSettings::pilotStreet() << ", PilotFaxHOme" << AbbrowserSettings::pilotFax() << endl;
+#endif
+
+ if ( !isFirstSync() )
+ mAllIds=fDatabase->idList();
+
+ /* Note:
+ if eCopyPCToHH or eCopyHHToPC, first sync everything, then lookup
+ those entries on the receiving side that are not yet syncced and delete
+ them. Use slotDeleteUnsyncedPCRecords and slotDeleteUnsyncedHHRecords
+ for this, and no longer purge the whole addressbook before the sync to
+ prevent data loss in case of connection loss. */
+
+ QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
+
+ return true;
+}
+
+
+
+void RecordConduit::slotPalmRecToPC()
+{
+ FUNCTIONSETUP;
+ PilotRecord *palmRec = 0L, *backupRec = 0L;
+
+ if ( getSyncDirection() == SyncAction::eCopyPCToHH )
+ {
+ mPCIter = mPCData->begin();
+ QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
+ return;
+ }
+
+ if ( isFullSync() )
+ palmRec = fDatabase->readRecordByIndex( mPalmIndex++ );
+ else
+ palmRec = dynamic_cast <PilotSerialDatabase * >(fDatabase)->readNextModifiedRec();
+
+ if ( !palmRec )
+ {
+ mPCIter = mPCData->begin();
+ QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
+ return;
+ }
+
+ // already synced, so skip:
+ if ( mSyncedIds.contains( palmRec->id() ) )
+ {
+ KPILOT_DELETE( palmRec );
+ QTimer::singleShot( 0, this, SLOT( slotPalmRecToPC() ) );
+ return;
+ }
+
+ backupRec = fLocalDatabase->readRecordById( palmRec->id() );
+ PilotRecord *compareRec = backupRec ? backupRec : palmRec;
+ PilotAppCategory *compareEntry = createPalmEntry( compareRec );
+ PCEntry *pcEntry = findMatch( compareEntry );
+ KPILOT_DELETE( compareEntry );
+
+ PilotAppCategory *backupEntry=0L;
+ if ( backupRec )
+ backupEntry = createPalmEntry( backupRec );
+ PilotAppCategory *palmEntry=0L;
+ if ( palmRec )
+ palmEntry = createPalmEntry( palmRec );
+
+ syncEntry( pcEntry, backupEntry, palmEntry );
+
+ mSyncedIds.append( palmRec->id() );
+
+ KPILOT_DELETE( pcEntry );
+ KPILOT_DELETE( palmEntry );
+ KPILOT_DELETE( backupEntry );
+ KPILOT_DELETE( palmRec );
+ KPILOT_DELETE( backupRec );
+
+ QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
+}
+
+
+
+void RecordConduit::slotPCRecToPalm()
+{
+ FUNCTIONSETUP;
+
+ if ( ( getSyncDirection()==SyncAction::eCopyHHToPC ) ||
+ mPCData->atEnd( mPCIter ) )
+ {
+ mPalmIndex = 0;
+ QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
+ return;
+ }
+
+ PilotRecord *backupRec=0L;
+ PCEntry *pcEntry = *mPCIter;
+ ++mPCIter;
+
+ // If marked as archived, don't sync!
+ if ( isArchived( pcEntry ) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": address with id " << pcEntry->uid() <<
+ " marked archived, so don't sync." << endl;
+#endif
+ KPILOT_DELETE( pcEntry );
+ QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
+ return;
+ }
+
+ recordid_t recID( pcEntry->recid() );
+ if ( recID == 0 )
+ {
+ // it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it
+ syncEntry( pcEntry, 0L, 0L );
+ KPILOT_DELETE( pcEntry );
+ QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
+ return;
+ }
+
+ // look into the list of already synced record ids to see if the PCEntry hasn't already been synced
+ if ( mSyncedIds.contains( recID ) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << ": address with id " << recID << " already synced." << endl;
+#endif
+ KPILOT_DELETE( pcEntry );
+ QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
+ return;
+ }
+
+
+ backupRec = fLocalDatabase->readRecordById( recID );
+ // only update if no backup record or the backup record is not equal to the PCEntry
+
+ PilotAppCategory*backupEntry=0L;
+ if ( backupRec )
+ backupEntry = createPalmEntry( backupRec );
+ if( !backupRec || isFirstSync() || !_equal( backupEntry, pcEntry ) )
+ {
+ PilotRecord *palmRec = fDatabase->readRecordById( recID );
+ PilotAppCategory *palmEntry=0L;
+ if (palmRec)
+ palmEntry = createPalmEntry( palmRec );
+ syncEntry( pcEntry, backupEntry, palmEntry );
+ // update the id just in case it changed
+ if ( palmRec )
+ recID = palmRec->id();
+ KPILOT_DELETE( palmRec );
+ KPILOT_DELETE( palmEntry );
+ }
+
+ KPILOT_DELETE( pcEntry );
+ KPILOT_DELETE( backupEntry );
+ KPILOT_DELETE( backupRec );
+ mSyncedIds.append( recID );
+
+ // done with the sync process, go on with the next one:
+ QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
+}
+
+
+
+void RecordConduit::slotDeletedRecord()
+{
+ FUNCTIONSETUP;
+
+ PilotRecord *backupRec = fLocalDatabase->readRecordByIndex( mPalmIndex++ );
+ if( !backupRec || isFirstSync() )
+ {
+ KPILOT_DELETE(backupRec);
+ QTimer::singleShot( 0, this, SLOT( slotDeleteUnsyncedPCRecords() ) );
+ return;
+ }
+
+ // already synced, so skip this record:
+ if ( mSyncedIds.contains( backupRec->id() ) )
+ {
+ KPILOT_DELETE( backupRec );
+ QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
+ return;
+ }
+
+ QString uid = mEntryMap[ backupRec->id() ];
+ PCEntry *pcEntry = mPCData->findByUid( uid );
+ PilotRecord *palmRec = fDatabase->readRecordById( backupRec->id() );
+ PilotAppCategory *backupEntry = 0L;
+ if (backupRec)
+ backupEntry = createPalmEntry( backupRec );
+ PilotAppCategory*palmEntry=0L;
+ if (palmRec)
+ palmEntry = createPalmEntry( palmRec );
+
+ mSyncedIds.append( backupRec->id() );
+ syncEntry( pcEntry, backupEntry, palmEntry );
+
+ KPILOT_DELETE( pcEntry );
+ KPILOT_DELETE( palmEntry );
+ KPILOT_DELETE( backupEntry );
+ KPILOT_DELETE( palmRec );
+ KPILOT_DELETE( backupRec );
+ QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
+}
+
+
+
+void RecordConduit::slotDeleteUnsyncedPCRecords()
+{
+ FUNCTIONSETUP;
+ if ( getSyncDirection() == SyncAction::eCopyHHToPC )
+ {
+ QStringList uids;
+ RecordIDList::iterator it;
+ QString uid;
+ for ( it = mSyncedIds.begin(); it != mSyncedIds.end(); ++it)
+ {
+ uid = mEntryMap[ *it ];
+ if ( !uid.isEmpty() ) uids.append( uid );
+ }
+ // TODO: Does this speed up anything?
+ // qHeapSort( uids );
+ const QStringList alluids( mPCData->uids() );
+ QStringList::ConstIterator uidit;
+ for ( uidit = alluids.constBegin(); uidit != alluids.constEnd(); ++uidit )
+ {
+ if ( !uids.contains( *uidit ) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << "Deleting PCEntry with uid " << (*uidit) << " from PC (is not on HH, and syncing with HH->PC direction)" << endl;
+#endif
+ mPCData->removeEntry( *uidit );
+ }
+ }
+ }
+ QTimer::singleShot(0, this, SLOT(slotDeleteUnsyncedHHRecords()));
+}
+
+
+
+void RecordConduit::slotDeleteUnsyncedHHRecords()
+{
+ FUNCTIONSETUP;
+ if ( getSyncDirection() == SyncAction::eCopyPCToHH )
+ {
+ RecordIDList ids = fDatabase->idList();
+ RecordIDList::iterator it;
+ for ( it = ids.begin(); it != ids.end(); ++it )
+ {
+ if ( !mSyncedIds.contains(*it) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << "Deleting record with ID " << *it << " from handheld (is not on PC, and syncing with PC->HH direction)" << endl;
+#endif
+ fDatabase->deleteRecord(*it);
+ fLocalDatabase->deleteRecord(*it);
+ }
+ }
+ }
+ QTimer::singleShot( 0, this, SLOT( slotCleanup() ) );
+}
+
+
+void RecordConduit::slotCleanup()
+{
+ FUNCTIONSETUP;
+
+ // Set the appInfoBlock, just in case the category labels changed
+ _setAppInfo();
+ doPostSync();
+ if(fDatabase)
+ {
+ fDatabase->resetSyncFlags();
+ fDatabase->cleanup();
+ }
+ if(fLocalDatabase)
+ {
+ fLocalDatabase->resetSyncFlags();
+ fLocalDatabase->cleanup();
+ }
+ KPILOT_DELETE( fDatabase );
+ KPILOT_DELETE( fLocalDatabase );
+ // TODO: do something if saving fails!
+ mPCData->saveData();
+ mPCData->cleanup();
+ emit syncDone(this);
+}
+
+
+/** Return the list of category names on the handheld
+ */
+const QStringList RecordConduit::categories() const
+{
+ QStringList cats;
+ for ( unsigned int j = 0; j < Pilot::CATEGORY_COUNT; j++ ) {
+ QString catName( category( j ) );
+ if ( !catName.isEmpty() ) cats << catName;
+ }
+ return cats;
+}
+int RecordConduit::findFlags() const
+{
+ return eqFlagsAlmostAll;
+}
+
+
+bool RecordConduit::isDeleted( const PilotAppCategory *palmEntry )
+{
+ if ( !palmEntry )
+ return true;
+ if ( palmEntry->isDeleted() && !palmEntry->isArchived() )
+ return true;
+ if ( palmEntry->isArchived() )
+ return !archiveDeleted();
+ return false;
+}
+bool RecordConduit::isArchived( const PilotAppCategory *palmEntry )
+{
+ if ( palmEntry && palmEntry->isArchived() )
+ return archiveDeleted();
+ else
+ return false;
+}
+
+
+
+
+/*********************************************************************
+ L O A D I N G T H E D A T A
+ *********************************************************************/
+
+
+
+bool RecordConduit::_prepare()
+{
+ FUNCTIONSETUP;
+
+ readConfig();
+ mSyncedIds.clear();
+ mPCData = initializePCData();
+
+ return mPCData && doPrepare();
+}
+
+
+void RecordConduit::_getAppInfo()
+{
+ FUNCTIONSETUP;
+ // get the address application header information
+ unsigned char *buffer = new unsigned char[Pilot::MAX_APPINFO_SIZE];
+ int appLen=fDatabase->readAppBlock(buffer, Pilot::MAX_APPINFO_SIZE);
+
+ doUnpackAppInfo( buffer, appLen );
+ delete[] buffer;
+ buffer = 0;
+}
+
+void RecordConduit::_setAppInfo()
+{
+ FUNCTIONSETUP;
+ // get the address application header information
+ int appLen = 0;
+ unsigned char *buffer = doPackAppInfo( &appLen );
+ if ( buffer )
+ { if (fDatabase)
+ fDatabase->writeAppBlock( buffer, appLen );
+ if (fLocalDatabase)
+ fLocalDatabase->writeAppBlock( buffer, appLen );
+ delete[] buffer;
+ }
+}
+
+
+int RecordConduit::compareStr( const QString & str1, const QString & str2 )
+{
+// FUNCTIONSETUP;
+ if ( str1.isEmpty() && str2.isEmpty() )
+ return 0;
+ else
+ return str1.compare( str2 );
+}
+
+
+/**
+ * _getCat returns the id of the category from the given categories list.
+ * If the address has no categories on the PC, QString::null is returned.
+ * If the current category exists in the list of cats, it is returned
+ * Otherwise the first cat in the list that exists on the HH is returned
+ * If none of the categories exists on the palm, QString::null is returned
+ */
+QString RecordConduit::getCatForHH( const QStringList cats, const QString curr ) const
+{
+ FUNCTIONSETUP;
+ if ( cats.size() < 1 )
+ return QString::null;
+ if ( cats.contains( curr ) )
+ return curr;
+ for ( QStringList::ConstIterator it = cats.begin(); it != cats.end(); ++it)
+ {
+ for ( unsigned int j = 0; j < Pilot::CATEGORY_COUNT; j++ )
+ {
+ QString catnm( category( j ) );
+ if ( !(*it).isEmpty() && ( (*it)==catnm ) )
+ {
+ return catnm;
+ }
+ }
+ }
+ // If we have a free label, return the first possible cat
+ QString lastCat( category( Pilot::CATEGORY_COUNT-1 ) );
+ return ( lastCat.isEmpty() ) ? ( cats.first() ) : ( QString::null );
+}
+
+void RecordConduit::setCategory(PCEntry * pcEntry, QString cat)
+{
+ if ( !cat.isEmpty() && cat!=category( 0 ) )
+ pcEntry->insertCategory(cat);
+}
+
+
+
+
+
+
+/*********************************************************************
+ G E N E R A L S Y N C F U N C T I O N
+ These functions modify the Handheld and the addressbook
+ *********************************************************************/
+
+
+
+bool RecordConduit::syncEntry( PCEntry *pcEntry, PilotAppCategory*backupEntry,
+ PilotAppCategory*palmEntry)
+{
+ FUNCTIONSETUP;
+
+ if ( getSyncDirection() == SyncAction::eCopyPCToHH )
+ {
+ if ( pcEntry->isEmpty() )
+ {
+ return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
+ }
+ else
+ {
+ return pcCopyToPalm( pcEntry, backupEntry, palmEntry );
+ }
+ }
+
+ if ( getSyncDirection() == SyncAction::eCopyHHToPC )
+ {
+ if (!palmEntry)
+ return pcDeleteEntry(pcEntry, backupEntry, palmEntry);
+ else
+ return palmCopyToPC(pcEntry, backupEntry, palmEntry);
+ }
+
+ if ( !backupEntry || isFirstSync() )
+ {
+ /*
+ Resolution matrix (0..does not exist, E..exists, D..deleted flag set, A..archived):
+ HH PC | Resolution
+ ------------------------------------------------------------
+ 0 A | -
+ 0 E | PC -> HH, reset ID if not set correctly
+ D 0 | delete (error, should never occur!!!)
+ D E | CR (ERROR)
+ E/A 0 | HH -> PC
+ E/A E/A| merge/CR
+ */
+ if ( !palmEntry && isArchived( pcEntry ) )
+ {
+ return true;
+ }
+ else if ( !palmEntry && !pcEntry->isEmpty() )
+ {
+ // PC->HH
+ bool res = pcCopyToPalm( pcEntry, 0L, 0L );
+ return res;
+ }
+ else if ( !palmEntry && pcEntry->isEmpty() )
+ {
+ // everything's empty -> ERROR
+ return false;
+ }
+ else if ( ( isDeleted( palmEntry ) || isArchived( palmEntry ) ) && pcEntry->isEmpty())
+ {
+ if ( isArchived( palmEntry ) )
+ return palmCopyToPC( pcEntry, 0L, palmEntry );
+ else
+ // this happens if you add a record on the handheld and delete it again before you do the next sync
+ return pcDeleteEntry( pcEntry, 0L, palmEntry );
+ }
+ else if ( ( isDeleted(palmEntry) || isArchived( palmEntry ) ) && !pcEntry->isEmpty() )
+ {
+ // CR (ERROR)
+ return smartMergeEntry( pcEntry, 0L, palmEntry );
+ }
+ else if ( pcEntry->isEmpty() )
+ {
+ // HH->PC
+ return palmCopyToPC( pcEntry, 0L, palmEntry );
+ }
+ else
+ {
+ // Conflict Resolution
+ return smartMergeEntry( pcEntry, 0L, palmEntry );
+ }
+ } // !backupEntry
+ else
+ {
+ /*
+ Resolution matrix:
+ 1) if HH.(empty| (deleted &! archived) ) -> { if (PC==B) -> delete, else -> CR }
+ if HH.archived -> {if (PC==B) -> copyToPC, else -> CR }
+ if PC.empty -> { if (HH==B) -> delete, else -> CR }
+ if PC.archived -> {if (HH==B) -> delete on HH, else CR }
+ 2) if PC==HH -> { update B, update ID of PC if needed }
+ 3) if PC==B -> { HH!=PC, thus HH modified, so copy HH->PC }
+ if HH==B -> { PC!=HH, thus PC modified, so copy PC->HH }
+ 4) else: all three PCEntrys are different -> CR
+ */
+
+ if ( !palmEntry || isDeleted(palmEntry) )
+ {
+ if ( _equal( backupEntry, pcEntry ) || pcEntry->isEmpty() )
+ {
+ return pcDeleteEntry( pcEntry, backupEntry, 0L );
+ }
+ else
+ {
+ return smartMergeEntry( pcEntry, backupEntry, 0L );
+ }
+ }
+ else if ( pcEntry->isEmpty() )
+ {
+ if (*palmEntry == *backupEntry)
+ {
+ return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
+ }
+ else
+ {
+ return smartMergeEntry( pcEntry, backupEntry, palmEntry );
+ }
+ }
+ else if ( _equal( palmEntry, pcEntry ) )
+ {
+ // update Backup, update ID of PC if neededd
+ return backupSaveEntry( palmEntry );
+ }
+ else if ( _equal( backupEntry, pcEntry ) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << "Flags: " << palmEntry->getAttrib() << ", isDeleted=" <<
+ isDeleted( palmEntry ) << ", isArchived=" << isArchived( palmEntry )
+ << endl;
+#endif
+ if ( isDeleted( palmEntry ) )
+ {
+ return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
+ }
+ else
+ {
+ return palmCopyToPC( pcEntry, backupEntry, palmEntry );
+ }
+ }
+ else if ( *palmEntry == *backupEntry )
+ {
+ return pcCopyToPalm( pcEntry, backupEntry, palmEntry );
+ }
+ else
+ {
+ // CR, since all are different
+ return smartMergeEntry( pcEntry, backupEntry, palmEntry );
+ }
+ } // backupEntry
+ return false;
+}
+
+bool RecordConduit::pcCopyToPalm( PCEntry *pcEntry, PilotAppCategory *backupEntry,
+ PilotAppCategory*palmEntry )
+{
+ FUNCTIONSETUP;
+
+ if ( pcEntry->isEmpty() ) return false;
+ PilotAppCategory *hhEntry = palmEntry;
+ bool hhEntryCreated = false;
+ if ( !hhEntry )
+ {
+ hhEntry = createPalmEntry( 0 );
+ hhEntryCreated=true;
+ }
+ _copy( hhEntry, pcEntry );
+#ifdef DEBUG
+ DEBUGKPILOT << "palmEntry->id=" << hhEntry->id() << ", pcEntry.ID=" <<
+ pcEntry->uid() << endl;
+#endif
+
+ if( palmSaveEntry( hhEntry, pcEntry ) )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << "Entry palmEntry->id=" <<
+ hhEntry->id() << "saved to palm, now updating pcEntry->uid()=" << pcEntry->uid() << endl;
+#endif
+ pcSaveEntry( pcEntry, backupEntry, hhEntry );
+ }
+ if ( hhEntryCreated ) KPILOT_DELETE( hhEntry );
+ return true;
+}
+
+
+
+
+bool RecordConduit::palmCopyToPC( PCEntry *pcEntry, PilotAppCategory *backupEntry,
+ PilotAppCategory *palmEntry )
+{
+ FUNCTIONSETUP;
+ if ( !palmEntry )
+ {
+ return false;
+ }
+ _copy( pcEntry, palmEntry );
+ pcSaveEntry( pcEntry, backupEntry, palmEntry );
+ backupSaveEntry( palmEntry );
+ return true;
+}
+
+
+
+/*********************************************************************
+ l o w - l e v e l f u n c t i o n s f o r
+ adding / removing palm/pc records
+ *********************************************************************/
+
+
+
+bool RecordConduit::palmSaveEntry( PilotAppCategory *palmEntry, PCEntry *pcEntry )
+{
+ FUNCTIONSETUP;
+
+#ifdef DEBUG
+ DEBUGKPILOT << "Saving to pilot " << palmEntry->id() << endl;
+#endif
+
+ PilotRecord *pilotRec = palmEntry->pack();
+ recordid_t pilotId = fDatabase->writeRecord(pilotRec);
+#ifdef DEBUG
+ DEBUGKPILOT << "PilotRec nach writeRecord (" << pilotId <<
+ ": ID=" << pilotRec->id() << endl;
+#endif
+ fLocalDatabase->writeRecord( pilotRec );
+ KPILOT_DELETE( pilotRec );
+
+ // pilotId == 0 if using local db, so don't overwrite the valid id
+ if ( pilotId != 0 )
+ {
+ palmEntry->setID( pilotId );
+ if ( !mSyncedIds.contains( pilotId ) )
+ {
+ mSyncedIds.append( pilotId );
+ }
+ }
+
+ recordid_t hhId( pcEntry->recid() );
+ if ( hhId != pilotId )
+ {
+ pcEntry->setRecid( pilotId );
+ return true;
+ }
+
+ return false;
+}
+
+
+
+bool RecordConduit::backupSaveEntry( PilotAppCategory *backup )
+{
+ FUNCTIONSETUP;
+ if ( !backup ) return false;
+
+
+#ifdef DEBUG
+// showPilotAppCategory( backup );
+#endif
+ PilotRecord *pilotRec = backup->pack();
+ fLocalDatabase->writeRecord( pilotRec );
+ KPILOT_DELETE( pilotRec );
+ return true;
+}
+
+
+
+bool RecordConduit::pcSaveEntry( PCEntry *pcEntry, PilotAppCategory *,
+ PilotAppCategory * )
+{
+ FUNCTIONSETUP;
+
+#ifdef DEBUG
+ DEBUGKPILOT << "Before _savepcEntry, pcEntry->uid()=" <<
+ pcEntry->uid() << endl;
+#endif
+ if ( pcEntry->recid() != 0 )
+ {
+ mEntryMap.insert( pcEntry->recid(), pcEntry->uid() );
+ }
+
+ mPCData->updateEntry( pcEntry );
+ return true;
+}
+
+
+
+bool RecordConduit::pcDeleteEntry( PCEntry *pcEntry, PilotAppCategory *backupEntry,
+ PilotAppCategory *palmEntry )
+{
+ FUNCTIONSETUP;
+
+ if ( palmEntry )
+ {
+ if ( !mSyncedIds.contains( palmEntry->id() ) )
+ {
+ mSyncedIds.append(palmEntry->id());
+ }
+ palmEntry->makeDeleted();
+ PilotRecord *pilotRec = palmEntry->pack();
+ pilotRec->setDeleted();
+ mPalmIndex--;
+ fDatabase->writeRecord( pilotRec );
+ fLocalDatabase->writeRecord( pilotRec );
+ mSyncedIds.append( pilotRec->id() );
+ KPILOT_DELETE( pilotRec );
+ }
+ else if ( backupEntry )
+ {
+ if ( !mSyncedIds.contains( backupEntry->id() ) )
+ {
+ mSyncedIds.append( backupEntry->id() );
+ }
+ backupEntry->makeDeleted();
+ PilotRecord *pilotRec = backupEntry->pack();
+ pilotRec->setDeleted();
+ mPalmIndex--;
+ fLocalDatabase->writeRecord( pilotRec );
+ mSyncedIds.append( pilotRec->id() );
+ KPILOT_DELETE( pilotRec );
+ }
+ if ( !pcEntry->isEmpty() )
+ {
+#ifdef DEBUG
+ DEBUGKPILOT << fname << " removing " << pcEntry->uid() << endl;
+#endif
+ mPCData->removeEntry( pcEntry );
+ }
+ return true;
+}
+
+
+
+/*********************************************************************
+ C O P Y R E C O R D S
+ *********************************************************************/
+
+
+
+
+
+/*********************************************************************
+ C O N F L I C T R E S O L U T I O N a n d M E R G I N G
+ *********************************************************************/
+
+
+
+
+// TODO: right now entries are equal if both first/last name and organization are
+// equal. This rules out two entries for the same person(e.g. real home and weekend home)
+// or two persons with the same name where you don't know the organization.!!!
+RecordConduit::PCEntry *RecordConduit::findMatch( PilotAppCategory *palmEntry ) const
+{
+ FUNCTIONSETUP;
+ if ( !palmEntry )
+ return 0;
+
+ // TODO: also search with the pilotID
+ // first, use the pilotID to UID map to find the appropriate record
+ if( !isFirstSync() && ( palmEntry->id() > 0) )
+ {
+ QString id( mEntryMap[palmEntry->id()] );
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": PilotRecord has id " << palmEntry->id() << ", mapped to " << id << endl;
+#endif
+ if( !id.isEmpty() )
+ {
+ PCEntry *res = mPCData->findByUid( id );
+ if ( !res && !res->isEmpty() ) return res;
+ KPILOT_DELETE( res );
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": PilotRecord has id " << palmEntry->id() <<
+ ", but could not be found on the PC side" << endl;
+#endif
+ }
+ }
+
+ for ( PCData::Iterator iter = mPCData->begin(); !mPCData->atEnd( iter ); ++iter )
+ {
+ PCEntry *abEntry = *iter;
+ recordid_t rid( abEntry->recid() );
+ if ( rid>0 )
+ {
+ if ( rid == palmEntry->id() )
+ return abEntry;// yes, we found it
+ // skip this PCEntry, as it has a different corresponding address on the handheld
+ //if ( mAllIds.contains( rid ) ) continue;
+ }
+
+ if ( _equal( palmEntry, abEntry, eqFlagsAlmostAll ) )
+ {
+ return abEntry;
+ }
+ KPILOT_DELETE( abEntry );
+ }
+#ifdef DEBUG
+ DEBUGKPILOT << fname << ": Could not find any entry matching Palm record with id " << QString::number( palmEntry->id() ) << endl;
+#endif
+ return 0;
+}
+
+#endif
+
+
+
+
+#include "recordConduit.moc"
+
diff --git a/kpilot/lib/recordConduit.h b/kpilot/lib/recordConduit.h
new file mode 100644
index 000000000..d12ceef2e
--- /dev/null
+++ b/kpilot/lib/recordConduit.h
@@ -0,0 +1,181 @@
+#ifndef _KPILOT_RECORDCONDUIT_H
+#define _KPILOT_RECORDCONDUIT_H
+/* record-conduit.h KPilot
+**
+** Copyright (C) 2005 by Adriaan de Groot
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <qtimer.h>
+#include <klocale.h>
+
+#include "plugin.h"
+#include "pilot.h"
+#include "pilotDatabase.h"
+#include "kpilotdevicelink.h"
+
+class KPilotDeviceLink;
+
+/** @file
+*
+* This file defines a generic syncing framework for Palm Pilot oriented data.
+* It is a lot like KitchenSync's Syncees and such. Basically, we define a
+* generic container for data on the PC side and give that container an API
+* for searching for a specific handheld record. Syncing consists of iterating
+* through the handheld's records and looking up the PC data for each record,
+* and then syncing.
+*
+*/
+
+/** An intermediate class that introduces the slots we need for our sync
+* implementation. This is here _only_ because mixing moc with template
+* classes sounds really scary.
+*/
+
+class RecordConduitBase : public ConduitAction
+{
+Q_OBJECT
+public:
+ /** Constructor. The QStringList @p a sets flags for the ConduitAction.
+ */
+ RecordConduitBase(KPilotDeviceLink *o,
+ const char *n,
+ const QStringList a = QStringList()) :
+ ConduitAction(o,n,a),
+ fTimer(0L)
+ {
+ } ;
+ /** Destructor. */
+ virtual ~RecordConduitBase()
+ {
+ // delete fTimer; // Timer is a child object
+ } ;
+
+ /** Return values for the processing functions. Each should return
+ * NotDone if it needs to be called again (e.g. to process another record),
+ * Done if it is finished and something else should be done, and
+ * Error if the sync cannot be completed.
+ */
+ enum SyncProgress { NotDone=0, Done=1, Error=2 } ;
+
+ /** Returns a human-readable name for the progress indicator @p s */
+ static QString name(SyncProgress s);
+
+ /** State of the conduit's sync. This is changed by process(). */
+ enum States { Initialize, PalmToPC, PCToPalm, Cleanup } ;
+
+ static QString name(States s);
+
+protected:
+ /** Function called at the beginning of a sync to load data from the PC.
+ * @return Done when the load has finished.
+ * @see process
+ */
+ virtual SyncProgress loadPC() = 0;
+
+ /** Function called repeatedly to fetch the next modified entry from the Palm and
+ * sync it with the PC by looking up the record, and calling the syncer for it.
+ *
+ * @return Dome when there are no more modified records on the Palm
+ * @see process()
+ */
+ virtual SyncProgress palmRecToPC() = 0;
+
+ /** Function called repeatedly to fetch the next modified entry from the PC and
+ * sync it with the Palm by looking up the record and calling the syncer for it.
+ *
+ * @return Done when there are no more modified records on the PC
+ * @see process()
+ */
+ virtual SyncProgress pcRecToPalm() = 0;
+
+ /** Function called at the end of this conduit's sync, which should reset DB flags
+ * and write changed config data out to disk.
+ *
+ * @return Done when the cleanup is complete.
+ * @see process()
+ */
+ virtual SyncProgress cleanup() = 0;
+
+protected slots:
+ /** Slot used for the implementation of a state machine: calls each of the
+ * relevant other slots (above) as needed until they return true.
+ */
+ void process();
+
+protected:
+ virtual bool exec();
+
+private:
+ /** Timer to signal the process() slot. Used to keep the UI responsive. */
+ QTimer *fTimer;
+
+ States fState;
+
+ Pilot::RecordIDList fIDList;
+ Pilot::RecordIDList::Iterator fIDListIterator;
+
+ QString fDBName;
+} ;
+
+template <class PCEntry, class PCContainer, class HHEntry, class HHAppInfo, class Syncer>
+class RecordConduit : public RecordConduitBase
+{
+public:
+ /** Construct a record conduit on a given device link. */
+ RecordConduit(
+ KPilotDeviceLink *o /**< Connection to HH */,
+ const char *n /**< Name for QObject */,
+ const QStringList a = QStringList() /**< Flags */) :
+ RecordConduitBase(o,n,a)
+ {
+ } ;
+ virtual ~RecordConduit()
+ {
+ } ;
+
+ virtual SyncProgress loadPC()
+ {
+ return Done;
+ } ;
+
+ virtual SyncProgress palmRecToPC()
+ {
+ return Done;
+ }
+
+ virtual SyncProgress pcRecToPalm()
+ {
+ return Done;
+ }
+
+ virtual SyncProgress cleanup()
+ {
+ return Done;
+ }
+} ;
+
+
+#endif
+
diff --git a/kpilot/lib/syncAction.cc b/kpilot/lib/syncAction.cc
new file mode 100644
index 000000000..818503807
--- /dev/null
+++ b/kpilot/lib/syncAction.cc
@@ -0,0 +1,512 @@
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2001 by Waldo Bastian (code in questionYesNo)
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include "options.h"
+
+#include <time.h>
+
+#include <pi-socket.h>
+#include <pi-dlp.h>
+
+#include <qtimer.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qmessagebox.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qtl.h>
+#include <qstyle.h>
+
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+
+#include "syncAction.moc"
+#include "kpilotlibSettings.h"
+
+SyncAction::SyncAction(KPilotLink *p,
+ const char *name) :
+ QObject(p, name),
+ fHandle(p),
+ fParent(0L)
+{
+ FUNCTIONSETUP;
+}
+
+SyncAction::SyncAction(KPilotLink *p,
+ QWidget * visibleparent,
+ const char *name) :
+ QObject(p, name),
+ fHandle(p),
+ fParent(visibleparent)
+{
+ FUNCTIONSETUP;
+}
+
+SyncAction::~SyncAction()
+{
+}
+
+/* virtual */ QString SyncAction::statusString() const
+{
+ FUNCTIONSETUP;
+ QString s = CSL1("status=");
+
+ s.append(QString::number(status()));
+ return s;
+}
+
+/* slot */ void SyncAction::execConduit()
+{
+ FUNCTIONSETUP;
+
+ DEBUGKPILOT << fname << ": Exec " << name() << endl;
+
+ bool r = this->exec();
+
+ DEBUGKPILOT << fname << ": Exec " << name()
+ << (r ? " is running" : " failed to start") << endl;
+
+ if (!r)
+ {
+ emit logError(i18n("The conduit %1 could not be executed.")
+ .arg(QString::fromLatin1(name())));
+ delayDone();
+ }
+}
+
+/* slot */ void SyncAction::delayedDoneSlot()
+{
+ emit syncDone(this);
+}
+
+bool SyncAction::delayDone()
+{
+ QTimer::singleShot(0,this,SLOT(delayedDoneSlot()));
+ return true;
+}
+
+static struct
+{
+ SyncAction::SyncMode::Mode mode;
+ const char *name;
+} maps[] =
+{
+ { SyncAction::SyncMode::eHotSync, "--hotsync" },
+ { SyncAction::SyncMode::eFullSync, "--full" },
+ { SyncAction::SyncMode::eCopyPCToHH, "--copyPCToHH" },
+ { SyncAction::SyncMode::eCopyHHToPC, "--copyHHToPC" },
+ { SyncAction::SyncMode::eBackup, "--backup" },
+ { SyncAction::SyncMode::eRestore, "--restore" },
+ { SyncAction::SyncMode::eFullSync, "--fullsync" },
+ { SyncAction::SyncMode::eHotSync, (const char *)0 }
+}
+;
+
+SyncAction::SyncMode::SyncMode(const QStringList &args) :
+ fMode(eHotSync),
+ fTest(args.contains("--test")),
+ fLocal(args.contains("--local"))
+{
+ int i = 0;
+ while(maps[i].name)
+ {
+ if (args.contains(QString::fromLatin1(maps[i].name)))
+ {
+ fMode = maps[i].mode;
+ break;
+ }
+ i++;
+ }
+
+ if (!maps[i].name)
+ {
+ WARNINGKPILOT << "No mode set by arguments ("
+ << args.join(",") << ") defaulting to HotSync." << endl;
+ }
+}
+
+SyncAction::SyncMode::SyncMode(Mode m, bool test, bool local) :
+ fMode(m),
+ fTest(test),
+ fLocal(local)
+{
+ if ( ((int)m<(int)eHotSync) || ((int)m>(int)eRestore) )
+ {
+ WARNINGKPILOT << "Mode value " << (int)m << " is illegal"
+ ", defaulting to HotSync." << endl;
+ fMode = eHotSync;
+ }
+}
+
+QStringList SyncAction::SyncMode::list() const
+{
+ FUNCTIONSETUPL(3);
+
+ QStringList l;
+ int i=0;
+
+ while(maps[i].name)
+ {
+ if ( fMode == maps[i].mode )
+ {
+ l.append(QString::fromLatin1(maps[i].name));
+ break;
+ }
+ i++;
+ }
+ if ( !maps[i].name )
+ {
+ WARNINGKPILOT << "Mode " << fMode << " does not have a name." << endl;
+ l.append(QString::fromLatin1(maps[0].name));
+ }
+
+ if (isTest()) l.append(CSL1("--test"));
+ if (isLocal()) l.append(CSL1("--local"));
+ return l;
+}
+
+/* static */ QString SyncAction::SyncMode::name(SyncAction::SyncMode::Mode e)
+{
+ switch(e)
+ {
+ case eHotSync : return i18n("HotSync");
+ case eFullSync : return i18n("Full Synchronization");
+ case eCopyPCToHH : return i18n("Copy PC to Handheld");
+ case eCopyHHToPC : return i18n("Copy Handheld to PC");
+ case eBackup : return i18n("Backup");
+ case eRestore : return i18n("Restore From Backup");
+ }
+ return CSL1("<unknown>");
+}
+
+QString SyncAction::SyncMode::name() const
+{
+ QString s = name(fMode);
+ if (isTest())
+ {
+
+ s.append(CSL1(" [%1]").arg(i18n("Test Sync")));
+ }
+ if (isLocal())
+ {
+ s.append(CSL1(" [%1]").arg(i18n("Local Sync")));
+ }
+ return s;
+}
+
+bool SyncAction::SyncMode::setMode(int mode)
+{
+ // Resets test and local flags too
+ fTest = fLocal = false;
+
+ if ( (mode>0) && (mode<=eRestore) )
+ {
+ fMode = (SyncAction::SyncMode::Mode) mode;
+ return true;
+ }
+ else
+ {
+ WARNINGKPILOT << "Bad sync mode " << mode << " requested." << endl ;
+ fMode = eHotSync;
+ return false;
+ }
+}
+
+bool SyncAction::SyncMode::setMode(SyncAction::SyncMode::Mode m)
+{
+ int i=0;
+ while ( maps[i].name )
+ {
+ if ( maps[i].mode == m )
+ {
+ fMode = m;
+ return true;
+ }
+ i++;
+ }
+
+ WARNINGKPILOT << "Bad sync mode " << m << " requested." << endl ;
+ fMode = eHotSync;
+ return false;
+}
+
+void SyncAction::startTickle(unsigned timeout)
+{
+ FUNCTIONSETUP;
+
+ if (!deviceLink())
+ {
+ WARNINGKPILOT << "Trying to tickle without a device." << endl;
+ }
+ else
+ {
+ connect(deviceLink(),SIGNAL(timeout()),this,SIGNAL(timeout()));
+ deviceLink()->startTickle(timeout);
+ }
+}
+
+void SyncAction::stopTickle()
+{
+ FUNCTIONSETUP;
+ if (!deviceLink())
+ {
+ WARNINGKPILOT << "Trying to tickle without a device." << endl;
+ }
+ else
+ {
+ disconnect(deviceLink(),SIGNAL(timeout()),this,SIGNAL(timeout()));
+ deviceLink()->stopTickle();
+ }
+}
+
+
+int SyncAction::questionYesNo(const QString & text,
+ const QString & caption,
+ const QString & key,
+ unsigned timeout,
+ const QString & yes,
+ const QString &no )
+{
+ FUNCTIONSETUP;
+
+ bool checkboxReturn = false;
+ int r;
+ KMessageBox::ButtonCode result;
+ if (!key.isEmpty())
+ {
+ if (!KMessageBox::shouldBeShownYesNo(key,result))
+ {
+ return result;
+ }
+ }
+
+ KDialogBase *dialog =
+ new KDialogBase(caption.isNull()? i18n("Question") : caption,
+ KDialogBase::Yes | KDialogBase::No,
+ KDialogBase::Yes, KDialogBase::No,
+ fParent, "questionYesNo", true, true,
+ yes.isEmpty() ? KStdGuiItem::yes() : yes,
+ no.isEmpty() ? KStdGuiItem::no() : no);
+
+ if ( (timeout > 0) && ( deviceLink() ) )
+ {
+ QObject::connect(deviceLink(), SIGNAL(timeout()),
+ dialog, SLOT(slotCancel()));
+ startTickle(timeout);
+ }
+
+#if KDE_IS_VERSION(3,3,0)
+ r = (KMessageBox::ButtonCode) KMessageBox::createKMessageBox(dialog,
+ QMessageBox::Question,
+ text,
+ QStringList(),
+ (key.isEmpty() ? QString::null : i18n("&Do not ask again")),
+ &checkboxReturn,
+ 0);
+
+#else
+ // The following code is taken from KDialogBase.cc,
+ // part of the KDE 2.2 libraries. Copyright 2001
+ // by Waldo Bastian.
+ //
+ //
+ QVBox *topcontents = new QVBox(dialog);
+
+ topcontents->setSpacing(KDialog::spacingHint() * 2);
+ topcontents->setMargin(KDialog::marginHint() * 2);
+
+ QWidget *contents = new QWidget(topcontents);
+ QHBoxLayout *lay = new QHBoxLayout(contents);
+
+ lay->setSpacing(KDialog::spacingHint() * 2);
+
+ lay->addStretch(1);
+ QLabel *label1 = new QLabel( contents);
+ label1->setPixmap(QMessageBox::standardIcon(QMessageBox::Information));
+ lay->add( label1 );
+ QLabel *label2 = new QLabel( text, contents);
+ label2->setMinimumSize(label2->sizeHint());
+ lay->add(label2);
+ lay->addStretch(1);
+
+ QSize extraSize = QSize(50, 30);
+
+ QCheckBox *checkbox = 0L;
+ if (!key.isEmpty())
+ {
+ checkbox = new QCheckBox(i18n("Do not ask again"),topcontents);
+ extraSize = QSize(50,0);
+ }
+
+ dialog->setMainWidget(topcontents);
+ dialog->enableButtonSeparator(false);
+ dialog->incInitialSize(extraSize);
+
+ r = dialog->exec();
+ if (checkbox)
+ {
+ checkboxReturn = checkbox->isChecked();
+ }
+#endif
+
+ switch(r)
+ {
+ case KDialogBase::Yes : result=KMessageBox::Yes ; break;
+ case KDialogBase::No : result=KMessageBox::No; break;
+ case KDialogBase::Cancel : result=KMessageBox::Cancel; break;
+ default : break;
+ }
+
+ stopTickle();
+
+ if (!key.isEmpty() && checkboxReturn)
+ {
+ KMessageBox::saveDontShowAgainYesNo(key,result);
+ }
+
+ return result;
+}
+
+
+int SyncAction::questionYesNoCancel(const QString & text,
+ const QString & caption,
+ const QString & key,
+ unsigned timeout,
+ const QString &yes,
+ const QString &no)
+{
+ FUNCTIONSETUP;
+
+ bool checkboxReturn = false;
+ int r;
+ KMessageBox::ButtonCode result;
+
+ if (!key.isEmpty())
+ {
+ if (!KMessageBox::shouldBeShownYesNo(key,result))
+ {
+ if (result != KMessageBox::Cancel)
+ {
+ return result;
+ }
+ }
+ }
+
+ KDialogBase *dialog =
+ new KDialogBase(caption.isNull()? i18n("Question") : caption,
+ KDialogBase::Yes | KDialogBase::No | KDialogBase::Cancel,
+ KDialogBase::Yes, KDialogBase::Cancel,
+ fParent, "questionYesNoCancel", true, true,
+ (yes.isEmpty() ? KStdGuiItem::yes() : yes),
+ (no.isEmpty() ? KStdGuiItem::no() : no),
+ KStdGuiItem::cancel());
+
+ if ( (timeout > 0) && (deviceLink()) )
+ {
+ QObject::connect(deviceLink(), SIGNAL(timeout()),
+ dialog, SLOT(slotCancel()));
+ startTickle(timeout);
+ }
+
+#if KDE_IS_VERSION(3,3,0)
+ r = KMessageBox::createKMessageBox(dialog,
+ QMessageBox::Question,
+ text,
+ QStringList(),
+ (key.isEmpty() ? QString::null : i18n("&Do not ask again")),
+ &checkboxReturn,
+ 0);
+#else
+ // The following code is taken from KDialogBase.cc,
+ // part of the KDE 2.2 libraries. Copyright 2001
+ // by Waldo Bastian.
+ //
+ //
+ QVBox *topcontents = new QVBox(dialog);
+
+ topcontents->setSpacing(KDialog::spacingHint() * 2);
+ topcontents->setMargin(KDialog::marginHint() * 2);
+
+ QWidget *contents = new QWidget(topcontents);
+ QHBoxLayout *lay = new QHBoxLayout(contents);
+
+ lay->setSpacing(KDialog::spacingHint() * 2);
+
+ lay->addStretch(1);
+ QLabel *label1 = new QLabel( contents);
+ label1->setPixmap(QMessageBox::standardIcon(QMessageBox::Information));
+ lay->add( label1 );
+ QLabel *label2 = new QLabel( text, contents);
+ label2->setMinimumSize(label2->sizeHint());
+ lay->add(label2);
+ lay->addStretch(1);
+
+ QSize extraSize = QSize(50, 30);
+
+ QCheckBox *checkbox = 0L;
+ if (!key.isEmpty())
+ {
+ checkbox = new QCheckBox(i18n("Do not ask again"),topcontents);
+ extraSize = QSize(50,0);
+ }
+
+ dialog->setMainWidget(topcontents);
+ dialog->enableButtonSeparator(false);
+ dialog->incInitialSize(extraSize);
+
+ r = dialog->exec();
+ if (checkbox)
+ {
+ checkboxReturn = checkbox->isChecked();
+ }
+#endif
+
+ switch(r)
+ {
+ case KDialogBase::Yes : result=KMessageBox::Yes ; break;
+ case KDialogBase::No : result=KMessageBox::No; break;
+ case KDialogBase::Cancel : result=KMessageBox::Cancel; break;
+ default : break;
+ }
+ stopTickle();
+
+ if (!key.isEmpty() && checkboxReturn)
+ {
+ KMessageBox::saveDontShowAgainYesNo(key,result);
+ }
+
+ return result;
+}
+
diff --git a/kpilot/lib/syncAction.h b/kpilot/lib/syncAction.h
new file mode 100644
index 000000000..a93bff99c
--- /dev/null
+++ b/kpilot/lib/syncAction.h
@@ -0,0 +1,410 @@
+#ifndef _KPILOT_SYNCACTION_H
+#define _KPILOT_SYNCACTION_H
+/* KPilot
+**
+** Copyright (C) 1998-2001 by Dan Pilone
+** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
+**
+*/
+
+/*
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program in a file called COPYING; if not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+** MA 02110-1301, USA.
+*/
+
+/*
+** Bug reports and questions can be sent to kde-pim@kde.org
+*/
+
+#include <time.h>
+
+#include <pi-dlp.h>
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "kpilotlink.h"
+
+/** @file
+* SyncAction
+*/
+
+class QTimer;
+class KPilotUser;
+class SyncAction;
+
+class KDE_EXPORT SyncAction : public QObject
+{
+Q_OBJECT
+
+public:
+ SyncAction(KPilotLink *p,
+ const char *name=0L);
+ SyncAction(KPilotLink *p,
+ QWidget *visibleparent,
+ const char *name=0L);
+ ~SyncAction();
+
+ typedef enum { Error=-1 } Status;
+
+ /** A syncaction has a status, which can be expressed as an
+ * integer. Subclasses are expected to define their own status
+ * values as needed.
+ */
+ int status() const
+ {
+ return fActionStatus;
+ }
+ /** Return a human-readable representation of the status. */
+ virtual QString statusString() const;
+
+protected:
+ /**
+ * This function starts the actual processing done
+ * by the conduit. It should return false if the
+ * processing cannot be initiated, f.ex. because
+ * some parameters were not set or a needed library
+ * is missing. This will be reported to the user.
+ * It should return true if processing is started
+ * normally. If processing starts normally, it is
+ * the _conduit's_ responsibility to eventually
+ * emit syncDone(); if processing does not start
+ * normally (ie. exec() returns false) then the
+ * environment will deal with syncDone().
+ */
+ virtual bool exec() = 0;
+
+public slots:
+ /**
+ * This just calls exec() and deals with the
+ * return code.
+ */
+ void execConduit();
+
+signals:
+ void syncDone(SyncAction *);
+ void logMessage(const QString &);
+ void logError(const QString &);
+ void logProgress(const QString &,int);
+
+protected slots:
+ /** This slot emits syncDone(), and does nothing else. This
+ * is safe, since the method returns immediately after the
+ * emit -- even if syncDone() causes the SyncAction to be deleted.
+ */
+ void delayedDoneSlot();
+
+protected:
+ /**
+ * It might not be safe to emit syncDone() from exec().
+ * So instead, call delayDone() to wait for the main event
+ * loop to return if you manage to do all processing
+ * immediately.
+ *
+ * delayDone() returns true, so that return delayDone();
+ * is a sensible final statement in exec().
+ */
+ bool delayDone();
+
+public:
+ /** Public API for adding a sync log entry, see the implementation
+ * in KPilotLink::addSyncLogEntry().
+ * @param e Message to add to the sync log
+ * @param log If @c true, also add the entry to the log in KPilot
+ * @note Having messages appear on the handheld but not in KPilot
+ * should be a @em very rare occurrence.
+ */
+ void addSyncLogEntry(const QString &e,bool log=true)
+ {
+ if (deviceLink())
+ {
+ deviceLink()->addSyncLogEntry(e,log);
+ }
+ }
+ /** Public API for adding a message to the log in KPilot.
+ * Adds @p msg to the synclog maintained on the PC.
+ */
+ void addLogMessage( const QString &msg )
+ {
+ emit logMessage( msg );
+ }
+ /** Log an error message in KPilot (the PC side of things). */
+ void addLogError( const QString &msg )
+ {
+ emit logError( msg );
+ }
+ /** Log progress in KPilot (the PC side of things). */
+ void addLogProgress( const QString &msg, int prog )
+ {
+ emit logProgress( msg, prog );
+ }
+protected:
+ /** Connection to the device. @todo make private. */
+ KPilotLink *fHandle;
+ int fActionStatus;
+
+ /** Returns a pointer to the connection to the device. */
+ inline KPilotLink *deviceLink() const
+ {
+ return fHandle;
+ }
+
+ /** Returns the file descriptor for the device link -- that is,
+ * the raw handle to the OS's connection to the device. Use with care.
+ * May return -1 if there is no device.
+ */
+ int pilotSocket() const
+ {
+ return deviceLink() ? deviceLink()->pilotSocket() : -1 ;
+ }
+
+ /** Tells the handheld device that someone is talking to it now.
+ * Useful (repeatedly) to inform the user of what is going on.
+ * May return < 0 on error (or if there is no device attached).
+ */
+ int openConduit()
+ {
+ return deviceLink() ? deviceLink()->openConduit() : -1;
+ }
+public:
+ /**
+ * This class encapsulates the different sync modes that
+ * can be used, and enforces a little discipline in changing
+ * the mode and messing around in general. It replaces a
+ * simple enum by not much more, but it makes things like
+ * local test backups less likely to happen.
+ *
+ * Note that this could all be packed into a bitfield (5 bits needed)
+ * but that makes for messy code in the end.
+ */
+ class SyncMode
+ {
+ public:
+ /** Available modes for the sync. */
+ enum Mode {
+ eHotSync=1,
+ eFullSync=2,
+ eCopyPCToHH=3,
+ eCopyHHToPC=4,
+ eBackup=5,
+ eRestore=6
+ } ;
+
+ /** Create a mode with the given Mode @p m and
+ * the mix-ins @p test and @p local, which
+ * determine whether the sync should actually change
+ * anything at all (test mode) and whether the HH is
+ * to be simulated by local databases.
+ */
+ SyncMode(Mode m, bool test=false, bool local=false);
+
+ /** Create a mode by parsing the string list. This
+ * is used mostly by the conduit proxies, which use
+ * a string list to pass aparameters to the shared
+ * library loader.
+ */
+ SyncMode(const QStringList &l);
+
+ /** Returns the kind of sync; this is just incomplete
+ * information, since a test hot sync is very different from
+ * a non-test one. */
+ Mode mode() const
+ {
+ return fMode;
+ }
+
+ /** Sets a mode from an integer @p mode, if possible.
+ * If the @p mode is illegal, return false and set the
+ * mode to Hot Sync. As a side effect, options test and local
+ * are reset to false.
+ */
+ bool setMode(int);
+
+ /** Sets a mode from a @p mode, if possible. This leaves
+ * the options unchanged, so as to reward properly-typed programming.
+ */
+ bool setMode(Mode m);
+
+ /** Sets options. Returns false if the combination of mode
+ * and the options is impossible. */
+ bool setOptions(bool test, bool local)
+ {
+ fTest=test;
+ fLocal=local;
+ return true;
+ }
+
+ /** Shorthand to test for a specific mode enum. This disregards
+ * the mixings local and test.
+ */
+ bool operator ==(const Mode &m) const
+ {
+ return mode() == m;
+ }
+ /** Longhand comparison. Compares two modes for the same
+ * mode enum and mixins local and test.
+ */
+ bool operator ==(const SyncMode &m) const
+ {
+ return ( mode() == m.mode() ) &&
+ ( isTest() == m.isTest() ) &&
+ ( isLocal() == m.isLocal() );
+ } ;
+
+ /** Accessor for the test part of the mode. Test syncs should
+ * never actually modify data anywhere.
+ */
+ bool isTest() const
+ {
+ return fTest;
+ }
+
+ /** Accessor for the local part of the mode. Local syncs use a
+ * local database instead of one on the device link.
+ */
+ bool isLocal() const
+ {
+ return fLocal;
+ }
+
+ bool isFullSync() const
+ {
+ return ( fMode==eFullSync ) ||
+ ( fMode==eCopyPCToHH) ||
+ ( fMode==eCopyHHToPC) ;
+ } ;
+ bool isFirstSync() const
+ {
+ return ( fMode==eCopyHHToPC ) || ( fMode==eCopyPCToHH ) ;
+ };
+
+ /** Classify every mode as either a sync (two-way) or copy (one-way) mode. */
+ bool isSync() const
+ {
+ return ( fMode==eFullSync ) ||
+ ( fMode == eHotSync );
+ } ;
+
+ /** Classify every mode as either a sync (two-way) or copy (one-way) mode. */
+ bool isCopy() const
+ {
+ return ( fMode==eBackup ) ||
+ ( fMode==eRestore ) ||
+ ( fMode==eCopyPCToHH ) ||
+ ( fMode==eCopyHHToPC );
+ } ;
+
+ /**
+ * Returns a standard name for each of the sync modes.
+ */
+ static QString name(Mode);
+
+ /**
+ * Returns a (human readable) name for this particular mode,
+ * including extra information about test and local mode.
+ */
+ QString name() const;
+
+ /**
+ * Returns a QStringList that, when passed to the constructor
+ * of SyncMode, will re-create it. Used to pass modes into
+ * shared library factories.
+ */
+ QStringList list() const;
+
+ private:
+ Mode fMode;
+ bool fTest;
+ bool fLocal;
+ };
+
+
+ enum ConflictResolution
+ {
+ eUseGlobalSetting=-1,
+ eAskUser=0,
+ eDoNothing,
+ eHHOverrides,
+ ePCOverrides,
+ ePreviousSyncOverrides,
+ eDuplicate,
+ eDelete,
+ eCROffset=-1
+ };
+
+ /**
+ * This MUST stay in sync with the combobox in
+ * kpilotConfigDialog_backup.ui. If it does not, you need to
+ * either change this enum or the combobox.
+ */
+ enum BackupFrequency
+ {
+ eEveryHotSync=0,
+ eOnRequestOnly
+ };
+
+protected:
+ /**
+ * Call startTickle() some time before showing a dialog to the
+ * user (we're assuming a local event loop here) so that while
+ * the dialog is up and the user is thinking, the pilot stays
+ * awake. Afterwards, call stopTickle().
+ *
+ * The parameter to startTickle indicates the timeout, in
+ * seconds, before signal timeout is emitted. You can connect
+ * to that, again, to take down the user interface part if the
+ * user isn't reacting.
+ */
+ void startTickle(unsigned count=0);
+ void stopTickle();
+signals:
+ void timeout();
+
+
+
+
+protected:
+ QWidget *fParent;
+
+ /**
+ * Ask a yes-no question of the user. This has a timeout so that
+ * you don't wait forever for inattentive users. It's much like
+ * KMessageBox::questionYesNo(), but with this extra timeout-on-
+ * no-answer feature. Returns a KDialogBase::ButtonCode value - Yes,No or
+ * Cancel on timeout. If there is a key set and the user indicates not to ask again,
+ * the selected answer (Yes or No) is remembered for future reference.
+ *
+ * @p caption Message Box caption, uses "Question" if null.
+ * @p key Key for the "Don't ask again" code.
+ * @p timeout Timeout, in seconds.
+ */
+ int questionYesNo(const QString &question ,
+ const QString &caption = QString::null,
+ const QString &key = QString::null,
+ unsigned timeout = 20,
+ const QString &yes = QString::null,
+ const QString &no = QString::null );
+ int questionYesNoCancel(const QString &question ,
+ const QString &caption = QString::null,
+ const QString &key = QString::null,
+ unsigned timeout = 20,
+ const QString &yes = QString::null,
+ const QString &no = QString::null ) ;
+};
+
+
+#endif