--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 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.
+\f
+ 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.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 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
+\f
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, 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!
--- /dev/null
+Copyright (C) 2004 SKYRIX Software AG
+
+
+Contact: info@skyrix.com
--- /dev/null
+2005-03-20 Helge Hess <helge.hess@opengroupware.org>
+
+ * moved OGoContentStore as GDLContentStore into sope-gdl1, removed
+ dependencies on NGiCal and removed some SOGo specific things
+ (v4.5.26)
+
+2005-03-07 Helge Hess <helge.hess@opengroupware.org>
+
+ * appointment.ocs: added missing 'partstates' field (v0.9.25)
+
+2005-03-04 Helge Hess <helge.hess@opengroupware.org>
+
+ * v0.9.24
+
+ * ocs_gensql.m: started tool to create SQL CREATE from ocs model file
+
+ * OCSFolderType.m: small change to the factory API, changed to use
+ NGResourceLocator
+
+2005-03-03 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSFolderManager.m: fixed a bug in subfolder listing (v0.9.23)
+
+2005-03-01 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.22
+
+ * appointment.ocs: added 'cycleenddate' and 'cycleinfo' to address
+ previous performance issues
+
+ * OCSiCalFieldExtractor.m: set 'cycleenddate' and 'cycleinfo' for
+ recurrent events. Reverted setting of 'enddate' to the previous
+ behaviour since 'cycleenddate' is dedicated to the task now
+
+ * iCalRepeatableEntityObject+OCS.[hm]: new category used by the
+ OCSiCalFieldExtractor to extract cycleInfo in an appropriate format
+
+ * sql/generate-folderinfo-sql-for-users.sh,
+ sql/foldertablecreate-helge-privcal.psql,
+ sql/foldertablecreate-helge-privcal.sqlite,
+ sql/generate-folderinfo-sql-for-users-sqlite.sh: adjusted to new
+ schema
+
+2005-03-01 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSFolder.m: added support for storing content and quick info in
+ the same table (untested) (v0.9.21)
+
+2005-02-21 Helge Hess <helge.hess@opengroupware.org>
+
+ * v0.9.20
+
+ * OCSFolderManager.m: removed quoting of SQL table and column names
+ (breaks with SQLite and isn't necessary for PG), fixed URL pooling
+ for SQLite
+
+ * NSURL+OCS.m: use tablename for last path component
+
+2005-02-12 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * OCSiCalFieldExtractor.m: uses new iCalEvent API to determine correct
+ 'enddate' for recurrent events. This is an optimization which can
+ save quite some time for complex rules. (v0.9.19)
+
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.18
+
+ * OCSiCalFieldExtractor.m: extract participants' state
+
+ * sql/generate-folderinfo-sql-for-user.sh, sql/appointment-create.psql,
+ sql/foldertablecreate-helge-privcal.psql: updated with new schema.
+
+2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * OCSiCalFieldExtractor.m: partmails + cn's are concatenated by '\n'
+ now - this directly eliminates any ambiguities. Also, instead of
+ using 'email' for partmails and orgmail, the extractor uses the
+ 'rfc822Email' value which strips away any preceeding 'mailto:'
+ prefix, compacting the representation and speeding up comparison.
+ Also, "iscycle", "isallday" and "isopaque" are now provided by
+ NGiCal and thus always extracted (v0.9.17)
+
+2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * sql/generate-folderinfo-sql-for-user.sh: fixed critical error in
+ Contacts folder_info, type was 'Appointment' but MUST be 'Contact'
+ (v0.9.16)
+
+2004-12-10 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * sql: updated all generation scripts to the latest version (v0.9.15)
+
+2004-12-09 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.14
+
+ * appointment.ocs: added "ispublic", "isopaque", "status" and
+ "orgmail".
+
+ * OCSiCalFieldExtractor.m: updated to extract new fields (see above)
+
+ * sql: updated generate-folderinfo-sql-for-users.sh
+
+2004-10-19 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSFolder.m: added new method -fetchContentsOfAllFiles method which
+ fetches the contents of all files stored in the folder (required for
+ iCal generation, such bulk fetches should be avoided if possible!)
+ (v0.9.13)
+
+2004-10-15 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * OCSStringFormatter.[hm]: minor cleanup (v0.9.12)
+
+ * v0.9.11
+
+ * OCSStringFormatter.[hm]: new class to format strings according to
+ Database requirements (escaping etc.).
+
+ * OCSFolder.m: uses new OCSStringFormatter now.
+
+2004-09-25 Helge Hess <helge.hess@opengroupware.org>
+
+ * fixed compilation on MacOSX (v0.9.10)
+
+2004-09-10 Helge Hess <helge.hess@skyrix.com>
+
+ * v0.9.9
+
+ * fixed some gcc warnings
+
+ * GNUmakefile.preamble: added pathes to compile against an FHS SOPE
+
+ * OCSiCalFieldExtractor.m: fixed type of sequence iCalEvent field
+
+2004-09-01 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * GNUmakefile: install type models into $(GNUSTEP_USER_ROOT) (v0.9.8)
+
+2004-08-27 Helge Hess <helge.hess@skyrix.com>
+
+ * v0.9.7
+
+ * OCSChannelManager.m: use PostgreSQL as adaptor, not PostgreSQL72
+
+ * OCSFolder.m: added support for doing folder sorting in SQL
+
+2004-08-26 Helge Hess <helge.hess@skyrix.com>
+
+ * v0.9.6
+
+ * added OCSContactFieldExtractor
+
+ * sql: added sample contact folder create scripts
+
+ * OCSFolderType.m: read extractor class name from type model
+
+ * OCSFolderManager.m: added contact type model per default (v0.9.5)
+
+2004-08-25 Helge Hess <helge.hess@skyrix.com>
+
+ * GNUmakefile: automatically install OCSTypeModels (v0.9.4)
+
+2004-08-15 Helge Hess <helge.hess@skyrix.com>
+
+ * OCSFolder.m: added content deletion (v0.9.3)
+
+ * OCSFolder.m: added sanity check to store method (v0.9.2)
+
+2004-08-14 Helge Hess <helge.hess@skyrix.com>
+
+ * v0.9.1
+
+ * OCSiCalFieldExtractor.m: extract new quick fields: location,
+ partmails, sequence (does not yet handle allday and cycle due to
+ NGiCal restrictions)
+
+ * appointment.ocs, sql/foldertablecreate-helge-privcal.psql,
+ sql/testapt-agenor-helge-privcal.psql, sql/appointment-create.psql:
+ added quick fields: isallday, iscycle, location, partmails, sequence
+
+ * started ocs_recreatequick tool intended for recreating a quick table
+ based on the content table of a folder
+
+2004-07-20 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSChannelManager.m: fixed a bug in the channel GC which resulted
+ in an exception during the GC NSTimer
+
+2004-07-16 Helge Hess <helge.hess@skyrix.com>
+
+ * improved error handling in various files
+
+2004-07-02 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSChannelManager.m: added garbage collector for channel pools
+
+2004-06-30 Helge Hess <helge.hess@opengroupware.org>
+
+ * OCSChannelManager.m: implemented pooling
+
+ * OCSFolder.m: added quick fetches
+
+ * GNUmakefile.preamble: fix link path
+
+ * GNUmakefile (libOGoContentStore_HEADER_FILES_INSTALL_DIR): install
+ headers in OGoContentStore
+
+ * GNUmakefile.preamble (ocs_ls_TOOL_LIBS): added static dependencies
+ for OSX
+
+2004-06-30 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * ocs_cat.m, ocs_ls.m, ocs_mkdir.m: fixed for gnustep compile.
+
+2004-06-29 Helge Hess <helge.hess@opengroupware.org>
+
+ * created ChangeLog
+
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_EOAdaptorChannel_GCS_H__
+#define __GDLContentStore_EOAdaptorChannel_GCS_H__
+
+#include <GDLAccess/EOAdaptorChannel.h>
+
+@interface EOAdaptorChannel(GCS)
+
+- (BOOL)tableExistsWithName:(NSString *)_tableName;
+
+@end
+
+#endif /* __GDLContentStore_EOAdaptorChannel_GCS_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "EOAdaptorChannel+GCS.h"
+#include "common.h"
+
+@implementation EOAdaptorChannel(GCS)
+
+- (BOOL)tableExistsWithName:(NSString *)_tableName {
+ NSException *ex;
+ NSString *sql;
+ BOOL didOpen;
+
+ didOpen = NO;
+ if (![self isOpen]) {
+ if (![self openChannel])
+ return NO;
+ didOpen = YES;
+ }
+
+ sql = @"SELECT COUNT(*) FROM ";
+ sql = [sql stringByAppendingString:_tableName];
+ sql = [sql stringByAppendingString:@" WHERE 1 = 2"];
+
+ ex = [[[self evaluateExpressionX:sql] retain] autorelease];
+ [self cancelFetch];
+
+ if (didOpen) [self closeChannel];
+ return ex != nil ? NO : YES;
+}
+
+@end /* EOAdaptorChannel(GCS) */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_EOQualifier_GCS_H__
+#define __GDLContentStore_EOQualifier_GCS_H__
+
+#include <EOControl/EOQualifier.h>
+
+@class NSMutableString;
+
+@interface EOQualifier(GCS)
+
+- (void)_gcsAppendToString:(NSMutableString *)_ms;
+
+@end
+
+#endif /* __GDLContentStore_EOQualifier_GCS_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "EOQualifier+GCS.h"
+#include "common.h"
+
+@implementation EOQualifier(GCS)
+
+- (void)_appendAndQualifier:(EOAndQualifier *)_q
+ toString:(NSMutableString *)_ms
+{
+ // TODO: move to EOQualifier category
+ NSArray *qs;
+ unsigned i, count;
+
+ qs = [_q qualifiers];
+ if ((count = [qs count]) == 0)
+ return;
+
+ for (i = 0; i < count; i++) {
+ if (i != 0) [_ms appendString:@" AND "];
+ if (count > 1) [_ms appendString:@"("];
+ [[qs objectAtIndex:i] _gcsAppendToString:_ms];
+ if (count > 1) [_ms appendString:@")"];
+ }
+}
+- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q
+ toString:(NSMutableString *)_ms
+{
+ id val;
+
+ [_ms appendString:[_q key]];
+
+ if ((val = [_q value])) {
+ SEL op = [_q selector];
+
+ if ([val isNotNull]) {
+ if (sel_eq(op, EOQualifierOperatorEqual))
+ [_ms appendString:@" = "];
+ else if (sel_eq(op, EOQualifierOperatorNotEqual))
+ [_ms appendString:@" != "];
+ else if (sel_eq(op, EOQualifierOperatorLessThan))
+ [_ms appendString:@" < "];
+ else if (sel_eq(op, EOQualifierOperatorGreaterThan))
+ [_ms appendString:@" > "];
+ else if (sel_eq(op, EOQualifierOperatorLessThanOrEqualTo))
+ [_ms appendString:@" <= "];
+ else if (sel_eq(op, EOQualifierOperatorGreaterThanOrEqualTo))
+ [_ms appendString:@" >= "];
+ else if (sel_eq(op, EOQualifierOperatorLike))
+ [_ms appendString:@" LIKE "];
+ else {
+ [self logWithFormat:@"ERROR(%s): unsupported operation for null: %@",
+ __PRETTY_FUNCTION__, NSStringFromSelector(op)];
+ }
+
+ if ([val isKindOfClass:[NSNumber class]])
+ [_ms appendString:[val stringValue]];
+ else if ([val isKindOfClass:[NSString class]]) {
+ [_ms appendString:@"'"];
+ [_ms appendString:val];
+ [_ms appendString:@"'"];
+ }
+ else {
+ [self logWithFormat:@"ERROR(%s): unsupported value class: %@",
+ __PRETTY_FUNCTION__, NSStringFromClass([val class])];
+ }
+ }
+ else {
+ if (sel_eq(op, EOQualifierOperatorEqual))
+ [_ms appendString:@" IS NULL"];
+ else if (sel_eq(op, EOQualifierOperatorEqual))
+ [_ms appendString:@" IS NOT NULL"];
+ else {
+ [self logWithFormat:@"ERROR(%s): invalid operation for null: %@",
+ __PRETTY_FUNCTION__, NSStringFromSelector(op)];
+ }
+ }
+ }
+ else
+ [_ms appendString:@" IS NULL"];
+}
+
+- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms {
+ if (_q == nil) return;
+
+ if ([_q isKindOfClass:[EOAndQualifier class]])
+ [self _appendAndQualifier:(id)_q toString:_ms];
+ else if ([_q isKindOfClass:[EOKeyValueQualifier class]])
+ [self _appendKeyValueQualifier:(id)_q toString:_ms];
+ else
+ NSLog(@"ERROR: unknown qualifier: %@", _q);
+}
+
+- (void)_gcsAppendToString:(NSMutableString *)_ms {
+ [self _appendQualifier:self toString:_ms];
+}
+
+@end /* EOQualifier(GCS) */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSChannelManager_H__
+#define __GDLContentStore_GCSChannelManager_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+ GCSChannelManager
+
+ This object manages the connection pooling.
+*/
+
+@class NSURL, NSMutableDictionary, NSMutableArray, NSTimer;
+@class EOAdaptorChannel, EOAdaptor;
+
+@interface GCSChannelManager : NSObject
+{
+ NSMutableDictionary *urlToAdaptor;
+
+ NSMutableArray *availableChannels;
+ NSMutableArray *busyChannels;
+ NSTimer *gcTimer;
+}
+
++ (id)defaultChannelManager;
+
+/* channels */
+
+- (EOAdaptorChannel *)acquireOpenChannelForURL:(NSURL *)_url;
+- (void)releaseChannel:(EOAdaptorChannel *)_channel;
+
+/* checking for tables */
+
+- (BOOL)canConnect:(NSURL *)_url;
+
+@end
+
+#endif /* __GDLContentStore_GCSChannelManager_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSChannelManager.h"
+#include "NSURL+GCS.h"
+#include "EOAdaptorChannel+GCS.h"
+#include <GDLAccess/EOAdaptor.h>
+#include <GDLAccess/EOAdaptorContext.h>
+#include <GDLAccess/EOAdaptorChannel.h>
+#include "common.h"
+
+/*
+ TODO:
+ - implemented pooling
+ - auto-close channels which are very old?!
+ (eg missing release due to an exception)
+*/
+
+@interface GCSChannelHandle : NSObject
+{
+@public
+ NSURL *url;
+ EOAdaptorChannel *channel;
+ NSDate *creationTime;
+ NSDate *lastReleaseTime;
+ NSDate *lastAcquireTime;
+}
+
+- (EOAdaptorChannel *)channel;
+- (BOOL)canHandleURL:(NSURL *)_url;
+- (NSTimeInterval)age;
+
+@end
+
+@implementation GCSChannelManager
+
+static BOOL debugOn = NO;
+static BOOL debugPools = NO;
+static int ChannelExpireAge = 180;
+static NSTimeInterval ChannelCollectionTimer = 5 * 60;
+
++ (void)initialize {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+ debugOn = [ud boolForKey:@"GCSChannelManagerDebugEnabled"];
+ debugPools = [ud boolForKey:@"GCSChannelManagerPoolDebugEnabled"];
+
+ ChannelExpireAge = [[ud objectForKey:@"GCSChannelExpireAge"] intValue];
+ if (ChannelExpireAge < 1)
+ ChannelExpireAge = 180;
+
+ ChannelCollectionTimer =
+ [[ud objectForKey:@"GCSChannelCollectionTimer"] intValue];
+ if (ChannelCollectionTimer < 1)
+ ChannelCollectionTimer = 5*60;
+}
+
++ (NSString *)adaptorNameForURLScheme:(NSString *)_scheme {
+ // TODO: map scheme to adaptors (eg 'postgresql://' to PostgreSQL
+ return @"PostgreSQL";
+}
+
++ (id)defaultChannelManager {
+ static GCSChannelManager *cm = nil;
+ if (cm == nil)
+ cm = [[self alloc] init];
+ return cm;
+}
+
+- (id)init {
+ if ((self = [super init])) {
+ self->urlToAdaptor = [[NSMutableDictionary alloc] initWithCapacity:4];
+ self->availableChannels = [[NSMutableArray alloc] initWithCapacity:16];
+ self->busyChannels = [[NSMutableArray alloc] initWithCapacity:16];
+
+ self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval:
+ ChannelCollectionTimer
+ target:self selector:@selector(_garbageCollect:)
+ userInfo:nil repeats:YES] retain];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ if (self->gcTimer) [self->gcTimer invalidate];
+ [self->gcTimer release];
+
+ [self->busyChannels release];
+ [self->availableChannels release];
+ [self->urlToAdaptor release];
+ [super dealloc];
+}
+
+/* DB key */
+
+- (NSString *)databaseKeyForURL:(NSURL *)_url {
+ /*
+ We need to build a proper key that omits passwords and URL path components
+ which are not required.
+ */
+ NSString *key;
+
+ key = [NSString stringWithFormat:@"%@\n%@\n%@\n%@",
+ [_url host], [_url port],
+ [_url user], [_url gcsDatabaseName]];
+ return key;
+}
+
+/* adaptors */
+
+- (NSDictionary *)connectionDictionaryForURL:(NSURL *)_url {
+ NSMutableDictionary *md;
+ id tmp;
+
+ md = [NSMutableDictionary dictionaryWithCapacity:4];
+
+ if ((tmp = [_url host]) != nil)
+ [md setObject:tmp forKey:@"hostName"];
+ if ((tmp = [_url port]) != nil)
+ [md setObject:tmp forKey:@"port"];
+ if ((tmp = [_url user]) != nil)
+ [md setObject:tmp forKey:@"userName"];
+ if ((tmp = [_url password]) != nil)
+ [md setObject:tmp forKey:@"password"];
+
+ if ((tmp = [_url gcsDatabaseName]) != nil)
+ [md setObject:tmp forKey:@"databaseName"];
+
+ [self debugWithFormat:@"build connection dictionary for URL %@: %@",
+ [_url absoluteString], md];
+ return md;
+}
+
+- (EOAdaptor *)adaptorForURL:(NSURL *)_url {
+ EOAdaptor *adaptor;
+ NSString *key;
+
+ if (_url == nil)
+ return nil;
+ if ((key = [self databaseKeyForURL:_url]) == nil)
+ return nil;
+ if ((adaptor = [self->urlToAdaptor objectForKey:key]) != nil) {
+ [self debugWithFormat:@"using cached adaptor: %@", adaptor];
+ return adaptor; /* cached :-) */
+ }
+
+ [self debugWithFormat:@"creating new adaptor for URL: %@", _url];
+
+ if ([EOAdaptor respondsToSelector:@selector(adaptorForURL:)]) {
+ adaptor = [EOAdaptor adaptorForURL:_url];
+ }
+ else {
+ NSString *adaptorName;
+ NSDictionary *condict;
+
+ adaptorName = [[self class] adaptorNameForURLScheme:[_url scheme]];
+ if ([adaptorName length] == 0) {
+ [self logWithFormat:@"ERROR: cannot handle URL: %@", _url];
+ return nil;
+ }
+
+ condict = [self connectionDictionaryForURL:_url];
+
+ if ((adaptor = [EOAdaptor adaptorWithName:adaptorName]) == nil) {
+ [self logWithFormat:@"ERROR: did not find adaptor '%@' for URL: %@",
+ adaptorName, _url];
+ return nil;
+ }
+
+ [adaptor setConnectionDictionary:condict];
+ }
+
+ [self->urlToAdaptor setObject:adaptor forKey:key];
+ return adaptor;
+}
+
+/* channels */
+
+- (GCSChannelHandle *)findBusyChannelHandleForChannel:(EOAdaptorChannel *)_ch {
+ NSEnumerator *e;
+ GCSChannelHandle *handle;
+
+ e = [self->busyChannels objectEnumerator];
+ while ((handle = [e nextObject])) {
+ if ([handle channel] == _ch)
+ return handle;
+ }
+ return nil;
+}
+- (GCSChannelHandle *)findAvailChannelHandleForURL:(NSURL *)_url {
+ NSEnumerator *e;
+ GCSChannelHandle *handle;
+
+ e = [self->availableChannels objectEnumerator];
+ while ((handle = [e nextObject])) {
+ if ([handle canHandleURL:_url])
+ return handle;
+
+ if (debugPools) {
+ [self logWithFormat:@"DBPOOL: cannot use handle (%@ vs %@)",
+ [_url absoluteString], [handle->url absoluteString]];
+ }
+ }
+ return nil;
+}
+
+- (EOAdaptorChannel *)_createChannelForURL:(NSURL *)_url {
+ EOAdaptor *adaptor;
+ EOAdaptorContext *adContext;
+ EOAdaptorChannel *adChannel;
+
+ if ((adaptor = [self adaptorForURL:_url]) == nil)
+ return nil;
+
+ if ((adContext = [adaptor createAdaptorContext]) == nil) {
+ [self logWithFormat:@"ERROR: could not create adaptor context!"];
+ return nil;
+ }
+ if ((adChannel = [adContext createAdaptorChannel]) == nil) {
+ [self logWithFormat:@"ERROR: could not create adaptor channel!"];
+ return nil;
+ }
+ return adChannel;
+}
+
+- (EOAdaptorChannel *)acquireOpenChannelForURL:(NSURL *)_url {
+ // TODO: naive implementation, add pooling!
+ EOAdaptorChannel *channel;
+ GCSChannelHandle *handle;
+ NSCalendarDate *now;
+
+ now = [NSCalendarDate date];
+
+ /* look for cached handles */
+
+ if ((handle = [self findAvailChannelHandleForURL:_url]) != nil) {
+ // TODO: check age?
+ [self->busyChannels addObject:handle];
+ [self->availableChannels removeObject:handle];
+ ASSIGN(handle->lastAcquireTime, now);
+
+ if (debugPools)
+ [self logWithFormat:@"DBPOOL: reused cached DB channel!"];
+ return [[handle channel] retain];
+ }
+
+ if (debugPools) {
+ [self logWithFormat:@"DBPOOL: create new DB channel for URL: %@",
+ [_url absoluteString]];
+ }
+
+ /* create channel */
+
+ if ((channel = [self _createChannelForURL:_url]) == nil)
+ return nil;
+
+ if ([channel isOpen])
+ ;
+ else if (![channel openChannel]) {
+ [self logWithFormat:@"could not open channel %@ for URL: %@",
+ channel, [_url absoluteString]];
+ return nil;
+ }
+
+ /* create handle for channel */
+
+ handle = [[GCSChannelHandle alloc] init];
+ handle->url = [_url retain];
+ handle->channel = [channel retain];
+ handle->creationTime = [now retain];
+ handle->lastAcquireTime = [now retain];
+
+ [self->busyChannels addObject:handle];
+ [handle release];
+
+ return [channel retain];
+}
+- (void)releaseChannel:(EOAdaptorChannel *)_channel {
+ GCSChannelHandle *handle;
+
+ if ((handle = [self findBusyChannelHandleForChannel:_channel]) != nil) {
+ NSCalendarDate *now;
+
+ now = [NSCalendarDate date];
+
+ handle = [handle retain];
+ ASSIGN(handle->lastReleaseTime, now);
+
+ [self->busyChannels removeObject:handle];
+
+ if ([[handle channel] isOpen] && [handle age] < ChannelExpireAge) {
+ // TODO: consider age
+ [self->availableChannels addObject:handle];
+ if (debugPools) {
+ [self logWithFormat:
+ @"DBPOOL: keeping channel (age %ds, #%d): %@",
+ (int)[handle age], [self->availableChannels count],
+ [handle->url absoluteString]];
+ }
+ [_channel release];
+ [handle release];
+ return;
+ }
+
+ if (debugPools) {
+ [self logWithFormat:
+ @"DBPOOL: freeing old channel (age %ds)", (int)[handle age]];
+ }
+
+ /* not reusing channel */
+ [handle release]; handle = nil;
+ }
+
+ if ([_channel isOpen])
+ [_channel closeChannel];
+
+ [_channel release];
+}
+
+/* checking for tables */
+
+- (BOOL)canConnect:(NSURL *)_url {
+ /*
+ this can check for DB connect as well as for table URLs (whether a table
+ exists)
+ */
+ EOAdaptorChannel *channel;
+ NSString *table;
+ BOOL result;
+
+ if ((channel = [self acquireOpenChannelForURL:_url]) == nil) {
+ if (debugOn) [self debugWithFormat:@"could not acquire channel: %@", _url];
+ return NO;
+ }
+ if (debugOn) [self debugWithFormat:@"acquired channel: %@", channel];
+ result = YES; /* could open channel */
+
+ /* check whether table exists */
+
+ table = [_url gcsTableName];
+ if ([table length] > 0)
+ result = [channel tableExistsWithName:table];
+
+ /* release channel */
+
+ [self releaseChannel:channel]; channel = nil;
+
+ return result;
+}
+
+/* collect old channels */
+
+- (void)_garbageCollect:(NSTimer *)_timer {
+ NSMutableArray *handlesToRemove;
+ unsigned i, count;
+
+ if ((count = [self->availableChannels count]) == 0)
+ /* no available channels */
+ return;
+
+ /* collect channels to expire */
+
+ handlesToRemove = [[NSMutableArray alloc] initWithCapacity:4];
+ for (i = 0; i < count; i++) {
+ GCSChannelHandle *handle;
+
+ handle = [self->availableChannels objectAtIndex:i];
+ if (![[handle channel] isOpen]) {
+ [handlesToRemove addObject:handle];
+ continue;
+ }
+ if ([handle age] > ChannelExpireAge) {
+ [handlesToRemove addObject:handle];
+ continue;
+ }
+ }
+
+ /* remove channels */
+ count = [handlesToRemove count];
+ if (debugPools)
+ [self logWithFormat:@"DBPOOL: garbage collecting %d channels.", count];
+ for (i = 0; i < count; i++) {
+ GCSChannelHandle *handle;
+
+ handle = [[handlesToRemove objectAtIndex:i] retain];
+ [self->availableChannels removeObject:handle];
+ if ([[handle channel] isOpen])
+ [[handle channel] closeChannel];
+ [handle release];
+ }
+
+ [handlesToRemove release];
+}
+
+/* debugging */
+
+- (BOOL)isDebuggingEnabled {
+ return debugOn;
+}
+
+/* description */
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+ [ms appendFormat:@" #adaptors=%d", [self->urlToAdaptor count]];
+
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSChannelManager */
+
+@implementation GCSChannelHandle
+
+- (void)dealloc {
+ [self->channel release];
+ [self->creationTime release];
+ [self->lastReleaseTime release];
+ [self->lastAcquireTime release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (EOAdaptorChannel *)channel {
+ return self->channel;
+}
+
+- (BOOL)canHandleURL:(NSURL *)_url {
+ BOOL isSQLite;
+
+ if (_url == nil) {
+ [self logWithFormat:@"MISMATCH: no url .."];
+ return NO;
+ }
+ if (_url == self->url)
+ return YES;
+
+ isSQLite = [[_url scheme] isEqualToString:@"sqlite"];
+
+ if (!isSQLite && ![[self->url host] isEqual:[_url host]]) {
+ [self logWithFormat:@"MISMATCH: different host .."];
+ return NO;
+ }
+ if (![[self->url gcsDatabaseName] isEqualToString:[_url gcsDatabaseName]]) {
+ [self logWithFormat:@"MISMATCH: different db .."];
+ return NO;
+ }
+ if (!isSQLite) {
+ if (![[self->url user] isEqual:[_url user]]) {
+ [self logWithFormat:@"MISMATCH: different user .."];
+ return NO;
+ }
+ if ([[self->url port] intValue] != [[_url port] intValue]) {
+ [self logWithFormat:@"MISMATCH: different port (%@ vs %@) ..",
+ [self->url port], [_url port]];
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (NSTimeInterval)age {
+ return [[NSCalendarDate calendarDate]
+ timeIntervalSinceDate:self->creationTime];
+}
+
+/* NSCopying */
+
+- (id)copyWithZone:(NSZone *)_zone {
+ return [self retain];
+}
+
+/* description */
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+ [ms appendFormat:@" channel=0x%08X", self->channel];
+ if (self->creationTime) [ms appendFormat:@" created=%@", self->creationTime];
+ if (self->lastReleaseTime)
+ [ms appendFormat:@" last-released=%@", self->lastReleaseTime];
+ if (self->lastAcquireTime)
+ [ms appendFormat:@" last-acquired=%@", self->lastAcquireTime];
+
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSChannelHandle */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSContext_H__
+#define __GDLContentStore_GCSContext_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+ GCSContext
+
+ Context passed to all operations.
+*/
+
+@interface GCSContext : NSObject
+{
+}
+
+@end
+
+#endif /* __GDLContentStore_GCSContext_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSContext.h"
+#include "common.h"
+
+@implementation GCSContext
+@end /* GCSContext */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSFieldExtractor_H__
+#define __GDLContentStore_GCSFieldExtractor_H__
+
+#import <Foundation/NSObject.h>
+
+@class NSString, NSMutableDictionary;
+
+@interface GCSFieldExtractor : NSObject
+{
+}
+
+- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content;
+
+@end
+
+#endif /* __GDLContentStore_GCSFieldExtractor_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSFieldExtractor.h"
+#include "common.h"
+
+@implementation GCSFieldExtractor
+
+- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content {
+ return nil;
+}
+
+@end /* GCSFieldExtractor */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSFieldInfo_H__
+#define __GDLContentStore_GCSFieldInfo_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+ GCSFieldInfo
+
+ The field info inside an .ocs schema file.
+
+ The field info objects are stored in an GCSFolderType.
+*/
+
+@class NSString, NSArray;
+
+@interface GCSFieldInfo : NSObject
+{
+ NSString *columnName;
+ NSString *sqlType;
+ BOOL allowsNull;
+ BOOL isPrimaryKey;
+}
+
++ (NSArray *)fieldsForPropertyList:(NSArray *)_plist;
+- (id)initWithPropertyList:(id)_plist;
+
+/* accessors */
+
+- (NSString *)columnName;
+- (NSString *)sqlType;
+- (BOOL)doesAllowNull;
+- (BOOL)isPrimaryKey;
+
+/* generating SQL */
+
+- (NSString *)sqlCreateSection;
+
+@end
+
+#endif /* __GDLContentStore_GCSFieldInfo_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSFieldInfo.h"
+#include "common.h"
+
+@implementation GCSFieldInfo
+
++ (NSArray *)fieldsForPropertyList:(NSArray *)_plist {
+ NSMutableArray *fields;
+ unsigned i, count;
+
+ if (_plist == nil)
+ return nil;
+
+ count = [_plist count];
+ fields = [NSMutableArray arrayWithCapacity:count];
+ for (i = 0; i < count; i++) {
+ GCSFieldInfo *field;
+
+ field = [[GCSFieldInfo alloc] initWithPropertyList:
+ [_plist objectAtIndex:i]];
+ if (field != nil) [fields addObject:field];
+ [field release];
+ }
+ return fields;
+}
+
+- (id)initWithPropertyList:(id)_plist {
+ if ((self = [super init])) {
+ self->columnName = [[_plist objectForKey:@"columnName"] copy];
+ self->sqlType = [[_plist objectForKey:@"sqlType"] copy];
+
+ self->allowsNull = [[_plist objectForKey:@"allowsNull"] boolValue];
+ self->isPrimaryKey = [[_plist objectForKey:@"isPrimaryKey"] boolValue];
+
+ if (![self->columnName isNotNull] || ![self->sqlType isNotNull]) {
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self->columnName release];
+ [self->sqlType release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (NSString *)columnName {
+ return self->columnName;
+}
+- (NSString *)sqlType {
+ return self->sqlType;
+}
+
+- (BOOL)doesAllowNull {
+ return self->allowsNull;
+}
+- (BOOL)isPrimaryKey {
+ return self->isPrimaryKey;
+}
+
+/* generating SQL */
+
+- (NSString *)sqlCreateSection {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:32];
+ [ms appendString:[self columnName]];
+ [ms appendString:@" "];
+ [ms appendString:[self sqlType]];
+
+ [ms appendString:@" "];
+ if (![self doesAllowNull]) [ms appendString:@"NOT "];
+ [ms appendString:@"NULL"];
+
+ if ([self isPrimaryKey]) [ms appendString:@" PRIMARY KEY"];
+ return ms;
+}
+
+/* description */
+
+- (void)appendAttributesToDescription:(NSMutableString *)ms {
+ id tmp;
+
+ if ((tmp = [self columnName]) != nil) [ms appendFormat:@" column=%@", tmp];
+ if ((tmp = [self sqlType]) != nil) [ms appendFormat:@" sql=%@", tmp];
+
+ if ([self doesAllowNull]) [ms appendString:@" allows-null"];
+ if ([self isPrimaryKey]) [ms appendString:@" pkey"];
+}
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+ [self appendAttributesToDescription:ms];
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSFieldInfo */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSFolder_H__
+#define __GDLContentStore_GCSFolder_H__
+
+#import <Foundation/NSObject.h>
+
+@class NSString, NSURL, NSNumber, NSArray, NSException, NSMutableString;
+@class NSDictionary;
+@class EOQualifier, EOFetchSpecification;
+@class EOAdaptorChannel;
+@class GCSFolderManager, GCSFolderType, GCSChannelManager;
+
+@interface GCSFolder : NSObject
+{
+ GCSFolderManager *folderManager;
+ GCSFolderType *folderInfo;
+
+ NSNumber *folderId;
+ NSString *folderName;
+ NSString *path;
+ NSURL *location;
+ NSURL *quickLocation;
+ NSString *folderTypeName;
+
+ struct {
+ int requiresFolderSelect:1;
+ int sameTableForQuick:1;
+ int reserved:30;
+ } ofFlags;
+}
+
+- (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId
+ folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype
+ location:(NSURL *)_loc quickLocation:(NSURL *)_qloc
+ folderManager:(GCSFolderManager *)_fm;
+
+/* accessors */
+
+- (NSNumber *)folderId;
+- (NSString *)folderName;
+- (NSString *)path;
+- (NSURL *)location;
+- (NSURL *)quickLocation;
+- (NSString *)folderTypeName;
+
+- (GCSFolderManager *)folderManager;
+- (GCSChannelManager *)channelManager;
+
+- (NSString *)storeTableName;
+- (NSString *)quickTableName;
+- (BOOL)isQuickInfoStoredInContentTable;
+
+/* connection */
+
+- (EOAdaptorChannel *)acquireStoreChannel;
+- (EOAdaptorChannel *)acquireQuickChannel;
+- (void)releaseChannel:(EOAdaptorChannel *)_channel;
+
+- (BOOL)canConnectStore;
+- (BOOL)canConnectQuick;
+
+/* operations */
+
+- (NSArray *)subFolderNames;
+- (NSArray *)allSubFolderNames;
+
+- (NSString *)fetchContentWithName:(NSString *)_name;
+- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name;
+- (NSException *)deleteContentWithName:(NSString *)_name;
+
+- (NSDictionary *)fetchContentsOfAllFiles;
+
+- (NSArray *)fetchFields:(NSArray *)_flds
+ fetchSpecification:(EOFetchSpecification *)_fs;
+- (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q;
+
+@end
+
+#endif /* __GDLContentStore_GCSFolder_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSFolder.h"
+#include "GCSFolderManager.h"
+#include "GCSFolderType.h"
+#include "GCSChannelManager.h"
+#include "GCSFieldExtractor.h"
+#include "NSURL+GCS.h"
+#include "EOAdaptorChannel+GCS.h"
+#include "EOQualifier+GCS.h"
+#include "GCSStringFormatter.h"
+#include "common.h"
+
+@implementation GCSFolder
+
+static BOOL debugOn = NO;
+static BOOL doLogStore = NO;
+
+static Class NSStringClass = Nil;
+static Class NSNumberClass = Nil;
+static Class NSCalendarDateClass = Nil;
+
+static GCSStringFormatter *stringFormatter = nil;
+
++ (void)initialize {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+ debugOn = [ud boolForKey:@"GCSFolderDebugEnabled"];
+ doLogStore = [ud boolForKey:@"GCSFolderStoreDebugEnabled"];
+
+ NSStringClass = [NSString class];
+ NSNumberClass = [NSNumber class];
+ NSCalendarDateClass = [NSCalendarDate class];
+
+ stringFormatter = [GCSStringFormatter sharedFormatter];
+}
+
+- (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId
+ folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype
+ location:(NSURL *)_loc quickLocation:(NSURL *)_qloc
+ folderManager:(GCSFolderManager *)_fm
+{
+ if (![_loc isNotNull]) {
+ [self logWithFormat:@"ERROR(%s): missing quicktable parameter!",
+ __PRETTY_FUNCTION__];
+ [self release];
+ return nil;
+ }
+
+ if ((self = [super init])) {
+ self->folderManager = [_fm retain];
+ self->folderInfo = [_ftype retain];
+
+ self->folderId = [_folderId copy];
+ self->folderName = [[_path lastPathComponent] copy];
+ self->path = [_path copy];
+ self->location = [_loc retain];
+ self->quickLocation = _qloc ? [_qloc retain] : [_loc retain];
+ self->folderTypeName = [_ftname copy];
+
+ self->ofFlags.requiresFolderSelect = 0;
+ self->ofFlags.sameTableForQuick =
+ [self->location isEqualTo:self->quickLocation] ? 1 : 0;
+ }
+ return self;
+}
+- (id)init {
+ return [self initWithPath:nil primaryKey:nil
+ folderTypeName:nil folderType:nil
+ location:nil quickLocation:nil
+ folderManager:nil];
+}
+
+- (void)dealloc {
+ [self->folderManager release];
+ [self->folderInfo release];
+ [self->folderId release];
+ [self->folderName release];
+ [self->path release];
+ [self->location release];
+ [self->quickLocation release];
+ [self->folderTypeName release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (NSNumber *)folderId {
+ return self->folderId;
+}
+
+- (NSString *)folderName {
+ return self->folderName;
+}
+- (NSString *)path {
+ return self->path;
+}
+
+- (NSURL *)location {
+ return self->location;
+}
+- (NSURL *)quickLocation {
+ return self->quickLocation;
+}
+
+- (NSString *)folderTypeName {
+ return self->folderTypeName;
+}
+
+- (GCSFolderManager *)folderManager {
+ return self->folderManager;
+}
+- (GCSChannelManager *)channelManager {
+ return [[self folderManager] channelManager];
+}
+
+- (NSString *)storeTableName {
+ return [[self location] gcsTableName];
+}
+- (NSString *)quickTableName {
+ return [[self quickLocation] gcsTableName];
+}
+
+- (BOOL)isQuickInfoStoredInContentTable {
+ return self->ofFlags.sameTableForQuick ? YES : NO;
+}
+
+/* channels */
+
+- (EOAdaptorChannel *)acquireStoreChannel {
+ return [[self channelManager] acquireOpenChannelForURL:[self location]];
+}
+- (EOAdaptorChannel *)acquireQuickChannel {
+ return [[self channelManager] acquireOpenChannelForURL:[self quickLocation]];
+}
+
+- (void)releaseChannel:(EOAdaptorChannel *)_channel {
+ [[self channelManager] releaseChannel:_channel];
+ if (debugOn) [self debugWithFormat:@"released channel: %@", _channel];
+}
+
+- (BOOL)canConnectStore {
+ return [[self channelManager] canConnect:[self location]];
+}
+- (BOOL)canConnectQuick {
+ return [[self channelManager] canConnect:[self quickLocation]];
+}
+
+/* operations */
+
+- (NSArray *)subFolderNames {
+ return [[self folderManager] listSubFoldersAtPath:[self path]
+ recursive:NO];
+}
+- (NSArray *)allSubFolderNames {
+ return [[self folderManager] listSubFoldersAtPath:[self path]
+ recursive:YES];
+}
+
+- (id)_fetchValueOfColumn:(NSString *)_col attributeName:(NSString *)_attrName
+ inContentWithName:(NSString *)_name
+{
+ EOAdaptorChannel *channel;
+ NSException *error;
+ NSDictionary *row;
+ NSArray *attrs;
+ NSString *result;
+ NSString *sql;
+
+ if ((channel = [self acquireStoreChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open storage channel!",
+ __PRETTY_FUNCTION__];
+ return nil;
+ }
+
+ /* generate SQL */
+
+ sql = @"SELECT ";
+ sql = [sql stringByAppendingString:_col];
+ sql = [sql stringByAppendingString:@" FROM "];
+ sql = [sql stringByAppendingString:[self storeTableName]];
+ sql = [sql stringByAppendingString:@" WHERE \"c_name\" = '"];
+ sql = [sql stringByAppendingString:_name];
+ sql = [sql stringByAppendingString:@"'"];
+
+ /* run SQL */
+
+ if ((error = [channel evaluateExpressionX:sql]) != nil) {
+ [self logWithFormat:@"ERROR(%s): cannot execute SQL '%@': %@",
+ __PRETTY_FUNCTION__, sql, error];
+ [self releaseChannel:channel];
+ return nil;
+ }
+
+ /* fetch results */
+
+ result = nil;
+ attrs = [channel describeResults];
+ if ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) {
+ result = [[[row objectForKey:_attrName] copy] autorelease];
+ if (![result isNotNull]) result = nil;
+ [channel cancelFetch];
+ }
+
+ /* release and return result */
+
+ [self releaseChannel:channel];
+ return result;
+}
+
+- (NSNumber *)versionOfContentWithName:(NSString *)_name {
+ return [self _fetchValueOfColumn:@"c_version" attributeName:@"cVersion"
+ inContentWithName:_name];
+}
+
+- (NSString *)fetchContentWithName:(NSString *)_name {
+ return [self _fetchValueOfColumn:@"c_content" attributeName:@"cContent"
+ inContentWithName:_name];
+}
+
+- (NSDictionary *)fetchContentsOfAllFiles {
+ /*
+ Note: try to avoid the use of this method! The key of the dictionary
+ will be filename, the value the content.
+ */
+ NSMutableDictionary *result;
+ EOAdaptorChannel *channel;
+ NSException *error;
+ NSDictionary *row;
+ NSArray *attrs;
+ NSString *sql;
+
+ if ((channel = [self acquireStoreChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open storage channel!",
+ __PRETTY_FUNCTION__];
+ return nil;
+ }
+
+ /* generate SQL */
+
+ sql = @"SELECT \"c_name\", \"c_content\" FROM ";
+ sql = [sql stringByAppendingString:[self storeTableName]];
+
+ /* run SQL */
+
+ if ((error = [channel evaluateExpressionX:sql]) != nil) {
+ [self logWithFormat:@"ERROR(%s): cannot execute SQL '%@': %@",
+ __PRETTY_FUNCTION__, sql, error];
+ [self releaseChannel:channel];
+ return nil;
+ }
+
+ /* fetch results */
+
+ result = [NSMutableDictionary dictionaryWithCapacity:128];
+ attrs = [channel describeResults];
+ while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) {
+ NSString *cName, *cContent;
+
+ cName = [row objectForKey:@"cName"];
+ cContent = [row objectForKey:@"cContent"];
+
+ if (![cName isNotNull]) {
+ [self logWithFormat:@"ERROR: missing cName in row: %@", row];
+ continue;
+ }
+ if (![cContent isNotNull]) {
+ [self logWithFormat:@"ERROR: missing cContent in row: %@", row];
+ continue;
+ }
+
+ [result setObject:cContent forKey:cName];
+ }
+
+ /* release and return result */
+
+ [self releaseChannel:channel];
+ return result;
+}
+
+/* writing content */
+
+- (NSString *)_formatRowValue:(id)_value {
+ if (![_value isNotNull])
+ return @"NULL";
+
+ if ([_value isKindOfClass:NSStringClass])
+ return [stringFormatter stringByFormattingString:_value];
+
+ if ([_value isKindOfClass:NSNumberClass])
+ return [_value stringValue];
+
+ if ([_value isKindOfClass:NSCalendarDateClass]) {
+ /* be smart ... convert to timestamp */
+ return [NSString stringWithFormat:@"%i", [_value timeIntervalSince1970]];
+ }
+
+ [self logWithFormat:@"cannot handle value class: %@", [_value class]];
+ return nil;
+}
+
+- (NSString *)_generateInsertStatementForRow:(NSDictionary *)_row
+ tableName:(NSString *)_table
+{
+ // TODO: move to NSDictionary category?
+ NSMutableString *sql;
+ NSArray *keys;
+ unsigned i, count;
+
+ if (_row == nil || _table == nil)
+ return nil;
+
+ keys = [_row allKeys];
+
+ sql = [NSMutableString stringWithCapacity:512];
+ [sql appendString:@"INSERT INTO "];
+ [sql appendString:_table];
+ [sql appendString:@" ("];
+
+ for (i = 0, count = [keys count]; i < count; i++) {
+ if (i != 0) [sql appendString:@", "];
+ [sql appendString:[keys objectAtIndex:i]];
+ }
+
+ [sql appendString:@") VALUES ("];
+
+ for (i = 0, count = [keys count]; i < count; i++) {
+ id value;
+
+ if (i != 0) [sql appendString:@", "];
+ value = [_row objectForKey:[keys objectAtIndex:i]];
+ value = [self _formatRowValue:value];
+ [sql appendString:value];
+ }
+
+ [sql appendString:@")"];
+ return sql;
+}
+
+- (NSString *)_generateUpdateStatementForRow:(NSDictionary *)_row
+ tableName:(NSString *)_table
+ whereColumn:(NSString *)_colname isEqualTo:(id)_value
+{
+ // TODO: move to NSDictionary category?
+ NSMutableString *sql;
+ NSArray *keys;
+ unsigned i, count;
+
+ if (_row == nil || _table == nil)
+ return nil;
+
+ keys = [_row allKeys];
+
+ sql = [NSMutableString stringWithCapacity:512];
+ [sql appendString:@"UPDATE "];
+ [sql appendString:_table];
+
+ [sql appendString:@" SET "];
+ for (i = 0, count = [keys count]; i < count; i++) {
+ id value;
+
+ value = [_row objectForKey:[keys objectAtIndex:i]];
+ value = [self _formatRowValue:value];
+
+ if (i != 0) [sql appendString:@", "];
+ [sql appendString:[keys objectAtIndex:i]];
+ [sql appendString:@" = "];
+ [sql appendString:value];
+ }
+
+ [sql appendString:@" WHERE "];
+ [sql appendString:_colname];
+ [sql appendString:@" = "];
+ [sql appendString:[self _formatRowValue:_value]];
+
+ return sql;
+}
+
+- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name {
+ EOAdaptorChannel *storeChannel, *quickChannel;
+ NSMutableDictionary *quickRow, *contentRow;
+ GCSFieldExtractor *extractor;
+ NSException *error;
+ NSNumber *storedVersion;
+ BOOL isNewRecord;
+ NSCalendarDate *nowDate;
+ NSNumber *now;
+ NSString *qsql, *bsql;
+
+ /* check preconditions */
+
+ if (_name == nil) {
+ return [NSException exceptionWithName:@"GCSStoreException"
+ reason:@"no content filename was provided"
+ userInfo:nil];
+ }
+ if (_content == nil) {
+ return [NSException exceptionWithName:@"GCSStoreException"
+ reason:@"no content was provided"
+ userInfo:nil];
+ }
+
+ /* run */
+
+ error = nil;
+ nowDate = [NSCalendarDate date];
+ now = [NSNumber numberWithUnsignedInt:[nowDate timeIntervalSince1970]];
+
+ if (doLogStore)
+ [self logWithFormat:@"should store content: '%@'\n%@", _name, _content];
+
+ storedVersion = [self versionOfContentWithName:_name];
+ if (doLogStore)
+ [self logWithFormat:@" version: %@", storedVersion];
+ isNewRecord = [storedVersion isNotNull] ? NO : YES;
+
+ /* extract quick info */
+
+ extractor = [self->folderInfo quickExtractor];
+ quickRow = [extractor extractQuickFieldsFromContent:_content];
+ [quickRow setObject:_name forKey:@"c_name"];
+
+ if (doLogStore)
+ [self logWithFormat:@" store quick: %@", quickRow];
+
+ /* make content row */
+
+ contentRow = [NSMutableDictionary dictionaryWithCapacity:16];
+
+ if (self->ofFlags.sameTableForQuick)
+ [contentRow addEntriesFromDictionary:quickRow];
+
+ [contentRow setObject:_name forKey:@"c_name"];
+ if (isNewRecord) [contentRow setObject:now forKey:@"c_creationdate"];
+ [contentRow setObject:now forKey:@"c_lastmodified"];
+ if (isNewRecord)
+ [contentRow setObject:[NSNumber numberWithInt:0] forKey:@"c_version"];
+ else {
+ // TODO: increase version?
+ [contentRow setObject:[NSNumber numberWithInt:[storedVersion intValue]]
+ forKey:@"c_version"];
+ }
+ [contentRow setObject:_content forKey:@"c_content"];
+
+ /* open channels */
+
+ if ((storeChannel = [self acquireStoreChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open storage channel!"];
+ return nil;
+ }
+ if (!self->ofFlags.sameTableForQuick) {
+ if ((quickChannel = [self acquireQuickChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open quick channel!"];
+ [self releaseChannel:storeChannel];
+ return nil;
+ }
+ }
+
+ /* generate SQL */
+
+ qsql = nil;
+ if (isNewRecord) { /* insert */
+ if (!self->ofFlags.sameTableForQuick) {
+ qsql = [self _generateInsertStatementForRow:quickRow
+ tableName:[self quickTableName]];
+ }
+ bsql = [self _generateInsertStatementForRow:contentRow
+ tableName:[self storeTableName]];
+ }
+ else {
+ if (!self->ofFlags.sameTableForQuick) {
+ qsql = [self _generateUpdateStatementForRow:quickRow
+ tableName:[self quickTableName]
+ whereColumn:@"c_name" isEqualTo:_name];
+ }
+ bsql = [self _generateUpdateStatementForRow:contentRow
+ tableName:[self storeTableName]
+ whereColumn:@"c_name" isEqualTo:_name];
+ }
+
+ /* execute */
+ // TODO: execute in transactions
+
+ if ((error = [storeChannel evaluateExpressionX:bsql]) != nil) {
+ [self logWithFormat:@"ERROR(%s): cannot %s content '%@': %@",
+ __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update", bsql, error];
+ }
+
+ if (error == nil && qsql != nil) {
+ if ((error = [quickChannel evaluateExpressionX:qsql]) != nil) {
+ NSString *delsql;
+ NSException *delErr;
+
+ [self logWithFormat:@"ERROR(%s): cannot %s quick '%@': %@",
+ __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update",
+ qsql, error];
+
+ if (isNewRecord) {
+ /* insert in quick failed, so delete in content table */
+
+ delsql = [@"DELETE FROM " stringByAppendingString:
+ [self storeTableName]];
+ delsql = [delsql stringByAppendingString:@" WHERE c_name="];
+ delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
+ if ((delErr = [storeChannel evaluateExpressionX:delsql]) != nil) {
+ [self logWithFormat:
+ @"ERROR(%s): could not delete content '%@' after quick-fail:"
+ @" %@", __PRETTY_FUNCTION__, delsql, error];
+ }
+ }
+ }
+ }
+
+ [self releaseChannel:storeChannel];
+ if (!self->ofFlags.sameTableForQuick) [self releaseChannel:quickChannel];
+ return error;
+}
+
+- (NSException *)deleteContentWithName:(NSString *)_name {
+ EOAdaptorChannel *storeChannel, *quickChannel;
+ NSException *error;
+ NSString *delsql;
+
+ /* check preconditions */
+
+ if (_name == nil) {
+ return [NSException exceptionWithName:@"GCSDeleteException"
+ reason:@"no content filename was provided"
+ userInfo:nil];
+ }
+
+ if (doLogStore)
+ [self logWithFormat:@"should delete content: '%@'", _name];
+
+ /* open channels */
+
+ if ((storeChannel = [self acquireStoreChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open storage channel!"];
+ return nil;
+ }
+ if (!self->ofFlags.sameTableForQuick) {
+ if ((quickChannel = [self acquireQuickChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open quick channel!"];
+ [self releaseChannel:storeChannel];
+ return nil;
+ }
+ }
+
+ /* delete rows */
+
+ delsql = [@"DELETE FROM " stringByAppendingString:[self storeTableName]];
+ delsql = [delsql stringByAppendingString:@" WHERE c_name="];
+ delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
+ if ((error = [storeChannel evaluateExpressionX:delsql]) != nil) {
+ [self logWithFormat:
+ @"ERROR(%s): cannot delete content '%@': %@",
+ __PRETTY_FUNCTION__, delsql, error];
+ }
+ else if (!self->ofFlags.sameTableForQuick) {
+ /* content row deleted, now delete the quick row */
+ delsql = [@"DELETE FROM " stringByAppendingString:[self quickTableName]];
+ delsql = [delsql stringByAppendingString:@" WHERE c_name="];
+ delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]];
+ if ((error = [quickChannel evaluateExpressionX:delsql]) != nil) {
+ [self logWithFormat:
+ @"ERROR(%s): cannot delete quick row '%@': %@",
+ __PRETTY_FUNCTION__, delsql, error];
+ /*
+ Note: we now have a "broken" record, needs to be periodically GCed by
+ a script!
+ */
+ }
+ }
+
+ /* release channels and return */
+
+ [self releaseChannel:storeChannel];
+ if (!self->ofFlags.sameTableForQuick)
+ [self releaseChannel:quickChannel];
+ return error;
+}
+
+- (NSString *)columnNameForFieldName:(NSString *)_fieldName {
+ return _fieldName;
+}
+
+/* SQL generation */
+
+- (NSString *)generateSQLForSortOrderings:(NSArray *)_so {
+ NSMutableString *sql;
+ unsigned i, count;
+
+ if ((count = [_so count]) == 0)
+ return nil;
+
+ sql = [NSMutableString stringWithCapacity:(count * 16)];
+ for (i = 0; i < count; i++) {
+ EOSortOrdering *so;
+ NSString *column;
+ SEL sel;
+
+ so = [_so objectAtIndex:i];
+ sel = [so selector];
+ column = [self columnNameForFieldName:[so key]];
+
+ if (i > 0) [sql appendString:@", "];
+
+ if (sel_eq(sel, EOCompareAscending)) {
+ [sql appendString:column];
+ [sql appendString:@" ASC"];
+ }
+ else if (sel_eq(sel, EOCompareDescending)) {
+ [sql appendString:column];
+ [sql appendString:@" DESC"];
+ }
+ else if (sel_eq(sel, EOCompareCaseInsensitiveAscending)) {
+ [sql appendString:@"UPPER("];
+ [sql appendString:column];
+ [sql appendString:@") ASC"];
+ }
+ else if (sel_eq(sel, EOCompareCaseInsensitiveDescending)) {
+ [sql appendString:@"UPPER("];
+ [sql appendString:column];
+ [sql appendString:@") DESC"];
+ }
+ else {
+ [self logWithFormat:@"cannot handle sort selector in store: %@",
+ NSStringFromSelector(sel)];
+ }
+ }
+ return sql;
+}
+
+- (NSString *)generateSQLForQualifier:(EOQualifier *)_q {
+ NSMutableString *ms;
+
+ if (_q == nil) return nil;
+ ms = [NSMutableString stringWithCapacity:32];
+ [_q _gcsAppendToString:ms];
+ return ms;
+}
+
+/* fetching */
+
+- (NSArray *)fetchFields:(NSArray *)_flds
+ fetchSpecification:(EOFetchSpecification *)_fs
+{
+ EOQualifier *qualifier;
+ NSArray *sortOrderings;
+ EOAdaptorChannel *channel;
+ NSException *error;
+ NSMutableString *sql;
+ NSArray *attrs;
+ NSMutableArray *results;
+ NSDictionary *row;
+
+ qualifier = [_fs qualifier];
+ sortOrderings = [_fs sortOrderings];
+
+#if 0
+ [self logWithFormat:@"FETCH: %@", _flds];
+ [self logWithFormat:@" MATCH: %@", _q];
+#endif
+
+ /* generate SQL */
+
+ sql = [NSMutableString stringWithCapacity:256];
+ [sql appendString:@"SELECT "];
+ if (_flds == nil)
+ [sql appendString:@"*"];
+ else {
+ unsigned i, count;
+
+ count = [_flds count];
+ for (i = 0; i < count; i++) {
+ if (i > 0) [sql appendString:@", "];
+ [sql appendString:[self columnNameForFieldName:[_flds objectAtIndex:i]]];
+ }
+ }
+ [sql appendString:@" FROM "];
+ [sql appendString:[self quickTableName]];
+
+ if (qualifier != nil) {
+ [sql appendString:@" WHERE "];
+ [sql appendString:[self generateSQLForQualifier:qualifier]];
+ }
+ if ([sortOrderings count] > 0) {
+ [sql appendString:@" ORDER BY "];
+ [sql appendString:[self generateSQLForSortOrderings:sortOrderings]];
+ }
+#if 0
+ /* limit */
+ [sql appendString:@" LIMIT "]; // count
+ [sql appendString:@" OFFSET "]; // index from 0
+#endif
+
+ /* open channel */
+
+ if ((channel = [self acquireStoreChannel]) == nil) {
+ [self logWithFormat:@"ERROR(%s): could not open storage channel!"];
+ return nil;
+ }
+
+ /* run SQL */
+
+ if ((error = [channel evaluateExpressionX:sql]) != nil) {
+ [self logWithFormat:@"ERROR(%s): cannot execute quick-fetch SQL '%@': %@",
+ __PRETTY_FUNCTION__, sql, error];
+ [self releaseChannel:channel];
+ return nil;
+ }
+
+ /* fetch results */
+
+ results = [NSMutableArray arrayWithCapacity:64];
+ attrs = [channel describeResults];
+ while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil)
+ [results addObject:row];
+
+ /* release channels */
+
+ [self releaseChannel:channel];
+
+ return results;
+}
+- (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q {
+ EOFetchSpecification *fs;
+
+ if (_q == nil)
+ fs = nil;
+ else {
+ fs = [EOFetchSpecification fetchSpecificationWithEntityName:
+ [self folderName]
+ qualifier:_q
+ sortOrderings:nil];
+ }
+ return [self fetchFields:_flds fetchSpecification:fs];
+}
+
+/* description */
+
+- (NSString *)description {
+ NSMutableString *ms;
+ id tmp;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+ if (self->folderId)
+ [ms appendFormat:@" id=%@", self->folderId];
+ else
+ [ms appendString:@" no-id"];
+
+ if ((tmp = [self path])) [ms appendFormat:@" path=%@", tmp];
+ if ((tmp = [self folderTypeName])) [ms appendFormat:@" type=%@", tmp];
+ if ((tmp = [self location]))
+ [ms appendFormat:@" loc=%@", [tmp absoluteString]];
+
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSFolder */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSFolderManager_H__
+#define __GDLContentStore_GCSFolderManager_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+ GCSFolderManager
+
+ Objects of this class manage the "folder_info" table, they manage the
+ model and manage the tables required for a folder.
+*/
+
+@class NSString, NSArray, NSURL, NSDictionary, NSException;
+@class GCSChannelManager, GCSFolder, GCSFolderType;
+
+@interface GCSFolderManager : NSObject
+{
+ GCSChannelManager *channelManager;
+ NSDictionary *nameToType;
+ NSURL *folderInfoLocation;
+}
+
++ (id)defaultFolderManager;
+- (id)initWithFolderInfoLocation:(NSURL *)_url;
+
+/* accessors */
+
+- (NSURL *)folderInfoLocation;
+- (NSString *)folderInfoTableName;
+
+/* connection */
+
+- (GCSChannelManager *)channelManager;
+- (BOOL)canConnect;
+
+/* handling folder names */
+
+- (NSString *)internalNameFromPath:(NSString *)_path;
+- (NSArray *)internalNamesFromPath:(NSString *)_path;
+- (NSString *)pathFromInternalName:(NSString *)_name;
+
+/* operations */
+
+- (BOOL)folderExistsAtPath:(NSString *)_path;
+- (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_flag;
+
+- (GCSFolder *)folderAtPath:(NSString *)_path;
+
+- (NSException *)createFolderOfType:(NSString *)_type atPath:(NSString *)_path;
+
+/* folder types */
+
+- (GCSFolderType *)folderTypeWithName:(NSString *)_name;
+
+/* cache management */
+
+- (void)reset;
+
+@end
+
+#endif /* __GDLContentStore_GCSFolderManager_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSFolderManager.h"
+#include "GCSChannelManager.h"
+#include "GCSFolderType.h"
+#include "GCSFolder.h"
+#include "NSURL+GCS.h"
+#include "EOAdaptorChannel+GCS.h"
+#include "common.h"
+#include <GDLAccess/EOAdaptorChannel.h>
+
+/*
+ Required database schema:
+
+ <arbitary table>
+ c_path
+ c_path1, path2, path3... [quickPathCount times]
+ c_foldername
+
+ TODO:
+ - add a local cache?
+*/
+
+@implementation GCSFolderManager
+
+static GCSFolderManager *fm = nil;
+static BOOL debugOn = NO;
+static BOOL debugSQLGen = NO;
+static BOOL debugPathTraversal = NO;
+static int quickPathCount = 4;
+static NSArray *emptyArray = nil;
+#if 0
+static NSString *GCSPathColumnName = @"c_path";
+static NSString *GCSTypeColumnName = @"c_folder_type";
+static NSString *GCSTypeRecordName = @"cFolderType";
+#endif
+static NSString *GCSPathRecordName = @"cPath";
+static NSString *GCSGenericFolderTypeName = @"Container";
+static const char *GCSPathColumnPattern = "c_path%i";
+
++ (void)initialize {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+ debugOn = [ud boolForKey:@"GCSFolderManagerDebugEnabled"];
+ debugSQLGen = [ud boolForKey:@"GCSFolderManagerSQLDebugEnabled"];
+ emptyArray = [[NSArray alloc] init];
+}
+
++ (id)defaultFolderManager {
+ NSString *s;
+ NSURL *url;
+ if (fm) return fm;
+
+ s = [[NSUserDefaults standardUserDefaults] stringForKey:@"OCSFolderInfoURL"];
+ if ([s length] == 0) {
+ NSLog(@"ERROR(%s): default 'OCSFolderInfoURL' is not configured.",
+ __PRETTY_FUNCTION__);
+ return nil;
+ }
+ if ((url = [NSURL URLWithString:s]) == nil) {
+ NSLog(@"ERROR(%s): default 'OCSFolderInfoURL' is not a valid URL: '%@'",
+ __PRETTY_FUNCTION__, s);
+ return nil;
+ }
+ if ((fm = [[self alloc] initWithFolderInfoLocation:url]) == nil) {
+ NSLog(@"ERROR(%s): could not create folder manager with URL: '%@'",
+ __PRETTY_FUNCTION__, [url absoluteString]);
+ return nil;
+ }
+
+ NSLog(@"Note: setup default manager at: %@", url);
+ return fm;
+}
+
+- (id)initWithFolderInfoLocation:(NSURL *)_url {
+ if (_url == nil) {
+ [self logWithFormat:@"ERROR(%s): missing folder info url!",
+ __PRETTY_FUNCTION__];
+ [self release];
+ return nil;
+ }
+ if ((self = [super init])) {
+ GCSFolderType *cal, *contact;
+
+ self->channelManager = [[GCSChannelManager defaultChannelManager] retain];
+ self->folderInfoLocation = [_url retain];
+
+ if ([[self folderInfoTableName] length] == 0) {
+ [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@",
+ __PRETTY_FUNCTION__, [_url absoluteString]];
+ [self release];
+ return nil;
+ }
+
+ /* register default folder types */
+
+ cal = [[GCSFolderType alloc] initWithFolderTypeName:@"appointment"];
+ contact = [[GCSFolderType alloc] initWithFolderTypeName:@"contact"];
+ self->nameToType = [[NSDictionary alloc] initWithObjectsAndKeys:
+ cal, @"appointment",
+ contact, @"contact",
+ nil];
+ [cal release]; cal = nil;
+ [contact release]; contact = nil;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self->nameToType release];
+ [self->folderInfoLocation release];
+ [self->channelManager release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (NSURL *)folderInfoLocation {
+ return self->folderInfoLocation;
+}
+
+- (NSString *)folderInfoTableName {
+ return [[self folderInfoLocation] gcsTableName];
+}
+
+/* connection */
+
+- (GCSChannelManager *)channelManager {
+ return self->channelManager;
+}
+
+- (EOAdaptorChannel *)acquireOpenChannel {
+ EOAdaptorChannel *ch;
+
+ ch = [[self channelManager] acquireOpenChannelForURL:
+ [self folderInfoLocation]];
+ return ch;
+}
+- (void)releaseChannel:(EOAdaptorChannel *)_channel {
+ [[self channelManager] releaseChannel:_channel];
+ if (debugOn) [self debugWithFormat:@"released channel: %@", _channel];
+}
+
+- (BOOL)canConnect {
+ return [[self channelManager] canConnect:[self folderInfoLocation]];
+}
+
+- (NSArray *)performSQL:(NSString *)_sql {
+ EOAdaptorChannel *channel;
+ NSException *ex;
+ NSMutableArray *rows;
+ NSDictionary *row;
+ NSArray *attrs;
+
+ /* acquire channel */
+
+ if ((channel = [self acquireOpenChannel]) == nil) {
+ if (debugOn) [self debugWithFormat:@"could not acquire channel!"];
+ return nil;
+ }
+ if (debugOn) [self debugWithFormat:@"acquired channel: %@", channel];
+
+ /* run SQL */
+
+ if ((ex = [channel evaluateExpressionX:_sql]) != nil) {
+ [self logWithFormat:@"ERROR(%s): cannot execute\n SQL '%@':\n %@",
+ __PRETTY_FUNCTION__, _sql, ex];
+ [self releaseChannel:channel];
+ return nil;
+ }
+
+ /* fetch results */
+
+ attrs = [channel describeResults];
+ rows = [NSMutableArray arrayWithCapacity:16];
+ while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil)
+ [rows addObject:row];
+
+ [self releaseChannel:channel];
+ return rows;
+}
+
+/* row factory */
+
+- (GCSFolder *)folderForRecord:(NSDictionary *)_record {
+ GCSFolder *folder;
+ GCSFolderType *folderType;
+ NSString *folderTypeName, *locationString, *folderName, *path;
+ NSNumber *folderId;
+ NSURL *location, *quickLocation;
+
+ if (_record == nil) return nil;
+
+ folderTypeName = [_record objectForKey:@"cFolderType"];
+ if (![folderTypeName isNotNull]) {
+ [self logWithFormat:@"ERROR(%s): missing type in folder: %@",
+ __PRETTY_FUNCTION__, _record];
+ return nil;
+ }
+ if ((folderType = [self folderTypeWithName:folderTypeName]) == nil) {
+ [self logWithFormat:
+ @"ERROR(%s): could not resolve type '%@' of folder: %@",
+ __PRETTY_FUNCTION__,
+ folderTypeName, [_record valueForKey:@"cPath"]];
+ return nil;
+ }
+
+ folderId = [_record objectForKey:@"cFolderId"];
+ folderName = [_record objectForKey:@"cPath"];
+ path = [self pathFromInternalName:folderName];
+
+ locationString = [_record objectForKey:@"cLocation"];
+ location = [locationString isNotNull]
+ ? [NSURL URLWithString:locationString]
+ : nil;
+ if (location == nil) {
+ [self logWithFormat:@"ERROR(%s): missing folder location in record: %@",
+ __PRETTY_FUNCTION__, _record];
+ return nil;
+ }
+
+ locationString = [_record objectForKey:@"cQuickLocation"];
+ quickLocation = [locationString isNotNull]
+ ? [NSURL URLWithString:locationString]
+ : nil;
+
+ if (quickLocation == nil) {
+ [self logWithFormat:@"WARNING(%s): missing quick location in record: %@",
+ __PRETTY_FUNCTION__, _record];
+ }
+
+ folder = [[GCSFolder alloc] initWithPath:path primaryKey:folderId
+ folderTypeName:folderTypeName
+ folderType:folderType
+ location:location quickLocation:quickLocation
+ folderManager:self];
+ return [folder autorelease];
+}
+
+/* path SQL */
+
+- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
+ exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
+{
+ /* generates a WHERE qualifier for matching the "quick" entries */
+ NSMutableString *sql;
+ unsigned i, count;
+
+ if ((count = [_names count]) == 0) {
+ [self debugWithFormat:@"WARNING(%s): passed in empty name array!",
+ __PRETTY_FUNCTION__];
+ return @"1 = 2";
+ }
+
+ sql = [NSMutableString stringWithCapacity:(count * 8)];
+ for (i = 0; i < quickPathCount; i++) {
+ NSString *pathColumn;
+ unsigned char buf[32];
+
+ sprintf(buf, GCSPathColumnPattern, (i + 1));
+ pathColumn = [[NSString alloc] initWithCString:buf];
+
+ /* Note: the AND addition must be inside the if's for non-exact stuff */
+
+ if (i < count) {
+ /* exact match, regular column */
+ if ([sql length] > 0) [sql appendString:@" AND "];
+ [sql appendString:pathColumn];
+ [sql appendFormat:@" = '%@'", [_names objectAtIndex:i]];
+ }
+ else if (_beExact) {
+ /* exact match, ensure that all additional quick-cols are NULL */
+ if ([sql length] > 0) [sql appendString:@" AND "];
+ [sql appendString:pathColumn];
+ [sql appendString:@" IS NULL"];
+ if (debugPathTraversal) [self logWithFormat:@"BE EXACT, NULL columns"];
+ }
+ else if (_directSubs) {
+ /* fetch immediate subfolders */
+ if ([sql length] > 0) [sql appendString:@" AND "];
+ [sql appendString:pathColumn];
+ if (i == count) {
+ /* if it is a direct subfolder, the next path cannot be empty */
+ [sql appendString:@" IS NOT NULL"];
+ if (debugPathTraversal)
+ [self logWithFormat:@"DIRECT SUBS, first level"];
+ }
+ else {
+ /* but for 'direct' subfolders, all following things must be empty */
+ [sql appendString:@" IS NULL"];
+ if (debugPathTraversal)
+ [self logWithFormat:@"DIRECT SUBS, lower level"];
+ }
+ }
+
+ [pathColumn release];
+ }
+
+ if (_beExact && (count > quickPathCount)) {
+ [sql appendString:@" AND c_foldername = '"];
+ [sql appendString:[_names lastObject]];
+ [sql appendString:@"'"];
+ }
+
+ return sql;
+}
+
+- (NSString *)generateSQLPathFetchForInternalNames:(NSArray *)_names
+ exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
+{
+ /* fetches the 'path' subset for a given quick-names */
+ NSMutableString *sql;
+ NSString *ws;
+
+ ws = [self generateSQLWhereForInternalNames:_names
+ exactMatch:_beExact orDirectSubfolderMatch:_directSubs];
+ if ([ws length] == 0)
+ return nil;
+
+ sql = [NSMutableString stringWithCapacity:256];
+ [sql appendString:@"SELECT c_path FROM "];
+ [sql appendString:[self folderInfoTableName]];
+ [sql appendString:@" WHERE "];
+ [sql appendString:ws];
+ if (debugSQLGen) [self logWithFormat:@"PathFetch-SQL: %@", sql];
+ return sql;
+}
+
+/* handling folder names */
+
+- (BOOL)_isStandardizedPath:(NSString *)_path {
+ if (![_path isAbsolutePath]) return NO;
+ if ([_path rangeOfString:@".."].length > 0) return NO;
+ if ([_path rangeOfString:@"~"].length > 0) return NO;
+ if ([_path rangeOfString:@"//"].length > 0) return NO;
+ return YES;
+}
+
+- (NSString *)internalNameFromPath:(NSString *)_path {
+ // TODO: ensure proper path and SQL escaping!
+
+ if (![self _isStandardizedPath:_path]) {
+ [self debugWithFormat:@"%s: not a standardized path: '%@'",
+ __PRETTY_FUNCTION__, _path];
+ return nil;
+ }
+
+ if ([_path hasSuffix:@"/"] && [_path length] > 1)
+ _path = [_path substringToIndex:([_path length] - 1)];
+
+ return _path;
+}
+- (NSArray *)internalNamesFromPath:(NSString *)_path {
+ NSString *fname;
+ NSArray *fnames;
+
+ if ((fname = [self internalNameFromPath:_path]) == nil)
+ return nil;
+
+ if ([fname hasPrefix:@"/"])
+ fname = [fname substringFromIndex:1];
+
+ fnames = [fname componentsSeparatedByString:@"/"];
+ if ([fnames count] == 0)
+ return nil;
+
+ return fnames;
+}
+- (NSString *)pathFromInternalName:(NSString *)_name {
+ /* for incomplete pathes, like '/Users/helge/' */
+ return _name;
+}
+- (NSString *)pathPartFromInternalName:(NSString *)_name {
+ /* for incomplete pathes, like 'Users/' */
+ return _name;
+}
+
+- (NSDictionary *)filterRecords:(NSArray *)_records forPath:(NSString *)_path {
+ unsigned i, count;
+ NSString *name;
+
+ if (_records == nil) return nil;
+ if ((name = [self internalNameFromPath:_path]) == nil) return nil;
+
+ for (i = 0, count = [_records count]; i < count; i++) {
+ NSDictionary *record;
+ NSString *recName;
+
+ record = [_records objectAtIndex:i];
+ recName = [record objectForKey:GCSPathRecordName];
+#if 0
+ [self logWithFormat:@"check '%@' vs '%@' (%@)...",
+ name, recName, [_records objectAtIndex:i]];
+#endif
+
+ if ([name isEqualToString:recName])
+ return [_records objectAtIndex:i];
+ }
+ return nil;
+}
+
+- (BOOL)folderExistsAtPath:(NSString *)_path {
+ NSString *fname;
+ NSArray *fnames, *records;
+ NSString *sql;
+ unsigned count;
+
+ if ((fnames = [self internalNamesFromPath:_path]) == nil) {
+ [self debugWithFormat:@"got no internal names for path: '%@'", _path];
+ return NO;
+ }
+
+ sql = [self generateSQLPathFetchForInternalNames:fnames
+ exactMatch:YES orDirectSubfolderMatch:NO];
+ if ([sql length] == 0) {
+ [self debugWithFormat:@"got no SQL for names: %@", fnames];
+ return NO;
+ }
+
+ if ((records = [self performSQL:sql]) == nil) {
+ [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
+ __PRETTY_FUNCTION__, sql];
+ return NO;
+ }
+
+ if ((count = [records count]) == 0)
+ return NO;
+
+ fname = [self internalNameFromPath:_path];
+ if (count == 1) {
+ NSDictionary *record;
+ NSString *sname;
+
+ record = [records objectAtIndex:0];
+ sname = [record objectForKey:GCSPathRecordName];
+ return [fname isEqualToString:sname];
+ }
+
+ [self logWithFormat:@"records: %@", records];
+
+ return NO;
+}
+
+- (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_recursive{
+ NSMutableArray *result;
+ NSString *fname;
+ NSArray *fnames, *records;
+ NSString *sql;
+ unsigned i, count;
+
+ if ((fnames = [self internalNamesFromPath:_path]) == nil) {
+ [self debugWithFormat:@"got no internal names for path: '%@'", _path];
+ return nil;
+ }
+
+ sql = [self generateSQLPathFetchForInternalNames:fnames
+ exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES)];
+ if ([sql length] == 0) {
+ [self debugWithFormat:@"got no SQL for names: %@", fnames];
+ return nil;
+ }
+
+ if ((records = [self performSQL:sql]) == nil) {
+ [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
+ __PRETTY_FUNCTION__, sql];
+ return nil;
+ }
+
+ if ((count = [records count]) == 0)
+ return emptyArray;
+
+ result = [NSMutableArray arrayWithCapacity:(count > 128 ? 128 : count)];
+
+ fname = [self internalNameFromPath:_path];
+ fname = [fname stringByAppendingString:@"/"]; /* add slash */
+ for (i = 0; i < count; i++) {
+ NSDictionary *record;
+ NSString *sname, *spath;
+
+ record = [records objectAtIndex:i];
+ sname = [record objectForKey:GCSPathRecordName];
+ if (![sname hasPrefix:fname]) /* does not match at all ... */
+ continue;
+
+ /* strip prefix and following slash */
+ sname = [sname substringFromIndex:[fname length]];
+ spath = [self pathPartFromInternalName:sname];
+
+ if (_recursive) {
+ if ([spath length] > 0) [result addObject:spath];
+ }
+ else {
+ /* direct children only, so exclude everything with a slash */
+ if ([sname rangeOfString:@"/"].length == 0 && [spath length] > 0)
+ [result addObject:spath];
+ }
+ }
+
+ return result;
+}
+
+- (GCSFolder *)folderAtPath:(NSString *)_path {
+ NSMutableString *sql;
+ NSArray *fnames, *records;
+ NSString *ws;
+ NSDictionary *record;
+
+ if ((fnames = [self internalNamesFromPath:_path]) == nil) {
+ [self debugWithFormat:@"got no internal names for path: '%@'", _path];
+ return nil;
+ }
+
+ /* generate SQL to fetch folder attributes */
+
+ ws = [self generateSQLWhereForInternalNames:fnames
+ exactMatch:YES orDirectSubfolderMatch:NO];
+
+ sql = [NSMutableString stringWithCapacity:256];
+ [sql appendString:@"SELECT "];
+ [sql appendString:@"c_folder_id, "];
+ [sql appendString:@"c_path, "];
+ [sql appendString:@"c_location, c_quick_location, "];
+ [sql appendString:@"c_folder_type"];
+ [sql appendString:@" FROM "];
+ [sql appendString:[self folderInfoTableName]];
+ [sql appendString:@" WHERE "];
+ [sql appendString:ws];
+
+ /* fetching */
+
+ if ((records = [self performSQL:sql]) == nil) {
+ [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
+ __PRETTY_FUNCTION__, sql];
+ return nil;
+ }
+
+ // TODO: need to filter on path
+ // required when we start to have deeper hierarchies
+ // => isn't that already done below?
+
+ if ([records count] != 1) {
+ if ([records count] == 0) {
+ [self debugWithFormat:@"found no records for path: '%@'", _path];
+ return nil;
+ }
+
+ [self logWithFormat:@"ERROR(%s): more than one row for path: '%@'",
+ __PRETTY_FUNCTION__, _path];
+ return nil;
+ }
+
+ if ((record = [self filterRecords:records forPath:_path]) == nil) {
+ [self debugWithFormat:@"found no record for path: '%@'", _path];
+ return nil;
+ }
+
+ return [self folderForRecord:record];
+}
+
+- (NSException *)createFolderOfType:(NSString *)_type atPath:(NSString *)_path{
+ // TODO: implement folder create
+ GCSFolderType *ftype;
+
+ if ((ftype = [self folderTypeWithName:_type]) == nil) {
+ return [NSException exceptionWithName:@"GCSMissingFolderType"
+ reason:@"missing folder type"
+ userInfo:nil];
+ }
+
+ [self logWithFormat:@"create folder of type: %@", ftype];
+
+ return [NSException exceptionWithName:@"NotYetImplemented"
+ reason:@"no money, no time, ..."
+ userInfo:nil];
+}
+
+/* folder types */
+
+- (GCSFolderType *)folderTypeWithName:(NSString *)_name {
+ if ([_name length] == 0)
+ _name = GCSGenericFolderTypeName;
+
+ return [self->nameToType objectForKey:[_name lowercaseString]];
+}
+
+/* cache management */
+
+- (void)reset {
+ /* does nothing in the moment, but we need a way to signal refreshes */
+}
+
+/* debugging */
+
+- (BOOL)isDebuggingEnabled {
+ return debugOn;
+}
+
+/* description */
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+ [ms appendFormat:@" url=%@", [self->folderInfoLocation absoluteString]];
+ [ms appendFormat:@" channel-manager=0x%08X", [self channelManager]];
+
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSFolderManager */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSFolderType_H__
+#define __GDLContentStore_GCSFolderType_H__
+
+/*
+ GCSFolderType
+
+ This is the "model" of a folder, it specifies what quick attributes are
+ available, in which tables it is stored and how it is retrieved.
+
+ For now, we only support one 'quick' table (we might want to have multiple
+ ones in the future).
+
+ Note: using the 'folderQualifier' we are actually prepared for 'multiple
+ folders in a single table' setups. So in case we want to go that
+ route later, we can still do it :-)
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSString, NSArray, NSDictionary;
+@class EOQualifier;
+@class GCSFolder, GCSFieldExtractor;
+
+@interface GCSFolderType : NSObject
+{
+ NSString *blobTablePattern; // eg 'SOGo_$folderId$_blob
+ NSString *quickTablePattern; // eg 'SOGo_$folderId$_quick
+ NSArray *fields; // GCSFieldInfo objects
+ NSDictionary *fieldDict; // maps a name to GCSFieldInfo
+ EOQualifier *folderQualifier; // to further limit the table set
+ NSString *extractorClassName;
+ GCSFieldExtractor *extractor;
+}
+
++ (id)folderTypeWithName:(NSString *)_type;
+
+- (id)initWithPropertyList:(id)_plist;
+- (id)initWithContentsOfFile:(NSString *)_path;
+- (id)initWithFolderTypeName:(NSString *)_typeName;
+
+/* operations */
+
+- (NSString *)blobTableNameForFolder:(GCSFolder *)_folder;
+- (NSString *)quickTableNameForFolder:(GCSFolder *)_folder;
+
+/* generating SQL */
+
+- (NSString *)sqlQuickCreateWithTableName:(NSString *)_tabName;
+
+/* quick support */
+
+- (GCSFieldExtractor *)quickExtractor;
+
+@end
+
+#endif /* __GDLContentStore_GCSFolderType_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSFolderType.h"
+#include "GCSFolder.h"
+#include "GCSFieldInfo.h"
+#include "GCSFieldExtractor.h"
+#include "common.h"
+#include <EOControl/EOKeyValueCoding.h>
+#include <NGExtensions/NGResourceLocator.h>
+
+@implementation GCSFolderType
+
+- (id)initWithPropertyList:(id)_plist {
+ if ((self = [super init])) {
+ self->blobTablePattern = [[_plist objectForKey:@"blobTablePattern"] copy];
+ self->quickTablePattern = [[_plist objectForKey:@"quickTablePattern"]copy];
+
+ self->extractorClassName =
+ [[_plist objectForKey:@"extractorClassName"] copy];
+ // TODO: qualifier;
+
+ self->fields = [[GCSFieldInfo fieldsForPropertyList:
+ [_plist objectForKey:@"fields"]] retain];
+ }
+ return self;
+}
+
+- (id)initWithContentsOfFile:(NSString *)_path {
+ NSDictionary *plist;
+
+ plist = [NSDictionary dictionaryWithContentsOfFile:_path];
+ if (plist == nil) {
+ NSLog(@"ERROR(%s): could not read dictionary at path %@",
+ __PRETTY_FUNCTION__, _path);
+ [self release];
+ return nil;
+ }
+ return [self initWithPropertyList:plist];
+}
+
++ (NGResourceLocator *)resourceLocator {
+ NGResourceLocator *loc;
+
+ // TODO: fix me, GCS instead of OCS
+ loc = [NGResourceLocator resourceLocatorForGNUstepPath:
+ @"Library/OCSTypeModels"
+ fhsPath:@"share/ocs"];
+ return loc;
+}
+
++ (id)folderTypeWithName:(NSString *)_typeName {
+ NSString *filename, *path;
+
+ // TODO: fix me, GCS instead of OCS
+ filename = [_typeName stringByAppendingPathExtension:@"ocs"];
+ path = [[self resourceLocator] lookupFileWithName:filename];
+
+ if (path != nil)
+ return [[self alloc] initWithContentsOfFile:path];
+
+ NSLog(@"ERROR(%s): did not find model for type: '%@'",
+ __PRETTY_FUNCTION__, _typeName);
+ return nil;
+}
+
+- (id)initWithFolderTypeName:(NSString *)_typeName {
+ // DEPRECATED
+ [self release];
+ return [[GCSFolderType folderTypeWithName:_typeName] retain];
+}
+
+- (void)dealloc {
+ [self->extractor release];
+ [self->extractorClassName release];
+ [self->blobTablePattern release];
+ [self->quickTablePattern release];
+ [self->fields release];
+ [self->fieldDict release];
+ [self->folderQualifier release];
+ [super dealloc];
+}
+
+/* operations */
+
+- (NSString *)blobTableNameForFolder:(GCSFolder *)_folder {
+ return [self->blobTablePattern
+ stringByReplacingVariablesWithBindings:_folder];
+}
+- (NSString *)quickTableNameForFolder:(GCSFolder *)_folder {
+ return [self->quickTablePattern
+ stringByReplacingVariablesWithBindings:_folder];
+}
+
+- (EOQualifier *)qualifierForFolder:(GCSFolder *)_folder {
+ NSArray *keys;
+ NSDictionary *bindings;
+
+ keys = [[self->folderQualifier allQualifierKeys] allObjects];
+ if ([keys count] == 0)
+ return self->folderQualifier;
+
+ bindings = [_folder valuesForKeys:keys];
+ return [self->folderQualifier qualifierWithBindings:bindings
+ requiresAllVariables:NO];
+}
+
+/* generating SQL */
+
+- (NSString *)sqlQuickCreateWithTableName:(NSString *)_tabName {
+ NSMutableString *sql;
+ unsigned i, count;
+
+ sql = [NSMutableString stringWithCapacity:512];
+ [sql appendString:@"CREATE TABLE "];
+ [sql appendString:_tabName];
+ [sql appendString:@" (\n"];
+
+ count = [self->fields count];
+ for (i = 0; i < count; i++) {
+ if (i > 0) [sql appendString:@",\n"];
+
+ [sql appendString:@" "];
+ [sql appendString:[[self->fields objectAtIndex:i] sqlCreateSection]];
+ }
+
+ [sql appendString:@"\n)"];
+
+ return sql;
+}
+
+/* quick support */
+
+- (GCSFieldExtractor *)quickExtractor {
+ Class clazz;
+
+ if (self->extractor)
+ return [self->extractor isNotNull] ? self->extractor : nil;
+
+ clazz = self->extractorClassName
+ ? NSClassFromString(self->extractorClassName)
+ : [GCSFieldExtractor class];
+ if (clazz == Nil) {
+ [self logWithFormat:@"ERROR: did not find field extractor class!"];
+ return nil;
+ }
+
+ if ((self->extractor = [[clazz alloc] init]) == nil) {
+ [self logWithFormat:@"ERROR: could not create field extractor of class %@",
+ clazz];
+ return nil;
+ }
+ return self->extractor;
+}
+
+/* description */
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:256];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+ [ms appendFormat:@" blobtable='%@'", self->blobTablePattern];
+ [ms appendFormat:@" quicktable='%@'", self->quickTablePattern];
+ [ms appendFormat:@" fields=%@", self->fields];
+ [ms appendFormat:@" extractor=%@", self->extractorClassName];
+
+ if (self->folderQualifier)
+ [ms appendFormat:@" qualifier=%@", self->folderQualifier];
+
+ [ms appendString:@">"];
+ return ms;
+}
+
+@end /* GCSFolderType */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_GCSStringFormatter_H__
+#define __GDLContentStore_GCSStringFormatter_H__
+
+#include <NGExtensions/NSString+Escaping.h>
+
+@interface GCSStringFormatter : NSObject < NGStringEscaping >
+{
+}
+
++ (id)sharedFormatter;
+
+- (NSString *)stringByFormattingString:(NSString *)_s;
+
+@end
+
+#endif /* __GDLContentStore_GCSStringFormatter_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "GCSStringFormatter.h"
+#include "common.h"
+
+@implementation GCSStringFormatter
+
+static NSCharacterSet *escapeSet = nil;
+
++ (void)initialize {
+ static BOOL didInit = NO;
+
+ if(didInit)
+ return;
+
+ didInit = YES;
+ escapeSet =
+ [[NSCharacterSet characterSetWithCharactersInString:@"\\'"] retain];
+}
+
++ (id)sharedFormatter {
+ static id sharedInstance = nil;
+ if(!sharedInstance) {
+ sharedInstance = [[self alloc] init];
+ }
+ return sharedInstance;
+}
+
+- (NSString *)stringByFormattingString:(NSString *)_s {
+ NSString *s;
+
+ s = [_s stringByEscapingCharactersFromSet:escapeSet
+ usingStringEscaping:self];
+ return [NSString stringWithFormat:@"'%@'", s];
+}
+
+- (NSString *)stringByEscapingString:(NSString *)_s {
+ if([_s isEqualToString:@"\\"]) {
+ return @"\\\\"; /* easy ;-) */
+ }
+ return @"\\'";
+}
+
+@end /* GCSStringFormatter */
--- /dev/null
+# GNUstep makefiles
+
+-include ../../config.make
+include ../common.make
+-include ../Version
+include ./Version
+
+LIBRARY_NAME = libGDLContentStore
+TOOL_NAME = gcs_ls gcs_mkdir gcs_cat gcs_recreatequick gcs_gensql
+
+libGDLContentStore_HEADER_FILES_DIR = .
+libGDLContentStore_HEADER_FILES_INSTALL_DIR = /GDLContentStore
+
+libGDLContentStore_HEADER_FILES += \
+ NSURL+GCS.h \
+ EOAdaptorChannel+GCS.h \
+ \
+ GCSContext.h \
+ GCSFieldInfo.h \
+ GCSFolder.h \
+ GCSFolderManager.h \
+ GCSFolderType.h \
+ GCSChannelManager.h \
+ GCSFieldExtractor.h \
+ GCSStringFormatter.h \
+
+libGDLContentStore_OBJC_FILES += \
+ NSURL+GCS.m \
+ EOAdaptorChannel+GCS.m \
+ EOQualifier+GCS.m \
+ \
+ GCSContext.m \
+ GCSFieldInfo.m \
+ GCSFolder.m \
+ GCSFolderManager.m \
+ GCSFolderType.m \
+ GCSChannelManager.m \
+ GCSFieldExtractor.m \
+ GCSStringFormatter.m \
+
+gcs_ls_OBJC_FILES += gcs_ls.m
+gcs_mkdir_OBJC_FILES += gcs_mkdir.m
+gcs_cat_OBJC_FILES += gcs_cat.m
+gcs_gensql_OBJC_FILES += gcs_gensql.m
+gcs_recreatequick_OBJC_FILES += gcs_recreatequick.m
+
+-include GNUmakefile.preamble
+include $(GNUSTEP_MAKEFILES)/library.make
+include $(GNUSTEP_MAKEFILES)/tool.make
+-include GNUmakefile.postamble
--- /dev/null
+# compilation settings
+
+libGDLContentStore_LIBRARIES_DEPEND_UPON += \
+ -lGDLAccess \
+ -lNGExtensions \
+ -lEOControl \
+ -lSaxObjC
+
+GCS_TOOL_LIBS += \
+ -lGDLContentStore \
+ -lGDLAccess \
+ -lNGExtensions -lEOControl \
+ -lDOM -lSaxObjC
+
+gcs_ls_TOOL_LIBS += $(GCS_TOOL_LIBS)
+gcs_mkdir_TOOL_LIBS += $(GCS_TOOL_LIBS)
+gcs_cat_TOOL_LIBS += $(GCS_TOOL_LIBS)
+gcs_recreatequick_TOOL_LIBS += $(GCS_TOOL_LIBS)
+gcs_gensql_TOOL_LIBS += $(GCS_TOOL_LIBS)
+
+ADDITIONAL_INCLUDE_DIRS += -I. -I..
+
+ADDITIONAL_LIB_DIRS += \
+ -L./$(GNUSTEP_OBJ_DIR) \
+ -L../GDLAccess/$(GNUSTEP_OBJ_DIR) \
+ -L/usr/local/lib -L/usr/lib
+
+SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
+
+ifeq ($(FOUNDATION_LIB),apple)
+libGDLContentStore_PREBIND_ADDR="0xC7700000"
+libGDLContentStore_LDFLAGS += -seg1addr $(libGDLContentStore_PREBIND_ADDR)
+endif
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __GDLContentStore_NSURL_GCS_H__
+#define __GDLContentStore_NSURL_GCS_H__
+
+#import <Foundation/NSURL.h>
+
+/*
+ "Database URLs"
+
+ We use the schema:
+ postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename]
+*/
+
+@interface NSURL(GCS)
+
+- (NSString *)gcsDatabaseName;
+- (NSString *)gcsTableName;
+
+@end
+
+#endif /* __GDLContentStore_NSURL_GCS_H__ */
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "NSURL+GCS.h"
+#include "common.h"
+
+@implementation NSURL(GCS)
+
+- (NSString *)gcsPathComponent:(unsigned)_idx {
+ NSString *p;
+ NSArray *pcs;
+ unsigned len;
+
+ p = [self path];
+ if ([p length] == 0)
+ return nil;
+
+ pcs = [p componentsSeparatedByString:@"/"];
+ if ((len = [pcs count]) == 0)
+ return nil;
+ if (len <= _idx)
+ return nil;
+ return [pcs objectAtIndex:_idx];
+}
+
+- (NSString *)gcsDatabaseName {
+ return [self gcsPathComponent:1];
+}
+- (NSString *)gcsTableName {
+ return [[self path] lastPathComponent];
+}
+
+@end /* NSURL(GCS) */
--- /dev/null
+Storage Backend
+===============
+
+The storage backend implements the "low level" folder abstraction, which is
+basically an arbitary "BLOB" containing some document. The feature is that
+we extract "quick access" / "searchable" attributes from the document content.
+
+Further it contains the "folder management" API, as named folders can be stored
+in different databases.
+Note: we need a way to tell where "new" folders should be created
+Note: to sync with LDAP we need to periodically delete or archive old folders
+
+Folders have associated a type (like 'calendar') which defines the query
+attributes and serialization format.
+
+TODO
+====
+- fix some OCS naming
+ - defaults
+ - lookup directories
+- hierarchies deeper than 4 (properly filter on path in OCS)
+
+Open Questions
+==============
+
+System-meta-data in the blob-table or in the quick-table?
+- master data belongs into the blob table
+- could be regular 'NSxxx' keys to differentiate meta data from
+
+Class Hierarchy
+===============
+
+ [NSObject]
+ OCSContext - tracking context
+ OCSFolder - represents a single folder
+ OCSFolderManager - manages folders
+ OCSFolderType - the mapping info for a specific folder-type
+ OCSFieldInfo - mapping info for one 'quick field'
+ OCSChannelManager - maintains EOAdaptorChannel objects
+
+ TBD:
+ - field 'extractor'
+ - field 'value' (eg array values for participants?)
+ - BLOB archiver/unarchiver
+
+Defaults
+========
+
+ OCSFolderInfoURL - the DB URL where the folder-info table is located
+ eg: http://OGo:OGo@localhost/test/folder_info
+
+ OCSFolderManagerDebugEnabled - enable folder-manager debug logs
+ OCSFolderManagerSQLDebugEnabled - enable folder-manager SQL gen debug logs
+
+ OCSChannelManagerDebugEnabled - enable channel debug pooling logs
+ OCSChannelManagerPoolDebugEnabled - debug pool handle allocation
+
+ OCSChannelExpireAge - if that age in seconds is exceeded, a channel
+ will be removed from the pool
+ OCSChannelCollectionTimer - time in seconds. each n-seconds the pool will be
+ checked for channels too old
+
+ [PGDebugEnabled] - enable PostgreSQL adaptor debugging
+
+URLs
+====
+
+ "Database URLs"
+
+ We use the schema:
+ postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename]
+
+Support Tools
+=============
+
+- tools we need:
+ - one to recreate a quick table based on the blob table
+
+Notes
+=====
+
+- need to use http:// URLs for connect info, until generic URLs in
+ libFoundation are fixed (the parses breaks on the login/password parts)
+
+QA
+==
+
+Q: Why do we use two tables, we could store the quick columns in the blob?
+==
+They could be in the same table. We considered using separate tables since the
+quick table is likely to be recreated now and then if BLOB indexing
+requirements change.
+Actually one could even use different _quick tables which share a common BLOB
+table.
+(a quick table is nothing more than a database index and like with DB indexes
+ multiple ones for different requirements can make sense).
+
+Further it might improve caching behaviour for row based caches (the quick
+table is going to be queried much more often) - not sure whether this is
+relevant with PostgreSQL, probably not?
+
+Q: Can we use a VARCHAR primary key?
+==
+We asked in the postgres IRC channel and apparently the performance penalty of
+string primary keys isn't big.
+We could also use an 'internal' int sequence in addition (might be useful for
+supporting ZideLook)
+Motivation: the 'iCalendar' ID is a string and usually looks like a GUID.
+
+Q: Why using VARCHAR instead of TEXT in the BLOB?
+==
+To quote PostgreSQL documentation:
+"There are no performance differences between these three types, apart from
+ the increased storage size when using the blank-padded type."
+So varchar(xx) is just a large TEXT. Since we intend to store mostly small
+snippets of data (tiny XML fragments), we considered VARCHAR the more
+appropriate type.
--- /dev/null
+# Version file
+
+MAJOR_VERSION:=4
+MINOR_VERSION:=5
+SUBMINOR_VERSION:=26
+
+# v4.5.26 does not require libNGiCal anymore
+# v0.9.19 requires libNGiCal v4.5.40
+# v0.9.18 requires libNGiCal v4.5.38
+# v0.9.17 requires libNGiCal v4.5.37
+# v0.9.11 requires libFoundation v1.0.63
+# v0.9.11 requires libNGExtensions v4.3.125
+# v0.9.7 requires libGDLAccess v1.1.35
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+// $Id$
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSURL.h>
+
+#include <NGExtensions/NGExtensions.h>
+
+#if NeXT_RUNTIME || APPLE_RUNTIME
+# define objc_free(__mem__) free(__mem__)
+# define objc_malloc(__size__) malloc(__size__)
+# define objc_calloc(__cnt__, __size__) calloc(__cnt__, __size__)
+# define objc_realloc(__ptr__, __size__) realloc(__ptr__, __size__)
+# ifndef sel_eq
+# define sel_eq(sela,selb) (sela==selb?YES:NO)
+# endif
+#endif
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSUserDefaults, NSArray;
+@class GCSFolderManager;
+
+@interface Tool : NSObject
+{
+ NSUserDefaults *ud;
+ GCSFolderManager *folderManager;
+}
+
++ (int)runWithArgs:(NSArray *)_args;
+- (int)run;
+
+@end
+
+#include <GDLContentStore/GCSFolder.h>
+#include <GDLContentStore/GCSFolderManager.h>
+#include "common.h"
+
+@implementation Tool
+
+- (id)init {
+ if ((self = [super init])) {
+ self->ud = [[NSUserDefaults standardUserDefaults] retain];
+ self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
+ }
+ return self;
+}
+- (void)dealloc {
+ [self->ud release];
+ [self->folderManager release];
+ [super dealloc];
+}
+
+/* operation */
+
+- (int)runOnPath:(NSString *)_path {
+ GCSFolder *folder;
+ NSString *dirname, *filename;
+ NSString *content;
+
+ dirname = [_path stringByDeletingLastPathComponent];
+ filename = [_path lastPathComponent];
+
+ if ((folder = [self->folderManager folderAtPath:dirname]) == nil) {
+ [self logWithFormat:@"did not find folder for file: '%@'", dirname];
+ return 1;
+ }
+
+ if ((content = [folder fetchContentWithName:filename]) == nil) {
+ [self logWithFormat:@"did not find file: '%@'", _path];
+ return 1;
+ }
+
+ printf("%s\n", [content cString]);
+
+ return 0;
+}
+
+- (int)run {
+ NSEnumerator *e;
+ NSString *path;
+
+ [self logWithFormat:@"manager: %@", self->folderManager];
+
+ if (![self->folderManager canConnect]) {
+ [self logWithFormat:@"cannot connect folder-info database!"];
+ return 1;
+ }
+
+ e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
+ objectEnumerator];
+ [e nextObject]; // skip tool name
+
+ while ((path = [e nextObject]))
+ [self runOnPath:path];
+
+ return 0;
+}
++ (int)runWithArgs:(NSArray *)_args {
+ return [(Tool *)[[[self alloc] init] autorelease] run];
+}
+
+@end /* Tool */
+
+int main(int argc, char **argv, char **env) {
+ NSAutoreleasePool *pool;
+ int rc;
+
+ pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+ [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+
+ rc = [Tool runWithArgs:
+ [[NSProcessInfo processInfo] argumentsWithoutDefaults]];
+
+ [pool release];
+ return rc;
+}
--- /dev/null
+/*
+ Copyright (C) 2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSUserDefaults, NSArray;
+@class GCSFolderManager;
+
+@interface Tool : NSObject
+{
+ NSUserDefaults *ud;
+}
+
++ (int)runWithArgs:(NSArray *)_args;
+- (int)run;
+
+@end
+
+#include <GDLContentStore/GCSFolderType.h>
+#include "common.h"
+
+@implementation Tool
+
+- (id)init {
+ if ((self = [super init])) {
+ self->ud = [[NSUserDefaults standardUserDefaults] retain];
+ }
+ return self;
+}
+- (void)dealloc {
+ [self->ud release];
+ [super dealloc];
+}
+
+/* operation */
+
+- (int)runOnTable:(NSString *)_tableName typeName:(NSString *)_typeName {
+ GCSFolderType *folderType;
+
+ if ((folderType = [GCSFolderType folderTypeWithName:_typeName]) != nil) {
+ NSString *s;
+
+ s = [folderType sqlQuickCreateWithTableName:_tableName];
+
+ fwrite([s cString], 1, [s cStringLength], stdout);
+ printf("\n");
+ }
+ else {
+ fprintf(stderr, "ERROR: did not find GCS type: '%s'\n",
+ [_typeName cString]);
+ }
+
+ return 0;
+}
+
+- (int)run {
+ NSEnumerator *e;
+ NSString *tableName, *typeName;
+
+ e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
+ objectEnumerator];
+ [e nextObject]; // skip tool name
+
+ while ((tableName = [e nextObject]) != nil) {
+ typeName = [e nextObject];
+ if (typeName == nil) {
+ [self logWithFormat:@"got tablename '%@' but no type?!", tableName];
+ break;
+ }
+
+ [self runOnTable:tableName typeName:typeName];
+ }
+
+ return 0;
+}
++ (int)runWithArgs:(NSArray *)_args {
+ return [(Tool *)[[[self alloc] init] autorelease] run];
+}
+
+@end /* Tool */
+
+int main(int argc, char **argv, char **env) {
+ NSAutoreleasePool *pool;
+ int rc;
+
+ pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+ [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+
+ rc = [Tool runWithArgs:
+ [[NSProcessInfo processInfo] argumentsWithoutDefaults]];
+
+ [pool release];
+ return rc;
+}
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSUserDefaults, NSArray;
+@class GCSFolderManager;
+
+@interface Tool : NSObject
+{
+ NSUserDefaults *ud;
+ GCSFolderManager *folderManager;
+}
+
++ (int)runWithArgs:(NSArray *)_args;
+- (int)run;
+
+@end
+
+#include <GDLContentStore/GCSFolder.h>
+#include <GDLContentStore/GCSFolderManager.h>
+#include "common.h"
+
+@implementation Tool
+
+- (id)init {
+ if ((self = [super init])) {
+ self->ud = [[NSUserDefaults standardUserDefaults] retain];
+ self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
+ }
+ return self;
+}
+- (void)dealloc {
+ [self->ud release];
+ [self->folderManager release];
+ [super dealloc];
+}
+
+/* operation */
+
+- (int)runOnPath:(NSString *)_path {
+ NSArray *subfolders;
+ unsigned i, count;
+ GCSFolder *folder;
+
+ [self logWithFormat:@"ls path: '%@'", _path];
+
+#if 0 // we do not necessarily need the whole hierarchy
+ if (![self->folderManager folderExistsAtPath:_path])
+ [self logWithFormat:@"folder does not exist: '%@'", _path];
+#endif
+
+ subfolders = [self->folderManager
+ listSubFoldersAtPath:_path
+ recursive:[ud boolForKey:@"r"]];
+ if (subfolders == nil) {
+ [self logWithFormat:@"cannot list folder: '%@'", _path];
+ return 1;
+ }
+
+ for (i = 0, count = [subfolders count]; i < count; i++) {
+ printf("%s\n", [[subfolders objectAtIndex:i] cString]);
+ }
+
+ folder = [self->folderManager folderAtPath:_path];
+
+ if ([folder isNotNull]) {
+ NSLog(@"folder: %@", folder);
+
+ NSLog(@" can%s connect store: %@", [folder canConnectStore] ? "" : "not",
+ [[folder location] absoluteString]);
+ NSLog(@" can%s connect quick: %@", [folder canConnectQuick] ? "" : "not",
+ [[folder quickLocation] absoluteString]);
+ }
+ else {
+ NSLog(@"ERROR: could not create folder object for path: '%@'", _path);
+ }
+
+ return 0;
+}
+
+- (int)run {
+ NSEnumerator *e;
+ NSString *path;
+
+ [self logWithFormat:@"manager: %@", self->folderManager];
+
+ if (![self->folderManager canConnect]) {
+ [self logWithFormat:@"cannot connect folder-info database!"];
+ return 1;
+ }
+
+ e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
+ objectEnumerator];
+ [e nextObject]; // skip tool name
+
+ while ((path = [e nextObject]) != nil)
+ [self runOnPath:path];
+
+ return 0;
+}
++ (int)runWithArgs:(NSArray *)_args {
+ return [(Tool *)[[[self alloc] init] autorelease] run];
+}
+
+@end /* Tool */
+
+int main(int argc, char **argv, char **env) {
+ NSAutoreleasePool *pool;
+ int rc;
+
+ pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+ [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+
+ rc = [Tool runWithArgs:
+ [[NSProcessInfo processInfo] argumentsWithoutDefaults]];
+
+ [pool release];
+ return rc;
+}
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSUserDefaults, NSArray;
+@class GCSFolderManager;
+
+@interface Tool : NSObject
+{
+ NSUserDefaults *ud;
+ GCSFolderManager *folderManager;
+}
+
++ (int)runWithArgs:(NSArray *)_args;
+- (int)run;
+
+@end
+
+#include <GDLContentStore/GCSFolder.h>
+#include <GDLContentStore/GCSFolderManager.h>
+#include "common.h"
+
+@implementation Tool
+
+- (id)init {
+ if ((self = [super init])) {
+ self->ud = [[NSUserDefaults standardUserDefaults] retain];
+ self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
+ }
+ return self;
+}
+- (void)dealloc {
+ [self->ud release];
+ [self->folderManager release];
+ [super dealloc];
+}
+
+/* operation */
+
+- (int)runOnPath:(NSString *)_path type:(NSString *)_type {
+ NSException *error;
+
+ [self logWithFormat:@"mkdir %@ at path: '%@'", _type, _path];
+
+ if ([self->folderManager folderExistsAtPath:_path]) {
+ [self logWithFormat:@"folder already exist at path: '%@'", _path];
+ return 1;
+ }
+
+ if ((error = [self->folderManager createFolderOfType:_type atPath:_path])) {
+ [self logWithFormat:@"creation of folder %@ at %@ failed: %@",
+ _type, _path, error];
+ return 1;
+ }
+
+ if ([self->folderManager folderExistsAtPath:_path])
+ [self logWithFormat:@"CREATED."];
+ else
+ [self logWithFormat:@"cannot find fresh folder?"];
+
+ return 0;
+}
+
+- (int)run {
+ NSEnumerator *e;
+ NSString *type;
+ NSString *path;
+
+ [self logWithFormat:@"manager: %@", self->folderManager];
+
+ if (![self->folderManager canConnect]) {
+ [self logWithFormat:@"cannot connect folder-info database!"];
+ return 1;
+ }
+
+ e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
+ objectEnumerator];
+ [e nextObject]; // skip tool name
+
+ type = [[[e nextObject] copy] autorelease];
+
+ while ((path = [e nextObject]))
+ [self runOnPath:path type:type];
+
+ return 0;
+}
++ (int)runWithArgs:(NSArray *)_args {
+ return [(Tool *)[[[self alloc] init] autorelease] run];
+}
+
+@end /* Tool */
+
+int main(int argc, char **argv, char **env) {
+ NSAutoreleasePool *pool;
+ int rc;
+
+ pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+ [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+
+ rc = [Tool runWithArgs:
+ [[NSProcessInfo processInfo] argumentsWithoutDefaults]];
+
+ [pool release];
+ return rc;
+}
--- /dev/null
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo 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, or (at your option) any
+ later version.
+
+ OGo 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 OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/NSObject.h>
+
+@class NSUserDefaults, NSArray;
+@class GCSFolderManager;
+
+@interface Tool : NSObject
+{
+ NSUserDefaults *ud;
+ GCSFolderManager *folderManager;
+}
+
++ (int)runWithArgs:(NSArray *)_args;
+- (int)run;
+
+@end
+
+#include <GDLContentStore/GCSFolder.h>
+#include <GDLContentStore/GCSFolderManager.h>
+#include "common.h"
+
+@implementation Tool
+
+- (id)init {
+ if ((self = [super init])) {
+ self->ud = [[NSUserDefaults standardUserDefaults] retain];
+ self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
+ }
+ return self;
+}
+- (void)dealloc {
+ [self->ud release];
+ [self->folderManager release];
+ [super dealloc];
+}
+
+/* operation */
+
+- (int)runOnPath:(NSString *)_path {
+ GCSFolder *folder;
+
+ [self logWithFormat:@"update quick from store at path: '%@'", _path];
+
+ if (![self->folderManager folderExistsAtPath:_path]) {
+ [self logWithFormat:@"no folder exist at path: '%@'", _path];
+ return 1;
+ }
+
+ if ((folder = [self->folderManager folderAtPath:_path]) == nil) {
+ [self logWithFormat:@"got no folder object for path: '%@'", _path];
+ return 2;
+ }
+ [self logWithFormat:@" folder: %@", folder];
+
+ // TODO:
+ [self logWithFormat:@" should recreate folder .."];
+
+ return 0;
+}
+
+- (int)run {
+ NSEnumerator *e;
+ NSString *path;
+
+ [self logWithFormat:@"manager: %@", self->folderManager];
+
+ if (![self->folderManager canConnect]) {
+ [self logWithFormat:@"cannot connect folder-info database!"];
+ return 1;
+ }
+
+ e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
+ objectEnumerator];
+ [e nextObject]; // skip tool name
+
+ while ((path = [e nextObject]))
+ [self runOnPath:path];
+
+ return 0;
+}
++ (int)runWithArgs:(NSArray *)_args {
+ return [(Tool *)[[[self alloc] init] autorelease] run];
+}
+
+@end /* Tool */
+
+int main(int argc, char **argv, char **env) {
+ NSAutoreleasePool *pool;
+ int rc;
+
+ pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+ [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+
+ rc = [Tool runWithArgs:
+ [[NSProcessInfo processInfo] argumentsWithoutDefaults]];
+
+ [pool release];
+ return rc;
+}