From 3ae3fcff25067a853e1639f8e9f14d51f9241256 Mon Sep 17 00:00:00 2001 From: francis Date: Thu, 11 Oct 2007 14:49:33 +0000 Subject: [PATCH] git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1184 d1b88da0-ebda-0310-925b-ed51d893ca5b --- SOPE/sope-gdl1/GDLContentStore/COPYING | 482 ++++++++ SOPE/sope-gdl1/GDLContentStore/COPYRIGHT | 4 + SOPE/sope-gdl1/GDLContentStore/ChangeLog | 329 +++++ .../GDLContentStore/EOAdaptorChannel+GCS.h | 40 + .../GDLContentStore/EOAdaptorChannel+GCS.m | 50 + .../GDLContentStore/EOQualifier+GCS.h | 35 + .../GDLContentStore/EOQualifier+GCS.m | 156 +++ .../GDLContentStore/GCSChannelManager.h | 58 + .../GDLContentStore/GCSChannelManager.m | 516 ++++++++ SOPE/sope-gdl1/GDLContentStore/GCSContext.h | 39 + SOPE/sope-gdl1/GDLContentStore/GCSContext.m | 26 + .../GDLContentStore/GCSFieldExtractor.h | 37 + .../GDLContentStore/GCSFieldExtractor.m | 31 + SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h | 61 + SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m | 127 ++ SOPE/sope-gdl1/GDLContentStore/GCSFolder.h | 128 ++ SOPE/sope-gdl1/GDLContentStore/GCSFolder.m | 1101 +++++++++++++++++ .../GDLContentStore/GCSFolderManager.h | 83 ++ .../GDLContentStore/GCSFolderManager.m | 853 +++++++++++++ .../sope-gdl1/GDLContentStore/GCSFolderType.h | 82 ++ .../sope-gdl1/GDLContentStore/GCSFolderType.m | 198 +++ .../GDLContentStore/GCSStringFormatter.h | 37 + .../GDLContentStore/GCSStringFormatter.m | 63 + SOPE/sope-gdl1/GDLContentStore/GNUmakefile | 77 ++ .../GDLContentStore/GNUmakefile.preamble | 78 ++ SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h | 41 + SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m | 51 + SOPE/sope-gdl1/GDLContentStore/README | 117 ++ SOPE/sope-gdl1/GDLContentStore/Version | 14 + SOPE/sope-gdl1/GDLContentStore/common.h | 36 + SOPE/sope-gdl1/GDLContentStore/fhs.make | 44 + SOPE/sope-gdl1/GDLContentStore/gcs_cat.m | 122 ++ SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m | 114 ++ SOPE/sope-gdl1/GDLContentStore/gcs_ls.m | 140 +++ SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m | 126 ++ .../GDLContentStore/gcs_recreatequick.m | 121 ++ 36 files changed, 5617 insertions(+) create mode 100644 SOPE/sope-gdl1/GDLContentStore/COPYING create mode 100644 SOPE/sope-gdl1/GDLContentStore/COPYRIGHT create mode 100644 SOPE/sope-gdl1/GDLContentStore/ChangeLog create mode 100644 SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSContext.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSContext.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolder.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolder.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/GNUmakefile create mode 100644 SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble create mode 100644 SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/README create mode 100644 SOPE/sope-gdl1/GDLContentStore/Version create mode 100644 SOPE/sope-gdl1/GDLContentStore/common.h create mode 100644 SOPE/sope-gdl1/GDLContentStore/fhs.make create mode 100644 SOPE/sope-gdl1/GDLContentStore/gcs_cat.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/gcs_ls.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m create mode 100644 SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m diff --git a/SOPE/sope-gdl1/GDLContentStore/COPYING b/SOPE/sope-gdl1/GDLContentStore/COPYING new file mode 100644 index 00000000..161a3d1d --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/COPYING @@ -0,0 +1,482 @@ + 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. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/SOPE/sope-gdl1/GDLContentStore/COPYRIGHT b/SOPE/sope-gdl1/GDLContentStore/COPYRIGHT new file mode 100644 index 00000000..1053b2aa --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/COPYRIGHT @@ -0,0 +1,4 @@ +Copyright (C) 2004 SKYRIX Software AG + + +Contact: info@skyrix.com diff --git a/SOPE/sope-gdl1/GDLContentStore/ChangeLog b/SOPE/sope-gdl1/GDLContentStore/ChangeLog new file mode 100644 index 00000000..bdd4c43b --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/ChangeLog @@ -0,0 +1,329 @@ +2007-08-29 Wolfgang Sourdeau + + * EOQualifier+GCS.m: rewrote comparison code, now uses UPPER instead of + the PostgreSQL specific ILIKE. Fixes OGo bug #1906 (v4.7.49) + +2007-07-20 Helge Hess + + * GCSFolderManager.m: added 'some' rollback after an error (v4.7.48) + +2007-07-20 Wolfgang Sourdeau + + * GCSFolderManager.m: fixed a bug in last check, DROP TABLE is allowed + to fail in the given context (bug #1883) (v4.7.47) + +2007-07-11 Helge Hess + + * GCSFolderManager.m: added some error checking, plenty of open ends + pending (eg folder creation not wrapped in a transaction) (v4.7.46) + +2007-06-29 Wolfgang Sourdeau + + * GCSFolderManager.m: fixed folder creation to populate empty path + fields with NULLs (OGo bug #1883) (v4.7.45) + +2007-04-25 Wolfgang Sourdeau + + * GCSFolder.[hm]: added methods to delete ACL records (OGo bug #1866) + (v4.7.44) + +2007-04-22 Helge Hess + + * GCSChannelManager.m: improved error log (v4.7.43) + +2007-04-17 Helge Hess + + * fixed a few GNUstep compilation warnings (v4.7.42) + +2007-03-21 Wolfgang Sourdeau + + * GCSFolder.[hm], GCSFolderManager.[hm]: added ability to create and + delete GCS folders programmatically (OGo bug #1850) (v4.7.41) + +2007-02-12 Helge Hess + + * GCSFolder.m: fixed a gnustep-base compilation warning (v4.7.40) + +2007-02-09 Helge Hess + + * use -errorWithFormat:, fixed a few logging crashes (incomplete format + strings) (v4.5.39) + +2007-02-08 Wolfgang Sourdeau + + * GCSFolder.m: added a gnustep-base hack to properly format bool + numbers for SQL. Base returns YES or NO in -stringValue while + libFoundation/NGExt returns 0 or 1 (v4.5.39) + +2007-01-15 Wolfgang Sourdeau + + * GCSFolder.[hm], GCSFolderManager.m: added support for content table + ACLs (v4.5.38) + +2006-08-31 Wolfgang Sourdeau + + * EOQualifier+GCS.m: added support for OR qualifiers and for case + insensitive-like qualifiers on PostgreSQL (v4.5.37) + +2006-07-04 Helge Hess + + * use %p for pointer formats, fixed gcc 4.1 warnings (v4.5.36) + +2005-08-16 Helge Hess + + * GNUmakefile, GNUmakefile.preamble: added OSX framework compilation + (v4.5.35) + +2005-07-23 Sebastian Reitenbach + + * GNUmakefile.preamble: added OpenBSD linking flags (v4.5.34) + +2005-07-13 Helge Hess + + * GCSFolder.h: added -versionOfContentWithName: method to header file + (v4.5.33) + + * GCSFolder.m: return a proper exception if the extractor was unable to + create a quickrow for a given content object (v4.5.32) + + * GCSFolder.m: added -writeContent:toName:baseVersion: to support + consistent update operations (eg using etags), properly increase + content object version on update operations (v4.5.31) + + * GCSFolderManager.m, GCSFolder.m: changed not to use EOF + attribute-name 'beautification', eg 'c_name' will stay 'c_name' + instead of being transformed into 'cName' (v4.5.30) + +2005-07-11 Helge Hess + + * GCSFolderManager.m: added automatic discovery of folder types by + scanning for .ocs files (v4.5.29) + +2005-04-25 Helge Hess + + * fixed gcc 4.0 warnings (v4.5.28) + +2005-03-21 Helge Hess + + * GNUmakefile: added FHS support (v4.5.27) + +2005-03-20 Helge Hess + + * 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 + + * appointment.ocs: added missing 'partstates' field (v0.9.25) + +2005-03-04 Helge Hess + + * 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 + + * OCSFolderManager.m: fixed a bug in subfolder listing (v0.9.23) + +2005-03-01 Marcus Mueller + + * 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 + + * OCSFolder.m: added support for storing content and quick info in + the same table (untested) (v0.9.21) + +2005-02-21 Helge Hess + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * sql: updated all generation scripts to the latest version (v0.9.15) + +2004-12-09 Marcus Mueller + + * 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 + + * 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 + + * 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 + + * fixed compilation on MacOSX (v0.9.10) + +2004-09-10 Helge Hess + + * 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 + + * GNUmakefile: install type models into $(GNUSTEP_USER_ROOT) (v0.9.8) + +2004-08-27 Helge Hess + + * 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 + + * 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 + + * GNUmakefile: automatically install OCSTypeModels (v0.9.4) + +2004-08-15 Helge Hess + + * OCSFolder.m: added content deletion (v0.9.3) + + * OCSFolder.m: added sanity check to store method (v0.9.2) + +2004-08-14 Helge Hess + + * 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 + + * OCSChannelManager.m: fixed a bug in the channel GC which resulted + in an exception during the GC NSTimer + +2004-07-16 Helge Hess + + * improved error handling in various files + +2004-07-02 Helge Hess + + * OCSChannelManager.m: added garbage collector for channel pools + +2004-06-30 Helge Hess + + * 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 + + * ocs_cat.m, ocs_ls.m, ocs_mkdir.m: fixed for gnustep compile. + +2004-06-29 Helge Hess + + * created ChangeLog + diff --git a/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h b/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h new file mode 100644 index 00000000..da28e551 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h @@ -0,0 +1,40 @@ +/* + 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 + +@protocol GCSEOAdaptorChannel + +- (NSException *) createGCSFolderTableWithName: (NSString *) tableName; +- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName; + +@end + +@interface EOAdaptorChannel(GCS) + +- (BOOL)tableExistsWithName:(NSString *)_tableName; + +@end + +#endif /* __GDLContentStore_EOAdaptorChannel_GCS_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m b/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m new file mode 100644 index 00000000..23e15617 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m @@ -0,0 +1,50 @@ +/* + 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) */ diff --git a/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h b/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h new file mode 100644 index 00000000..27fae8d7 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h @@ -0,0 +1,35 @@ +/* + 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 + +@class NSMutableString; + +@interface EOQualifier(GCS) + +- (void)_gcsAppendToString:(NSMutableString *)_ms; + +@end + +#endif /* __GDLContentStore_EOQualifier_GCS_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m b/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m new file mode 100644 index 00000000..88343350 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m @@ -0,0 +1,156 @@ +/* + Copyright (C) 2004-2007 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)_appendOrQualifier:(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:@" OR "]; + if (count > 1) [_ms appendString:@"("]; + [[qs objectAtIndex:i] _gcsAppendToString:_ms]; + if (count > 1) [_ms appendString:@")"]; + } +} +- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q + toString:(NSMutableString *)_ms +{ + id val; + NSString *qKey, *qOperator, *qValue, *qFormat; + BOOL isCI; + + qKey = [_q key]; + + if ((val = [_q value])) { + SEL op = [_q selector]; + + if ([val isNotNull]) { + isCI = NO; + + if (sel_eq(op, EOQualifierOperatorEqual)) + qOperator = @"="; + else if (sel_eq(op, EOQualifierOperatorNotEqual)) + qOperator = @"!="; + else if (sel_eq(op, EOQualifierOperatorLessThan)) + qOperator = @"<"; + else if (sel_eq(op, EOQualifierOperatorGreaterThan)) + qOperator = @">"; + else if (sel_eq(op, EOQualifierOperatorLessThanOrEqualTo)) + qOperator = @"<="; + else if (sel_eq(op, EOQualifierOperatorGreaterThanOrEqualTo)) + qOperator = @">="; + else if (sel_eq(op, EOQualifierOperatorLike)) + qOperator = @"LIKE"; + else if (sel_eq(op, EOQualifierOperatorCaseInsensitiveLike)) { + isCI = YES; + qOperator = @"LIKE"; + } + else { + [self errorWithFormat:@"%s: unsupported operation for null: %@", + __PRETTY_FUNCTION__, NSStringFromSelector(op)]; + } + + if ([val isKindOfClass:[NSNumber class]]) + qValue = [val stringValue]; + else if ([val isKindOfClass:[NSString class]]) { + qValue = [NSString stringWithFormat: @"'%@'", val]; + } + else { + [self errorWithFormat:@"%s: unsupported value class: %@", + __PRETTY_FUNCTION__, NSStringFromClass([val class])]; + } + } + else { + if (sel_eq(op, EOQualifierOperatorEqual)) { + qOperator = @"IS"; + qValue = @"NULL"; + } + else if (sel_eq(op, EOQualifierOperatorEqual)) { + qOperator = @"IS NOT"; + qValue = @"NULL"; + } + else { + [self errorWithFormat:@"%s: invalid operation for null: %@", + __PRETTY_FUNCTION__, NSStringFromSelector(op)]; + } + } + } + else { + qOperator = @"IS"; + qValue = @"NULL"; + } + + if (isCI) + qFormat = @"UPPER(%@) %@ UPPER(%@)"; + else + qFormat = @"%@ %@ %@"; + + [_ms appendFormat: qFormat, qKey, qOperator, qValue]; +} + +- (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:[EOOrQualifier class]]) + [self _appendOrQualifier:(id)_q toString:_ms]; + else if ([_q isKindOfClass:[EOKeyValueQualifier class]]) + [self _appendKeyValueQualifier:(id)_q toString:_ms]; + else + [self errorWithFormat:@"unknown qualifier: %@", _q]; +} + +- (void)_gcsAppendToString:(NSMutableString *)_ms { + [self _appendQualifier:self toString:_ms]; +} + +@end /* EOQualifier(GCS) */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h b/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h new file mode 100644 index 00000000..1e8bae60 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h @@ -0,0 +1,58 @@ +/* + 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 + +/* + 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__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m b/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m new file mode 100644 index 00000000..e1983909 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m @@ -0,0 +1,516 @@ +/* + 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 +#include +#include +#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 errorWithFormat:@"cannot handle URL: %@", _url]; + return nil; + } + + condict = [self connectionDictionaryForURL:_url]; + + if ((adaptor = [EOAdaptor adaptorWithName:adaptorName]) == nil) { + [self errorWithFormat:@"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 errorWithFormat:@"could not create adaptor context!"]; + return nil; + } + if ((adChannel = [adContext createAdaptorChannel]) == nil) { + [self errorWithFormat:@"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 errorWithFormat:@"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%p[%@]:", 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 (%@ vs %@)", + [self->url host], [_url 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%p[%@]:", self, NSStringFromClass([self class])]; + + [ms appendFormat:@" channel=0x%p", 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSContext.h b/SOPE/sope-gdl1/GDLContentStore/GCSContext.h new file mode 100644 index 00000000..f386e658 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSContext.h @@ -0,0 +1,39 @@ +/* + 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 + +/* + GCSContext + + Context passed to all operations. +*/ + +@interface GCSContext : NSObject +{ +} + +@end + +#endif /* __GDLContentStore_GCSContext_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSContext.m b/SOPE/sope-gdl1/GDLContentStore/GCSContext.m new file mode 100644 index 00000000..17dd4857 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSContext.m @@ -0,0 +1,26 @@ +/* + 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h b/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h new file mode 100644 index 00000000..1b1e64af --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h @@ -0,0 +1,37 @@ +/* + 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 + +@class NSString, NSMutableDictionary; + +@interface GCSFieldExtractor : NSObject +{ +} + +- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content; + +@end + +#endif /* __GDLContentStore_GCSFieldExtractor_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m b/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m new file mode 100644 index 00000000..6ec8af66 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m @@ -0,0 +1,31 @@ +/* + 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h b/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h new file mode 100644 index 00000000..4f87f090 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h @@ -0,0 +1,61 @@ +/* + 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 + +/* + 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__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m b/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m new file mode 100644 index 00000000..96a8578c --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m @@ -0,0 +1,127 @@ +/* + 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])) { + NSDictionary *plist = _plist; + + 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%p[%@]:", self, NSStringFromClass([self class])]; + [self appendAttributesToDescription:ms]; + [ms appendString:@">"]; + return ms; +} + +@end /* GCSFieldInfo */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolder.h b/SOPE/sope-gdl1/GDLContentStore/GCSFolder.h new file mode 100644 index 00000000..7209517e --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolder.h @@ -0,0 +1,128 @@ +/* + Copyright (C) 2004-2007 SKYRIX Software AG + Copyright (C) 2007 Helge Hess + + 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 + +/* + GCSFolder + + TODO: document + + Fixed Quick-Table SQL fields: + - "c_name" (name of the file in the folder) + + Fixed BLOB-Table SQL fields: + - "c_name" (name of the file in the folder) + - "c_content" (content of the file in the folder) + - "c_version" (update revision of the file in the folder) +*/ + +@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; + NSURL *aclLocation; + 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 + aclLocation: (NSURL *)_aloc + folderManager:(GCSFolderManager *)_fm; + +/* accessors */ + +- (NSNumber *)folderId; +- (NSString *)folderName; +- (NSString *)path; +- (NSURL *)location; +- (NSURL *)quickLocation; +- (NSURL *)aclLocation; +- (NSString *)folderTypeName; + +- (GCSFolderManager *)folderManager; +- (GCSChannelManager *)channelManager; + +- (NSString *)storeTableName; +- (NSString *)quickTableName; +- (NSString *)aclTableName; +- (BOOL)isQuickInfoStoredInContentTable; + +/* connection */ + +- (EOAdaptorChannel *)acquireStoreChannel; +- (EOAdaptorChannel *)acquireQuickChannel; +- (EOAdaptorChannel *)acquireAclChannel; +- (void)releaseChannel:(EOAdaptorChannel *)_channel; + +- (BOOL)canConnectStore; +- (BOOL)canConnectQuick; + +/* operations */ + +- (NSArray *)subFolderNames; +- (NSArray *)allSubFolderNames; + +- (NSNumber *)versionOfContentWithName:(NSString *)_name; + +- (NSString *)fetchContentWithName:(NSString *)_name; +- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name + baseVersion:(unsigned int)_baseVersion; +- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name; +- (NSException *)deleteContentWithName:(NSString *)_name; + +- (NSException *)deleteFolder; + +- (NSDictionary *)fetchContentsOfAllFiles; + +- (NSArray *)fetchFields:(NSArray *)_flds + fetchSpecification:(EOFetchSpecification *)_fs; +- (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q; +- (NSArray *)fetchAclMatchingQualifier:(EOQualifier *)_q; +- (void)deleteAclMatchingQualifier:(EOQualifier *)_q; +- (void)deleteAclWithSpecification:(EOFetchSpecification *)_fs; + +@end + +#endif /* __GDLContentStore_GCSFolder_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolder.m b/SOPE/sope-gdl1/GDLContentStore/GCSFolder.m new file mode 100644 index 00000000..be7f22c5 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolder.m @@ -0,0 +1,1101 @@ +/* + Copyright (C) 2004-2007 SKYRIX Software AG + Copyright (C) 2007 Helge Hess + + 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" + +#include +#include +#include +#include + +#define CHECKERROR() \ + if (error) { \ + [[storeChannel adaptorContext] rollbackTransaction]; \ + [[quickChannel adaptorContext] rollbackTransaction]; \ + [self logWithFormat:@"ERROR(%s): cannot %s content : %@", \ + __PRETTY_FUNCTION__, isNewRecord ? "insert" : "update", error]; \ + return error; \ + } \ + +@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 + aclLocation:(NSURL *)_aloc + folderManager:(GCSFolderManager *)_fm +{ + if (![_loc isNotNull]) { + [self errorWithFormat:@"missing quicktable parameter!"]; + [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->aclLocation = [_aloc 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 + aclLocation:nil + folderManager:nil]; +} +- (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId + folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype + location:(NSURL *)_loc quickLocation:(NSURL *)_qloc + folderManager:(GCSFolderManager *)_fm +{ + return [self initWithPath:_path primaryKey:_folderId folderTypeName:_ftname + folderType:_ftype location:_loc quickLocation:_qloc + aclLocation:nil + folderManager:_fm]; +} + +- (void)dealloc { + [self->folderManager release]; + [self->folderInfo release]; + [self->folderId release]; + [self->folderName release]; + [self->path release]; + [self->location release]; + [self->quickLocation release]; + [self->aclLocation 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; +} +- (NSURL *)aclLocation { + return self->aclLocation; +} + +- (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]; +} +- (NSString *)aclTableName { + return [[self aclLocation] 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]]; +} +- (EOAdaptorChannel *)acquireAclChannel { + return [[self channelManager] acquireOpenChannelForURL:[self aclLocation]]; +} + +- (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]]; +} +- (BOOL)canConnectAcl { + return [[self channelManager] canConnect:[self quickLocation]]; +} + +/* errors */ + +- (NSException *)errorVersionMismatchBetweenStoredVersion:(unsigned int)_store + andExpectedVersion:(unsigned int)_base +{ + NSDictionary *ui; + + ui = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInt:_base], + @"GCSExpectedVersion", + [NSNumber numberWithUnsignedInt:_store], + @"GCSStoredVersion", + self, @"GCSFolder", + nil]; + + return [NSException exceptionWithName:@"GCSVersionMismatch" + reason:@"Transaction conflict during a GCS modification." + userInfo:ui]; +} + +- (NSException *)errorExtractorReturnedNoQuickRow:(id)_extractor + forContent:(NSString *)_content +{ + NSDictionary *ui; + + ui = [NSDictionary dictionaryWithObjectsAndKeys: + self, @"GCSFolder", + _extractor, @"GCSExtractor", + _content, @"GCSContent", + nil]; + return [NSException exceptionWithName:@"GCSExtractFailed" + reason:@"Quickfield extractor did not return a result!" + userInfo:ui]; +} + +/* 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 inContentWithName:(NSString *)_name{ + EOAdaptorChannel *channel; + NSException *error; + NSDictionary *row; + NSArray *attrs; + NSString *result; + NSString *sql; + + if ((channel = [self acquireStoreChannel]) == nil) { + [self errorWithFormat:@"could not open storage channel!"]; + 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 errorWithFormat:@"%s: cannot execute SQL '%@': %@", + __PRETTY_FUNCTION__, sql, error]; + [self releaseChannel:channel]; + return nil; + } + + /* fetch results */ + + result = nil; + attrs = [channel describeResults:NO /* do not beautify names */]; + if ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) { + result = [[[row objectForKey:_col] 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" inContentWithName:_name]; +} + +- (NSString *)fetchContentWithName:(NSString *)_name { + return [self _fetchValueOfColumn:@"c_content" 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 errorWithFormat:@"%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:NO /* do not beautify names */]; + while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) { + NSString *cName, *cContent; + + cName = [row objectForKey:@"c_name"]; + cContent = [row objectForKey:@"c_content"]; + + if (![cName isNotNull]) { + [self errorWithFormat:@"missing c_name in row: %@", row]; + continue; + } + if (![cContent isNotNull]) { + [self errorWithFormat:@"missing c_content 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]) { +#if GNUSTEP_BASE_LIBRARY + _value = [_value stringValue]; + return ([(NSString *)_value hasPrefix:@"Y"] || + [(NSString *)_value hasPrefix:@"N"]) + ? (id)([_value boolValue] ? @"1" : @"0") + : _value; +#endif + return [_value stringValue]; + } + + if ([_value isKindOfClass:NSCalendarDateClass]) { + /* be smart ... convert to timestamp. Note: we loose precision. */ + char buf[256]; + snprintf(buf, sizeof(buf), "%i", (int)[_value timeIntervalSince1970]); + return [NSString stringWithCString:buf]; + } + + [self errorWithFormat:@"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 + andColumn:(NSString *)_colname2 isEqualTo:(id)_value2 +{ + // 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]]; + + if (_colname2 != nil) { + [sql appendString:@" AND "]; + [sql appendString:_colname2]; + [sql appendString:@" = "]; + [sql appendString:[self _formatRowValue:_value2]]; + } + + return sql; +} + + +- (EOEntity *) _entityWithName: (NSString *) _name +{ + EOAttribute *attribute; + EOEntity *entity; + + entity = AUTORELEASE([[EOEntity alloc] init]); + [entity setName: _name]; + [entity setExternalName: _name]; + + attribute = AUTORELEASE([[EOAttribute alloc] init]); + [attribute setName: @"c_name"]; + [attribute setColumnName: @"c_name"]; + [entity addAttribute: attribute]; + + return entity; +} + +- (EOSQLQualifier *) _qualifierUsingWhereColumn:(NSString *)_colname + isEqualTo:(id)_value + andColumn:(NSString *)_colname2 + isEqualTo:(id)_value2 + entity: (EOEntity *)_entity +{ + EOSQLQualifier *qualifier; + + if (_colname2 == nil) + { + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@", _colname, [self _formatRowValue:_value]]; + } + else + { + qualifier = [[EOSQLQualifier alloc] initWithEntity: _entity + qualifierFormat: @"%A = %@ AND %A = %@", _colname, [self _formatRowValue:_value], + _colname2, [self _formatRowValue:_value2]]; + } + + return AUTORELEASE(qualifier); +} + +- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name + baseVersion:(unsigned int)_baseVersion +{ + EOAdaptorChannel *storeChannel, *quickChannel; + NSMutableDictionary *quickRow, *contentRow; + GCSFieldExtractor *extractor; + NSException *error; + NSNumber *storedVersion; + BOOL isNewRecord, hasInsertDelegate, hasUpdateDelegate; + NSCalendarDate *nowDate; + NSNumber *now; + + /* 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; + + /* check whether sequence matches */ + if (_baseVersion != 0 /* use 0 to override check */) { + if (_baseVersion != [storedVersion unsignedIntValue]) { + /* version mismatch (concurrent update) */ + return [self errorVersionMismatchBetweenStoredVersion: + [storedVersion unsignedIntValue] + andExpectedVersion:_baseVersion]; + } + } + + /* extract quick info */ + extractor = [self->folderInfo quickExtractor]; + if ((quickRow = [extractor extractQuickFieldsFromContent:_content]) == nil) { + return [self errorExtractorReturnedNoQuickRow:extractor + forContent:_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] + 1)] + forKey:@"c_version"]; + } + [contentRow setObject:_content forKey:@"c_content"]; + + /* open channels */ + if ((storeChannel = [self acquireStoreChannel]) == nil) { + [self errorWithFormat:@"%s: could not open storage channel!", + __PRETTY_FUNCTION__]; + return nil; + } + if (!self->ofFlags.sameTableForQuick) { + if ((quickChannel = [self acquireQuickChannel]) == nil) { + [self errorWithFormat:@"%s: could not open quick channel!", + __PRETTY_FUNCTION__]; + [self releaseChannel:storeChannel]; + return nil; + } + } + + /* we check if we can call directly methods on our adaptor + channel delegate. If not, we generate SQL ourself since it'll + be a little bit faster and less complex than using GDL to do so */ + hasInsertDelegate = [[storeChannel delegate] + respondsToSelector: @selector(adaptorChannel:willInsertRow:forEntity:)]; + hasUpdateDelegate = [[storeChannel delegate] + respondsToSelector: @selector(adaptorChannel:willUpdateRow:describedByQualifier:)]; + + [[storeChannel adaptorContext] beginTransaction]; + [[quickChannel adaptorContext] beginTransaction]; + + if (isNewRecord) { + if (!self->ofFlags.sameTableForQuick) { + error = (hasInsertDelegate ? [quickChannel insertRowX: quickRow + forEntity: [self _entityWithName: [self quickTableName]]] + : [quickChannel evaluateExpressionX: [self _generateInsertStatementForRow: quickRow + tableName: [self quickTableName]]]); + CHECKERROR(); + } + + error = (hasInsertDelegate ? [storeChannel insertRowX: contentRow + forEntity: [self _entityWithName: [self storeTableName]]] + : [storeChannel evaluateExpressionX: [self _generateInsertStatementForRow: contentRow + tableName: [self storeTableName]]]); + + CHECKERROR(); + } + else { + if (!self->ofFlags.sameTableForQuick) { + error = (hasUpdateDelegate ? [quickChannel updateRowX: quickRow + describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" + isEqualTo: _name andColumn: nil isEqualTo: nil + entity: [self _entityWithName: [self quickTableName]]]] + : [quickChannel evaluateExpressionX: [self _generateUpdateStatementForRow: quickRow + tableName: [self quickTableName] + whereColumn: @"c_name" isEqualTo: _name + andColumn: nil isEqualTo: nil]]); + CHECKERROR(); + } + + error = (hasUpdateDelegate ? [storeChannel updateRowX: contentRow + describedByQualifier: [self _qualifierUsingWhereColumn: @"c_name" isEqualTo: _name + andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) + isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt:_baseVersion] : (NSNumber *)nil) + entity: [self _entityWithName: [self storeTableName]]]] + : [storeChannel evaluateExpressionX: [self _generateUpdateStatementForRow: contentRow tableName:[self storeTableName] + whereColumn: @"c_name" isEqualTo: _name + andColumn: (_baseVersion != 0 ? (id)@"c_version" : (id)nil) + isEqualTo: (_baseVersion != 0 ? [NSNumber numberWithUnsignedInt: _baseVersion] : (NSNumber *)nil)]]); + CHECKERROR(); + } + + [[storeChannel adaptorContext] commitTransaction]; + [[quickChannel adaptorContext] commitTransaction]; + + [self releaseChannel: storeChannel]; + if (!self->ofFlags.sameTableForQuick) [self releaseChannel: quickChannel]; + + return error; +} + + +- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name { + /* this method does not check for concurrent writes */ + return [self writeContent:_content toName:_name baseVersion:0]; +} + +- (NSException *)deleteContentWithName:(NSString *)_name { + EOAdaptorChannel *storeChannel, *quickChannel; + NSException *error; + NSString *delsql; + NSCalendarDate *nowDate; + + /* 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 errorWithFormat:@"could not open storage channel!"]; + return nil; + } + if (!self->ofFlags.sameTableForQuick) { + if ((quickChannel = [self acquireQuickChannel]) == nil) { + [self errorWithFormat:@"could not open quick channel!"]; + [self releaseChannel:storeChannel]; + return nil; + } + } + + /* delete rows */ + nowDate = [NSCalendarDate calendarDate]; + + delsql = [@"UPDATE " stringByAppendingString:[self storeTableName]]; + delsql = [delsql stringByAppendingString:@" SET c_deleted = 1"]; + delsql = [delsql stringByAppendingFormat:@", c_lastmodified = %u", + (unsigned int) [nowDate timeIntervalSince1970]]; + delsql = [delsql stringByAppendingString:@" WHERE c_name="]; + delsql = [delsql stringByAppendingString:[self _formatRowValue:_name]]; + if ((error = [storeChannel evaluateExpressionX:delsql]) != nil) { + [self errorWithFormat: + @"%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 errorWithFormat: + @"%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; +} + +- (NSException *)deleteFolder { + EOAdaptorChannel *channel; + NSString *delsql; + NSString *table; + + /* open channels */ + + if ((channel = [self acquireStoreChannel]) == nil) { + [self errorWithFormat:@"could not open channel!"]; + return nil; + } + + /* delete rows */ + + table = [self storeTableName]; + if ([table length] > 0) { + delsql = [@"DROP TABLE " stringByAppendingString: table]; + [channel evaluateExpressionX:delsql]; + } + table = [self quickTableName]; + if ([table length] > 0) { + delsql = [@"DROP TABLE " stringByAppendingString: table]; + [channel evaluateExpressionX:delsql]; + } + table = [self aclTableName]; + if ([table length] > 0) { + delsql = [@"DROP TABLE " stringByAppendingString: table]; + [channel evaluateExpressionX:delsql]; + } + + [self releaseChannel:channel]; + + return nil; +} + +- (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 errorWithFormat:@" could not open storage channel!"]; + return nil; + } + + /* run SQL */ + + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self errorWithFormat:@"%s: cannot execute quick-fetch SQL '%@': %@", + __PRETTY_FUNCTION__, sql, error]; + [self releaseChannel:channel]; + return nil; + } + + /* fetch results */ + + results = [NSMutableArray arrayWithCapacity:64]; + attrs = [channel describeResults:NO /* do not beautify names */]; + 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]; +} + +- (NSArray *)fetchAclWithSpecification:(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 c_uid, c_object, c_role"]; + [sql appendString:@" FROM "]; + [sql appendString:[self aclTableName]]; + + 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 acquireAclChannel]) == nil) { + [self errorWithFormat:@"could not open acl channel!"]; + return nil; + } + + /* run SQL */ + + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self errorWithFormat:@"%s: cannot execute acl-fetch SQL '%@': %@", + __PRETTY_FUNCTION__, sql, error]; + [self releaseChannel:channel]; + return nil; + } + + /* fetch results */ + + results = [NSMutableArray arrayWithCapacity:64]; + attrs = [channel describeResults:NO /* do not beautify names */]; + while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) + [results addObject:row]; + + /* release channels */ + + [self releaseChannel:channel]; + + return results; +} +- (NSArray *) fetchAclMatchingQualifier:(EOQualifier *)_q { + EOFetchSpecification *fs; + + if (_q == nil) + fs = nil; + else { + fs = [EOFetchSpecification fetchSpecificationWithEntityName: + [self folderName] + qualifier:_q + sortOrderings:nil]; + } + return [self fetchAclWithSpecification:fs]; +} + +- (void) deleteAclMatchingQualifier:(EOQualifier *)_q { + EOFetchSpecification *fs; + + if (_q != nil) { + fs = [EOFetchSpecification fetchSpecificationWithEntityName: + [self folderName] + qualifier:_q + sortOrderings:nil]; + [self deleteAclWithSpecification:fs]; + } +} + +- (void)deleteAclWithSpecification:(EOFetchSpecification *)_fs +{ + EOQualifier *qualifier; + EOAdaptorChannel *channel; + NSException *error; + NSMutableString *sql; + + qualifier = [_fs qualifier]; + if (qualifier != nil) { + sql = [NSMutableString stringWithCapacity:256]; + [sql appendString:@"DELETE FROM "]; + [sql appendString:[self aclTableName]]; + [sql appendString:@" WHERE "]; + [sql appendString:[self generateSQLForQualifier:qualifier]]; + } + + /* open channel */ + + if ((channel = [self acquireAclChannel]) == nil) { + [self errorWithFormat:@"could not open acl channel!"]; + return; + } + + /* run SQL */ + + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self errorWithFormat:@"%s: cannot execute acl-fetch SQL '%@': %@", + __PRETTY_FUNCTION__, sql, error]; + [self releaseChannel:channel]; + return; + } + + [self releaseChannel:channel]; +} + +/* description */ + +- (NSString *)description { + NSMutableString *ms; + id tmp; + + ms = [NSMutableString stringWithCapacity:256]; + [ms appendFormat:@"<0x%p[%@]:", 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h b/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h new file mode 100644 index 00000000..27aa09ac --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h @@ -0,0 +1,83 @@ +/* + 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 + +/* + 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 withName:(NSString *)_name atPath:(NSString *)_path; +- (NSException *)deleteFolderAtPath:(NSString *)_path; + +/* folder types */ + +- (GCSFolderType *)folderTypeWithName:(NSString *)_name; + +/* cache management */ + +- (void)reset; + +@end + +#endif /* __GDLContentStore_GCSFolderManager_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m b/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m new file mode 100644 index 00000000..3526fd16 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m @@ -0,0 +1,853 @@ +/* + Copyright (C) 2004-2007 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 +#include +#include + +/* + Required database schema: + + + 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 = @"c_folder_type"; +#endif +static NSString *GCSPathRecordName = @"c_path"; +static NSString *GCSGenericFolderTypeName = @"Container"; +static const char *GCSPathColumnPattern = "c_path%i"; +static NSCharacterSet *asciiAlphaNumericCS = nil; + ++ (void)initialize { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + + debugOn = [ud boolForKey:@"GCSFolderManagerDebugEnabled"]; + debugSQLGen = [ud boolForKey:@"GCSFolderManagerSQLDebugEnabled"]; + emptyArray = [[NSArray alloc] init]; + if (!asciiAlphaNumericCS) + { + asciiAlphaNumericCS + = [NSCharacterSet characterSetWithCharactersInString: + @"0123456789" + @"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + @"abcdefghijklmnopqrstuvwxyz"]; + [asciiAlphaNumericCS retain]; + } +} + ++ (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; +} + +- (NSDictionary *)loadDefaultFolderTypes { + NSMutableDictionary *typeMap; + NSArray *types; + unsigned i, count; + + + types = [[GCSFolderType resourceLocator] lookupAllFilesWithExtension:@"ocs" + doReturnFullPath:NO]; + if ((count = [types count]) == 0) { + [self logWithFormat:@"Note: no GCS folder types found."]; + return nil; + } + + typeMap = [NSMutableDictionary dictionaryWithCapacity:count]; + + [self logWithFormat:@"Note: loading %d GCS folder types:", count]; + for (i = 0, count = [types count]; i < count; i++) { + NSString *type; + GCSFolderType *typeObject; + + type = [[types objectAtIndex:i] stringByDeletingPathExtension]; + typeObject = [[GCSFolderType alloc] initWithFolderTypeName:type]; + + [self logWithFormat:@" %@: %s", + type, [typeObject isNotNull] ? "OK" : "FAIL"]; + [typeMap setObject:typeObject forKey:type]; + [typeObject release]; + } + + return typeMap; +} + +- (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])) { + 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 */ + self->nameToType = [[self loadDefaultFolderTypes] copy]; + } + 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:NO /* do not beautify names */]; + 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, *aclLocation; + + if (_record == nil) return nil; + + folderTypeName = [_record objectForKey:@"c_folder_type"]; + 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:@"c_path"]]; + return nil; + } + + folderId = [_record objectForKey:@"c_folder_id"]; + folderName = [_record objectForKey:@"c_path"]; + path = [self pathFromInternalName:folderName]; + + locationString = [_record objectForKey:@"c_location"]; + 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:@"c_quick_location"]; + quickLocation = [locationString isNotNull] + ? [NSURL URLWithString:locationString] + : nil; + + if (quickLocation == nil) { + [self logWithFormat:@"WARNING(%s): missing quick location in record: %@", + __PRETTY_FUNCTION__, _record]; + } + + locationString = [_record objectForKey:@"c_acl_location"]; + aclLocation = [locationString isNotNull] + ? [NSURL URLWithString:locationString] + : nil; + + folder = [[GCSFolder alloc] initWithPath:path primaryKey:folderId + folderTypeName:folderTypeName + folderType:folderType + location:location quickLocation:quickLocation + aclLocation:aclLocation + 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; + 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, c_acl_location,"]; + [sql appendString:@" c_folder_type"]; + [sql appendString:@" FROM "]; + [sql appendString:[self folderInfoTableName]]; + [sql appendString:@" WHERE "]; + [sql appendString:ws]; + + if (debugSQLGen) [self logWithFormat:@"folderAtPath: %@", sql]; + + /* 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]; +} + +- (NSString *)baseTableNameWithUID:(NSString *)_uid { + NSDate *now; + unichar currentChar; + unsigned int count, max, done; + NSMutableString *newUID; + + newUID = [NSMutableString string]; + now = [NSDate date]; + + max = [_uid length]; + done = 0; + count = 0; + while (done < 8 && count < max) + { + currentChar = [_uid characterAtIndex: count]; + if ([asciiAlphaNumericCS characterIsMember: currentChar]) + { + [newUID appendFormat: @"%c", currentChar]; + done++; + } + count++; + } + + return [NSString stringWithFormat: @"%@%u", + newUID, [now timeIntervalSince1970]]; +} + +- (NSException *)createFolderOfType:(NSString *)_type + withName:(NSString*)_name atPath:(NSString *)_path +{ + // TBD: badly broken, needs to be wrapped in a transaction. + // TBD: would be best to perform all operations as a single SQL statement. + GCSFolderType *ftype; + NSString *tableName, *quickTableName, *aclTableName; + NSString *baseURL, *pathElement; + EOAdaptorChannel *channel; + NSEnumerator *pathElements; + NSMutableArray *paths; + NSException *error; + NSString *sql; + + paths = [[NSMutableArray alloc] initWithCapacity: 5]; + + pathElements = [[_path componentsSeparatedByString: @"/"] objectEnumerator]; + while ((pathElement = [pathElements nextObject]) != nil) { + NSString *p = [[NSString alloc] initWithFormat: @"'%@'", pathElement]; + [paths addObject: p]; + [p release]; p = nil; + } + while ([paths count] < 5) + [paths addObject: @"NULL"]; + + // TBD: fix SQL injection issue! + sql = [NSString stringWithFormat: @"SELECT * FROM %@ WHERE c_path = '%@'", + [self folderInfoTableName], _path]; + if ([[self performSQL: sql] isNotEmpty]) { + return [NSException exceptionWithName:@"GCSExitingFolder" + reason:@"a folder already exists at that path" + userInfo:nil]; + } + if ((ftype = [self folderTypeWithName:_type]) == nil) { + return [NSException exceptionWithName:@"GCSMissingFolderType" + reason:@"missing folder type"userInfo:nil]; + } + if ((channel = [self acquireOpenChannel]) == nil) { + return [NSException exceptionWithName:@"GCSNoChannel" + reason:@"could not open channel" + userInfo:nil]; + } + + tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]]; + quickTableName = [tableName stringByAppendingString: @"_quick"]; + aclTableName = [tableName stringByAppendingString: @"_acl"]; + + sql = [@"DROP TABLE " stringByAppendingString:quickTableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) + ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) + + sql = [@"DROP TABLE " stringByAppendingString:tableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) + ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) + + sql = [@"DROP TABLE " stringByAppendingString:aclTableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) + ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) + + if ((error = [channel createGCSFolderTableWithName: tableName]) != nil) + return error; + + sql = [ftype sqlQuickCreateWithTableName: quickTableName]; + if (debugSQLGen) [self logWithFormat:@"quick-Create: %@", sql]; + + if ((error = [channel evaluateExpressionX:sql]) != nil) { + /* 'rollback' TBD: wrap in proper tx */ + sql = [@"DROP TABLE " stringByAppendingString:tableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self warnWithFormat:@"failed to drop freshly created table: %@", + tableName]; + } + + return error; + } + + if (debugSQLGen) [self logWithFormat:@"acl-Create: %@", sql]; + if ((error = [channel createGCSFolderACLTableWithName: aclTableName]) + != nil) { + /* 'rollback' TBD: wrap in proper tx */ + sql = [@"DROP TABLE " stringByAppendingString:quickTableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self warnWithFormat:@"failed to drop freshly created table: %@", + tableName]; + } + sql = [@"DROP TABLE " stringByAppendingString:tableName]; + if ((error = [channel evaluateExpressionX:sql]) != nil) { + [self warnWithFormat:@"failed to drop freshly created table: %@", + tableName]; + } + + return error; + } + + // TBD: fix SQL injection issues + baseURL + = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent]; + + sql = [NSString stringWithFormat: @"INSERT INTO %@" + @" (c_path, c_path1, c_path2, c_path3, c_path4," + @" c_foldername, c_location, c_quick_location," + @" c_acl_location, c_folder_type)" + @" VALUES ('%@', %@, %@, %@, %@, '%@', '%@/%@'," + @" '%@/%@', '%@/%@', '%@')", + [self folderInfoTableName], _path, + [paths objectAtIndex: 1], [paths objectAtIndex: 2], + [paths objectAtIndex: 3], [paths objectAtIndex: 4], + _name, + baseURL, tableName, + baseURL, quickTableName, + baseURL, aclTableName, + _type]; + if ((error = [channel evaluateExpressionX:sql]) != nil) + return error; + + [paths release]; paths = nil; + [self releaseChannel: channel]; + + return nil; +} + +- (NSException *)deleteFolderAtPath:(NSString *)_path { + GCSFolder *folder; + NSArray *fnames; + NSString *sql, *ws; + EOAdaptorChannel *channel; + NSException *ex; + + if ((folder = [self folderAtPath:_path]) == nil) { + return [NSException exceptionWithName:@"GCSMissingFolder" + reason:@"missing folder" + userInfo:nil]; + } + + if ((fnames = [self internalNamesFromPath:_path]) == nil) { + [self debugWithFormat:@"got no internal names for path: '%@'", _path]; + return nil; + } + + ws = [self generateSQLWhereForInternalNames:fnames + exactMatch:YES orDirectSubfolderMatch:NO]; + + sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE %@", + [self folderInfoTableName], ws]; + if ((channel = [self acquireOpenChannel]) == nil) { + return [NSException exceptionWithName:@"GCSNoChannel" + reason:@"could not " + userInfo:nil]; + } + + if ((ex = [channel evaluateExpressionX:sql]) != nil) { + [self releaseChannel:channel]; + return ex; + } + + [self releaseChannel:channel]; + + return [folder deleteFolder]; +} + +/* folder types */ + +- (GCSFolderType *)folderTypeWithName:(NSString *)_name { + NSString *specificName; + GCSFolderType *type; + + if ([_name length] == 0) + _name = GCSGenericFolderTypeName; + + specificName = [NSString stringWithFormat: @"%@-%@", + _name, [folderInfoLocation scheme]]; + type = [self->nameToType objectForKey: [specificName lowercaseString]]; + if (!type) + type = [self->nameToType objectForKey:[_name lowercaseString]]; + + return type; +} + +/* 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%p[%@]:", self, NSStringFromClass([self class])]; + + [ms appendFormat:@" url=%@", [self->folderInfoLocation absoluteString]]; + [ms appendFormat:@" channel-manager=0x%p", [self channelManager]]; + + [ms appendString:@">"]; + return ms; +} + +@end /* GCSFolderManager */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h b/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h new file mode 100644 index 00000000..8c62e052 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h @@ -0,0 +1,82 @@ +/* + 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 + +@class NSString, NSArray, NSDictionary; +@class EOQualifier; +@class NGResourceLocator; +@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; + +/* locator used to find .ocs files */ + ++ (NGResourceLocator *)resourceLocator; + +@end + +#endif /* __GDLContentStore_GCSFolderType_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m b/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m new file mode 100644 index 00000000..76f2d36b --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m @@ -0,0 +1,198 @@ +/* + 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 +#include + +@implementation GCSFolderType + +- (id)initWithPropertyList:(id)_plist { + if ((self = [super init])) { + NSDictionary *plist = _plist; + + 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 != nil) { + return [self->extractor isNotNull] + ? self->extractor : (GCSFieldExtractor *)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%p[%@]:", 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h b/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h new file mode 100644 index 00000000..cdd57c2a --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h @@ -0,0 +1,37 @@ +/* + 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 + +@interface GCSStringFormatter : NSObject < NGStringEscaping > +{ +} + ++ (id)sharedFormatter; + +- (NSString *)stringByFormattingString:(NSString *)_s; + +@end + +#endif /* __GDLContentStore_GCSStringFormatter_H__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m b/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m new file mode 100644 index 00000000..b08bab5e --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m @@ -0,0 +1,63 @@ +/* + 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 */ diff --git a/SOPE/sope-gdl1/GDLContentStore/GNUmakefile b/SOPE/sope-gdl1/GDLContentStore/GNUmakefile new file mode 100644 index 00000000..85b394e8 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GNUmakefile @@ -0,0 +1,77 @@ +# GNUstep makefiles + +include ../../../config.make +include $(GNUSTEP_MAKEFILES)/common.make +include ./Version + +GNUSTEP_INSTALLATION_DIR = ${GNUSTEP_LOCAL_ROOT} + +ifneq ($(frameworks),yes) +LIBRARY_NAME = libGDLContentStore +else +FRAMEWORK_NAME = GDLContentStore +endif + +libGDLContentStore_PCH_FILE = common.h +libGDLContentStore_SOVERSION=$(MAJOR_VERSION).$(MINOR_VERSION) +libGDLContentStore_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION) + +TOOL_NAME = gcs_ls gcs_mkdir gcs_cat gcs_recreatequick gcs_gensql + +libGDLContentStore_HEADER_FILES_DIR = . +libGDLContentStore_HEADER_FILES_INSTALL_DIR = /GDLContentStore +FHS_HEADER_FILES_INSTALL_DIR = $(libGDLContentStore_HEADER_FILES_INSTALL_DIR) + +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 + + +# framework support + +GDLContentStore_PCH_FILE = $(libGDLContentStore_PCH_FILE) +GDLContentStore_HEADER_FILES = $(libGDLContentStore_HEADER_FILES) +GDLContentStore_OBJC_FILES = $(libGDLContentStore_OBJC_FILES) +GDLContentStore_SUBPROJECTS = $(libGDLContentStore_SUBPROJECTS) + + +# building + +-include GNUmakefile.preamble +ifneq ($(frameworks),yes) +include $(GNUSTEP_MAKEFILES)/library.make +else +include $(GNUSTEP_MAKEFILES)/framework.make +endif +include $(GNUSTEP_MAKEFILES)/tool.make +-include GNUmakefile.postamble +include fhs.make diff --git a/SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble b/SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble new file mode 100644 index 00000000..ada5aa19 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble @@ -0,0 +1,78 @@ +# compilation settings + +SOPE_ROOT=../.. + +ADDITIONAL_CPPFLAGS += -Wall + +ADDITIONAL_INCLUDE_DIRS += -I. -I.. + + +# dependencies + +libGDLContentStore_LIBRARIES_DEPEND_UPON += \ + -lGDLAccess \ + -lNGExtensions -lEOControl \ + -lDOM -lSaxObjC + +GDLContentStore_LIBRARIES_DEPEND_UPON += \ + -framework GDLAccess \ + -framework NGExtensions -framework EOControl \ + -framework DOM -framework SaxObjC + +ifneq ($(frameworks),yes) +GCS_TOOL_LIBS += \ + -lGDLContentStore -lGDLAccess \ + -lNGExtensions -lEOControl \ + -lDOM -lSaxObjC +else +GCS_TOOL_LIBS += \ + -framework GDLContentStore -framework GDLAccess \ + -framework NGExtensions -framework EOControl \ + -framework DOM -framework SaxObjC +endif + +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) + +gcs_ls_PCH_FILE += common.h +gcs_mkdir_PCH_FILE += common.h +gcs_cat_PCH_FILE += common.h +gcs_recreatequick_PCH_FILE += common.h +gcs_gensql_PCH_FILE += common.h + + +# library/framework search pathes + +DEP_DIRS = \ + . \ + ../GDLAccess \ + $(SOPE_ROOT)/sope-core/NGExtensions \ + $(SOPE_ROOT)/sope-core/EOControl \ + $(SOPE_ROOT)/sope-xml/DOM \ + $(SOPE_ROOT)/sope-xml/SaxObjC + +ifneq ($(frameworks),yes) +ADDITIONAL_LIB_DIRS += \ + $(foreach dir,$(DEP_DIRS),\ + -L$(GNUSTEP_BUILD_DIR)/$(dir)/$(GNUSTEP_OBJ_DIR_NAME)) +else +ADDITIONAL_LIB_DIRS += \ + $(foreach dir,$(DEP_DIRS),-F$(GNUSTEP_BUILD_DIR)/$(dir)) +endif + +SYSTEM_LIB_DIR += $(CONFIGURE_SYSTEM_LIB_DIR) + + +# platform specific settings + +ifeq ($(FOUNDATION_LIB),apple) +libGDLContentStore_PREBIND_ADDR="0xC7700000" +libGDLContentStore_LDFLAGS += -seg1addr $(libGDLContentStore_PREBIND_ADDR) +endif + +ifeq ($(findstring openbsd3, $(GNUSTEP_HOST_OS)), openbsd3) +GCS_TOOL_LIBS += -liconv +endif diff --git a/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h b/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h new file mode 100644 index 00000000..c03ea403 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h @@ -0,0 +1,41 @@ +/* + 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 + +/* + "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__ */ diff --git a/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m b/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m new file mode 100644 index 00000000..285aad65 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m @@ -0,0 +1,51 @@ +/* + 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) */ diff --git a/SOPE/sope-gdl1/GDLContentStore/README b/SOPE/sope-gdl1/GDLContentStore/README new file mode 100644 index 00000000..981e3d94 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/README @@ -0,0 +1,117 @@ +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. diff --git a/SOPE/sope-gdl1/GDLContentStore/Version b/SOPE/sope-gdl1/GDLContentStore/Version new file mode 100644 index 00000000..eb335281 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/Version @@ -0,0 +1,14 @@ +# Version file + +MAJOR_VERSION:=4 +MINOR_VERSION:=7 +SUBMINOR_VERSION:=49 + +# v4.5.29 requires libNGExtensions v4.5.161 +# 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 diff --git a/SOPE/sope-gdl1/GDLContentStore/common.h b/SOPE/sope-gdl1/GDLContentStore/common.h new file mode 100644 index 00000000..fa68afc0 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/common.h @@ -0,0 +1,36 @@ +/* + 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: common.h 673 2005-03-20 19:09:53Z helge $ + +#import +#import + +#include + +#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 diff --git a/SOPE/sope-gdl1/GDLContentStore/fhs.make b/SOPE/sope-gdl1/GDLContentStore/fhs.make new file mode 100644 index 00000000..24d1e43f --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/fhs.make @@ -0,0 +1,44 @@ +# postprocessing + +# FHS support (this is a hack and is going to be done by gstep-make!) + +# NOTE: you need to define FHS_HEADER_FILES_INSTALL_DIR for one library + +ifneq ($(FHS_INSTALL_ROOT),) + +FHS_INCLUDE_DIR=$(FHS_INSTALL_ROOT)/include/ +FHS_BIN_DIR=$(FHS_INSTALL_ROOT)/bin +FHS_LIB_DIR=$(CONFIGURE_FHS_INSTALL_LIBDIR) + +NONFHS_LIBDIR="$(GNUSTEP_LIBRARIES)/$(GNUSTEP_TARGET_LDIR)/" +NONFHS_LIBNAME="$(LIBRARY_NAME)$(LIBRARY_NAME_SUFFIX)$(SHARED_LIBEXT)" +NONFHS_BINDIR="$(GNUSTEP_TOOLS)/$(GNUSTEP_TARGET_LDIR)" + + +fhs-header-dirs :: + $(MKDIRS) $(FHS_INCLUDE_DIR)$(FHS_HEADER_FILES_INSTALL_DIR) + +fhs-bin-dirs :: + $(MKDIRS) $(FHS_BIN_DIR) + + +move-headers-to-fhs :: fhs-header-dirs + @echo "moving headers to $(FHS_INCLUDE_DIR) .." + mv $(GNUSTEP_HEADERS)$(FHS_HEADER_FILES_INSTALL_DIR)/*.h \ + $(FHS_INCLUDE_DIR)$(FHS_HEADER_FILES_INSTALL_DIR)/ + +move-libs-to-fhs :: + @echo "moving libs to $(FHS_LIB_DIR) .." + mv $(NONFHS_LIBDIR)/$(NONFHS_LIBNAME)* $(FHS_LIB_DIR)/ + +move-tools-to-fhs :: fhs-bin-dirs + @echo "moving tools from $(NONFHS_BINDIR) to $(FHS_BIN_DIR) .." + for i in $(TOOL_NAME); do \ + mv "$(NONFHS_BINDIR)/$${i}" $(FHS_BIN_DIR); \ + done + +move-to-fhs :: move-headers-to-fhs move-libs-to-fhs move-tools-to-fhs + +after-install :: move-to-fhs + +endif diff --git a/SOPE/sope-gdl1/GDLContentStore/gcs_cat.m b/SOPE/sope-gdl1/GDLContentStore/gcs_cat.m new file mode 100644 index 00000000..7cf768c2 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/gcs_cat.m @@ -0,0 +1,122 @@ +/* + 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 + +@class NSUserDefaults, NSArray; +@class GCSFolderManager; + +@interface Tool : NSObject +{ + NSUserDefaults *ud; + GCSFolderManager *folderManager; +} + ++ (int)runWithArgs:(NSArray *)_args; +- (int)run; + +@end + +#include +#include +#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; +} diff --git a/SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m b/SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m new file mode 100644 index 00000000..13e0caea --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m @@ -0,0 +1,114 @@ +/* + 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 + +@class NSUserDefaults, NSArray; +@class GCSFolderManager; + +@interface Tool : NSObject +{ + NSUserDefaults *ud; +} + ++ (int)runWithArgs:(NSArray *)_args; +- (int)run; + +@end + +#include +#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; +} diff --git a/SOPE/sope-gdl1/GDLContentStore/gcs_ls.m b/SOPE/sope-gdl1/GDLContentStore/gcs_ls.m new file mode 100644 index 00000000..20de58e9 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/gcs_ls.m @@ -0,0 +1,140 @@ +/* + 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 + +@class NSUserDefaults, NSArray; +@class GCSFolderManager; + +@interface Tool : NSObject +{ + NSUserDefaults *ud; + GCSFolderManager *folderManager; +} + ++ (int)runWithArgs:(NSArray *)_args; +- (int)run; + +@end + +#include +#include +#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; +} diff --git a/SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m b/SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m new file mode 100644 index 00000000..fc3b3547 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m @@ -0,0 +1,126 @@ +/* + 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 + +@class NSUserDefaults, NSArray; +@class GCSFolderManager; + +@interface Tool : NSObject +{ + NSUserDefaults *ud; + GCSFolderManager *folderManager; +} + ++ (int)runWithArgs:(NSArray *)_args; +- (int)run; + +@end + +#include +#include +#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; +} diff --git a/SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m b/SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m new file mode 100644 index 00000000..d6835535 --- /dev/null +++ b/SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m @@ -0,0 +1,121 @@ +/* + 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 + +@class NSUserDefaults, NSArray; +@class GCSFolderManager; + +@interface Tool : NSObject +{ + NSUserDefaults *ud; + GCSFolderManager *folderManager; +} + ++ (int)runWithArgs:(NSArray *)_args; +- (int)run; + +@end + +#include +#include +#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; +} -- 2.39.5