]> git.sur5r.net Git - contagged/commitdiff
initial checkin
authorAndreas Gohr <gohr@cosmocode.de>
Wed, 7 Dec 2005 09:25:29 +0000 (10:25 +0100)
committerAndreas Gohr <gohr@cosmocode.de>
Wed, 7 Dec 2005 09:25:29 +0000 (10:25 +0100)
darcs-hash:20051207092529-6e07b-ccd48731093a8d618db0a6e4c11b940f604a938f.gz

131 files changed:
COPYING [new file with mode: 0644]
Contact_Vcard_Parse.php [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
config.php [new file with mode: 0644]
entries.conf [new file with mode: 0644]
entry.php [new file with mode: 0644]
functions.php [new file with mode: 0644]
help.php [new file with mode: 0644]
img.php [new file with mode: 0644]
import.php [new file with mode: 0644]
importVCF.php [new file with mode: 0644]
index.php [new file with mode: 0644]
init.php [new file with mode: 0644]
lang/de.php [new file with mode: 0644]
lang/en.php [new file with mode: 0644]
ldapab.schema [new file with mode: 0644]
login.php [new file with mode: 0644]
orgs.php [new file with mode: 0644]
pix/copy.png [new file with mode: 0644]
pix/delete.png [new file with mode: 0644]
pix/edit.png [new file with mode: 0644]
pix/help.png [new file with mode: 0644]
pix/image.png [new file with mode: 0644]
pix/import.png [new file with mode: 0644]
pix/login.png [new file with mode: 0644]
pix/logout.png [new file with mode: 0644]
pix/new.png [new file with mode: 0644]
pix/org.png [new file with mode: 0644]
pix/orgs.png [new file with mode: 0644]
pix/private.png [new file with mode: 0644]
pix/public.png [new file with mode: 0644]
pix/search.png [new file with mode: 0644]
pix/show.png [new file with mode: 0644]
pix/vcard.png [new file with mode: 0644]
slapd.example [new file with mode: 0644]
smarty/Config_File.class.php [new file with mode: 0644]
smarty/Smarty.class.php [new file with mode: 0644]
smarty/Smarty_Compiler.class.php [new file with mode: 0644]
smarty/core/core.assemble_plugin_filepath.php [new file with mode: 0644]
smarty/core/core.assign_smarty_interface.php [new file with mode: 0644]
smarty/core/core.create_dir_structure.php [new file with mode: 0644]
smarty/core/core.display_debug_console.php [new file with mode: 0644]
smarty/core/core.get_include_path.php [new file with mode: 0644]
smarty/core/core.get_microtime.php [new file with mode: 0644]
smarty/core/core.get_php_resource.php [new file with mode: 0644]
smarty/core/core.is_secure.php [new file with mode: 0644]
smarty/core/core.is_trusted.php [new file with mode: 0644]
smarty/core/core.load_plugins.php [new file with mode: 0644]
smarty/core/core.load_resource_plugin.php [new file with mode: 0644]
smarty/core/core.process_cached_inserts.php [new file with mode: 0644]
smarty/core/core.process_compiled_include.php [new file with mode: 0644]
smarty/core/core.read_cache_file.php [new file with mode: 0644]
smarty/core/core.rm_auto.php [new file with mode: 0644]
smarty/core/core.rmdir.php [new file with mode: 0644]
smarty/core/core.run_insert_handler.php [new file with mode: 0644]
smarty/core/core.smarty_include_php.php [new file with mode: 0644]
smarty/core/core.write_cache_file.php [new file with mode: 0644]
smarty/core/core.write_compiled_include.php [new file with mode: 0644]
smarty/core/core.write_compiled_resource.php [new file with mode: 0644]
smarty/core/core.write_file.php [new file with mode: 0644]
smarty/debug.tpl [new file with mode: 0644]
smarty/plugins/block.textformat.php [new file with mode: 0644]
smarty/plugins/function.assign.php [new file with mode: 0644]
smarty/plugins/function.assign_debug_info.php [new file with mode: 0644]
smarty/plugins/function.config_load.php [new file with mode: 0644]
smarty/plugins/function.counter.php [new file with mode: 0644]
smarty/plugins/function.cycle.php [new file with mode: 0644]
smarty/plugins/function.debug.php [new file with mode: 0644]
smarty/plugins/function.eval.php [new file with mode: 0644]
smarty/plugins/function.fetch.php [new file with mode: 0644]
smarty/plugins/function.html_checkboxes.php [new file with mode: 0644]
smarty/plugins/function.html_image.php [new file with mode: 0644]
smarty/plugins/function.html_options.php [new file with mode: 0644]
smarty/plugins/function.html_radios.php [new file with mode: 0644]
smarty/plugins/function.html_select_date.php [new file with mode: 0644]
smarty/plugins/function.html_select_time.php [new file with mode: 0644]
smarty/plugins/function.html_table.php [new file with mode: 0644]
smarty/plugins/function.mailto.php [new file with mode: 0644]
smarty/plugins/function.math.php [new file with mode: 0644]
smarty/plugins/function.popup.php [new file with mode: 0644]
smarty/plugins/function.popup_init.php [new file with mode: 0644]
smarty/plugins/modifier.capitalize.php [new file with mode: 0644]
smarty/plugins/modifier.cat.php [new file with mode: 0644]
smarty/plugins/modifier.count_characters.php [new file with mode: 0644]
smarty/plugins/modifier.count_paragraphs.php [new file with mode: 0644]
smarty/plugins/modifier.count_sentences.php [new file with mode: 0644]
smarty/plugins/modifier.count_words.php [new file with mode: 0644]
smarty/plugins/modifier.date_format.php [new file with mode: 0644]
smarty/plugins/modifier.debug_print_var.php [new file with mode: 0644]
smarty/plugins/modifier.default.php [new file with mode: 0644]
smarty/plugins/modifier.escape.php [new file with mode: 0644]
smarty/plugins/modifier.indent.php [new file with mode: 0644]
smarty/plugins/modifier.lower.php [new file with mode: 0644]
smarty/plugins/modifier.nl2br.php [new file with mode: 0644]
smarty/plugins/modifier.regex_replace.php [new file with mode: 0644]
smarty/plugins/modifier.replace.php [new file with mode: 0644]
smarty/plugins/modifier.spacify.php [new file with mode: 0644]
smarty/plugins/modifier.string_format.php [new file with mode: 0644]
smarty/plugins/modifier.strip.php [new file with mode: 0644]
smarty/plugins/modifier.strip_tags.php [new file with mode: 0644]
smarty/plugins/modifier.truncate.php [new file with mode: 0644]
smarty/plugins/modifier.upper.php [new file with mode: 0644]
smarty/plugins/modifier.wordwrap.php [new file with mode: 0644]
smarty/plugins/outputfilter.trimwhitespace.php [new file with mode: 0644]
smarty/plugins/shared.escape_special_chars.php [new file with mode: 0644]
smarty/plugins/shared.make_timestamp.php [new file with mode: 0644]
template.php [new file with mode: 0644]
templates/blank.gif [new file with mode: 0644]
templates/entry_edit.tpl [new file with mode: 0644]
templates/entry_show.tpl [new file with mode: 0644]
templates/entry_vcf.tpl [new file with mode: 0644]
templates/error.tpl [new file with mode: 0644]
templates/extended_edit.tpl [new file with mode: 0644]
templates/extended_show.tpl [new file with mode: 0644]
templates/footer.tpl [new file with mode: 0644]
templates/header.tpl [new file with mode: 0644]
templates/help.tpl [new file with mode: 0644]
templates/import.tpl [new file with mode: 0644]
templates/importVCF.tpl [new file with mode: 0644]
templates/importVCF_entry.tpl [new file with mode: 0644]
templates/import_entry.tpl [new file with mode: 0644]
templates/ldaperror.tpl [new file with mode: 0644]
templates/list.tpl [new file with mode: 0644]
templates/list_entry.tpl [new file with mode: 0644]
templates/list_filter.tpl [new file with mode: 0644]
templates/login.tpl [new file with mode: 0644]
templates/orgs.tpl [new file with mode: 0644]
templates/pngbehavior.htc [new file with mode: 0644]
templates/style.css [new file with mode: 0644]
xml.php [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 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.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, 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 software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, 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 redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) 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; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+  5. 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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+  7. 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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Contact_Vcard_Parse.php b/Contact_Vcard_Parse.php
new file mode 100644 (file)
index 0000000..da1f16f
--- /dev/null
@@ -0,0 +1,836 @@
+<?php
+/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */ 
+// +----------------------------------------------------------------------+ 
+// | PHP version 4                                                        | 
+// +----------------------------------------------------------------------+ 
+// | Copyright (c) 1997-2002 The PHP Group                                | 
+// +----------------------------------------------------------------------+ 
+// | This source file is subject to version 2.0 of the PHP license,       | 
+// | that is bundled with this package in the file LICENSE, and is        | 
+// | available at through the world-wide-web at                           | 
+// | http://www.php.net/license/2_02.txt.                                 | 
+// | If you did not receive a copy of the PHP license and are unable to   | 
+// | obtain it through the world-wide-web, please send a note to          | 
+// | license@php.net so we can mail you a copy immediately.               | 
+// +----------------------------------------------------------------------+ 
+// | Authors: Paul M. Jones <pmjones@ciaweb.net>                          | 
+// +----------------------------------------------------------------------+ 
+// 
+// $Id: Contact_Vcard_Parse.php,v 1.1 2004/06/01 08:48:59 gohr Exp $ 
+
+
+/**
+* 
+* Parser for vCards.
+*
+* This class parses vCard 2.1 and 3.0 sources from file or text into a
+* structured array.
+* 
+* Usage:
+* 
+* <code>
+*     // include this class file
+*     require_once 'Contact_Vcard_Parse.php';
+*     
+*     // instantiate a parser object
+*     $parse = new Contact_Vcard_Parse();
+*     
+*     // parse a vCard file and store the data
+*     // in $cardinfo
+*     $cardinfo = $parse->fromFile('sample.vcf');
+*     
+*     // view the card info array
+*     echo '<pre>';
+*     print_r($cardinfo);
+*     echo '</pre>';
+* </code>
+* 
+*
+* @author Paul M. Jones <pmjones@ciaweb.net>
+* 
+* @package Contact_Vcard_Parse
+* 
+* @version 1.30
+* 
+*/
+
+class Contact_Vcard_Parse {
+    
+    
+    /**
+    * 
+    * Reads a file for parsing, then sends it to $this->fromText()
+    * and returns the results.
+    * 
+    * @access public
+    * 
+    * @param array $filename The filename to read for vCard information.
+    * 
+    * @return array An array of of vCard information extracted from the
+    * file.
+    * 
+    * @see Contact_Vcard_Parse::fromText()
+    * 
+    * @see Contact_Vcard_Parse::_fromArray()
+    * 
+    */
+    
+    function fromFile($filename, $decode_qp = true)
+    {
+        $text = $this->fileGetContents($filename);
+        
+        if ($text === false) {
+            return false;
+        } else {
+            // dump to, and get return from, the fromText() method.
+            return $this->fromText($text, $decode_qp);
+        }
+    }
+    
+    
+    /**
+    * 
+    * Reads the contents of a file.  Included for users whose PHP < 4.3.0.
+    * 
+    * @access public
+    * 
+    * @param array $filename The filename to read for vCard information.
+    * 
+    * @return string|bool The contents of the file if it exists and is
+    * readable, or boolean false if not.
+    * 
+    * @see Contact_Vcard_Parse::fromFile()
+    * 
+    */
+    
+    function fileGetContents($filename)
+    {
+        if (file_exists($filename) &&
+            is_readable($filename)) {
+            
+            $text = '';
+            $len = filesize($filename);
+            
+            $fp = fopen($filename, 'r');
+            while ($line = fread($fp, filesize($filename))) {
+                $text .= $line;
+            }
+            fclose($fp);
+            
+            return $text;
+            
+        } else {
+        
+            return false;
+            
+        }
+    }
+    
+    
+    /**
+    * 
+    * Prepares a block of text for parsing, then sends it through and
+    * returns the results from $this->fromArray().
+    * 
+    * @access public
+    * 
+    * @param array $text A block of text to read for vCard information.
+    * 
+    * @return array An array of vCard information extracted from the
+    * source text.
+    * 
+    * @see Contact_Vcard_Parse::_fromArray()
+    * 
+    */
+    
+    function fromText($text, $decode_qp = true)
+    {
+        // convert all kinds of line endings to Unix-standard and get
+        // rid of double blank lines.
+        $this->convertLineEndings($text);
+        
+        // unfold lines.  concat two lines where line 1 ends in \n and
+        // line 2 starts with a whitespace character.  only removes
+        // the first whitespace character, leaves others in place.
+        $fold_regex = '(\n)([ |\t])';
+        $text = preg_replace("/$fold_regex/i", "", $text);
+        
+        // massage for Macintosh OS X Address Book (remove nulls that
+        // Address Book puts in for unicode chars)
+        $text = str_replace("\x00", '', $text);
+        
+        // convert the resulting text to an array of lines
+        $lines = explode("\n", $text);
+        
+        // parse the array of lines and return vCard info
+        return $this->_fromArray($lines, $decode_qp);
+    }
+    
+    
+    /**
+    * 
+    * Converts line endings in text.
+    * 
+    * Takes any text block and converts all line endings to UNIX
+    * standard. DOS line endings are \r\n, Mac are \r, and UNIX is \n.
+    *
+    * NOTE: Acts on the text block in-place; does not return a value.
+    * 
+    * @access public
+    * 
+    * @param string $text The string on which to convert line endings.
+    * 
+    * @return void
+    * 
+    */
+    
+    function convertLineEndings(&$text)
+    {
+        // DOS
+        $text = str_replace("\r\n", "\n", $text);
+        
+        // Mac
+        $text = str_replace("\r", "\n", $text);
+    }
+    
+    
+    /**
+    * 
+    * Splits a string into an array at semicolons.  Honors backslash-
+    * escaped semicolons (i.e., splits at ';' not '\;').
+    * 
+    * @access public
+    * 
+    * @param string $text The string to split into an array.
+    * 
+    * @param bool $convertSingle If splitting the string results in a
+    * single array element, return a string instead of a one-element
+    * array.
+    * 
+    * @return mixed An array of values, or a single string.
+    * 
+    */
+    
+    function splitBySemi($text, $convertSingle = false)
+    {
+        // we use these double-backs (\\) because they get get converted
+        // to single-backs (\) by preg_split.  the quad-backs (\\\\) end
+        // up as as double-backs (\\), which is what preg_split requires
+        // to indicate a single backslash (\). what a mess.
+        $regex = '(?<!\\\\)(\;)';
+        $tmp = preg_split("/$regex/i", $text);
+        
+        // if there is only one array-element and $convertSingle is
+        // true, then return only the value of that one array element
+        // (instead of returning the array).
+        if ($convertSingle && count($tmp) == 1) {
+            return $tmp[0];
+        } else {
+            return $tmp;
+        }
+    }
+    
+    
+    /**
+    * 
+    * Splits a string into an array at commas.  Honors backslash-
+    * escaped commas (i.e., splits at ',' not '\,').
+    * 
+    * @access public
+    * 
+    * @param string $text The string to split into an array.
+    * 
+    * @param bool $convertSingle If splitting the string results in a
+    * single array element, return a string instead of a one-element
+    * array.
+    * 
+    * @return mixed An array of values, or a single string.
+    * 
+    */
+    
+    function splitByComma($text, $convertSingle = false)
+    {
+        // we use these double-backs (\\) because they get get converted
+        // to single-backs (\) by preg_split.  the quad-backs (\\\\) end
+        // up as as double-backs (\\), which is what preg_split requires
+        // to indicate a single backslash (\). ye gods, how ugly.
+        $regex = '(?<!\\\\)(\,)';
+        $tmp = preg_split("/$regex/i", $text);
+        
+        // if there is only one array-element and $convertSingle is
+        // true, then return only the value of that one array element
+        // (instead of returning the array).
+        if ($convertSingle && count($tmp) == 1) {
+            return $tmp[0];
+        } else {
+            return $tmp;
+        }
+    }
+    
+    
+    /**
+    * 
+    * Used to make string human-readable after being a vCard value.
+    * 
+    * Converts...
+    *     \; => ;
+    *     \, => ,
+    *     literal \n => newline
+    * 
+    * @access public
+    * 
+    * @param mixed $text The text to unescape.
+    * 
+    * @return void
+    * 
+    */
+    
+    function unescape(&$text)
+    {
+        if (is_array($text)) {
+            foreach ($text as $key => $val) {
+                $this->unescape($val);
+                $text[$key] = $val;
+            }
+        } else {
+            $text = str_replace('\;', ';', $text);
+            $text = str_replace('\,', ',', $text);
+            $text = str_replace('\n', "\n", $text);
+        }
+    }
+    
+    
+    /**
+    *
+    * Emulated destructor.
+    *
+    * @access private
+    * @return boolean true
+    *
+    */
+    
+    function _Contact_Vcard_Parse()
+    {
+        return true;
+    }
+    
+    
+    /**
+    * 
+    * Parses an array of source lines and returns an array of vCards.
+    * Each element of the array is itself an array expressing the types,
+    * parameters, and values of each part of the vCard. Processes both
+    * 2.1 and 3.0 vCard sources.
+    *
+    * @access private
+    * 
+    * @param array $source An array of lines to be read for vCard
+    * information.
+    * 
+    * @return array An array of of vCard information extracted from the
+    * source array.
+    * 
+    */
+    
+    function _fromArray($source, $decode_qp = true)
+    {
+        // the info array will hold all resulting vCard information.
+        $info = array();
+        
+        // tells us whether the source text indicates the beginning of a
+        // new vCard with a BEGIN:VCARD tag.
+        $begin = false;
+        
+        // holds information about the current vCard being read from the
+        // source text.
+        $card = array();
+        
+        // loop through each line in the source array
+        foreach ($source as $line) {
+            
+            // if the line is blank, skip it.
+            if (trim($line) == '') {
+                continue;
+            }
+            
+            // find the first instance of ':' on the line.  The part
+            // to the left of the colon is the type and parameters;
+            // the part to the right of the colon is the value data.
+            $pos = strpos($line, ':');
+            
+            // if there is no colon, skip the line.
+            if ($pos === false) {
+                continue;
+            }
+            
+            // get the left and right portions
+            $left = trim(substr($line, 0, $pos));
+            $right = trim(substr($line, $pos+1, strlen($line)));
+            
+            // have we started yet?
+            if (! $begin) {
+                
+                // nope.  does this line indicate the beginning of
+                // a new vCard?
+                if (strtoupper($left) == 'BEGIN' &&
+                    strtoupper($right) == 'VCARD') {
+                    
+                    // tell the loop that we've begun a new card
+                    $begin = true;
+                }
+                
+                // regardless, loop to the next line of source. if begin
+                // is still false, the next loop will check the line. if
+                // begin has now been set to true, the loop will start
+                // collecting card info.
+                continue;
+                
+            } else {
+                
+                // yep, we've started, but we don't know how far along
+                // we are in the card. is this the ending line of the
+                // current vCard?
+                if (strtoupper($left) == 'END' &&
+                    strtoupper($right) == 'VCARD') {
+                    
+                    // yep, we're done. keep the info from the current
+                    // card...
+                    $info[] = $card;
+                    
+                    // ...and reset to grab a new card if one exists in
+                    // the source array.
+                    $begin = false;
+                    $card = array();
+                    
+                } else {
+                    
+                    // we're not on an ending line, so collect info from
+                    // this line into the current card. split the
+                    // left-portion of the line into a type-definition
+                    // (the kind of information) and parameters for the
+                    // type.
+                    $typedef = $this->_getTypeDef($left);
+                    $params = $this->_getParams($left);
+                    
+                    // if we are decoding quoted-printable, do so now.
+                    // QUOTED-PRINTABLE is not allowed in version 3.0,
+                    // but we don't check for versioning, so we do it
+                    // regardless.  ;-)
+                    $this->_decode_qp($params, $right);
+                    
+                    // now get the value-data from the line, based on
+                    // the typedef
+                    switch ($typedef) {
+                        
+                    case 'N':
+                        // structured name of the person
+                        $value = $this->_parseN($right);
+                        break;
+                        
+                    case 'ADR':
+                        // structured address of the person
+                        $value = $this->_parseADR($right);
+                        break;
+                        
+                    case 'NICKNAME':
+                        // nicknames
+                        $value = $this->_parseNICKNAME($right);
+                        break;
+                        
+                    case 'ORG':
+                        // organizations the person belongs to
+                        $value = $this->_parseORG($right);
+                        break;
+                        
+                    case 'CATEGORIES':
+                        // categories to which this card is assigned
+                        $value = $this->_parseCATEGORIES($right);
+                        break;
+                        
+                    case 'GEO':
+                        // geographic coordinates
+                        $value = $this->_parseGEO($right);
+                        break;
+                        
+                    default:
+                        // by default, just grab the plain value. keep
+                        // as an array to make sure *all* values are
+                        // arrays.  for consistency. ;-)
+                        $value = array(array($right));
+                        break;
+                    }
+                    
+                    // add the type, parameters, and value to the
+                    // current card array.  note that we allow multiple
+                    // instances of the same type, which might be dumb
+                    // in some cases (e.g., N).
+                    $card[$typedef][] = array(
+                        'param' => $params,
+                        'value' => $value
+                    );
+                }
+            }
+        }
+        
+        $this->unescape($info);
+        return $info;
+    }
+    
+    
+    /**
+    *
+    * Takes a vCard line and extracts the Type-Definition for the line.
+    * 
+    * @access private
+    *
+    * @param string $text A left-part (before-the-colon part) from a
+    * vCard line.
+    * 
+    * @return string The type definition for the line.
+    * 
+    */
+    
+    function _getTypeDef($text)
+    {
+        // split the text by semicolons
+        $split = $this->splitBySemi($text);
+        
+        // only return first element (the typedef)
+        return $split[0];
+    }
+    
+    
+    /**
+    *
+    * Finds the Type-Definition parameters for a vCard line.
+    * 
+    * @access private
+    * 
+    * @param string $text A left-part (before-the-colon part) from a
+    * vCard line.
+    * 
+    * @return mixed An array of parameters.
+    * 
+    */
+    
+    function _getParams($text)
+    {
+        // split the text by semicolons into an array
+        $split = $this->splitBySemi($text);
+        
+        // drop the first element of the array (the type-definition)
+        array_shift($split);
+        
+        // set up an array to retain the parameters, if any
+        $params = array();
+        
+        // loop through each parameter.  the params may be in the format...
+        // "TYPE=type1,type2,type3"
+        //    ...or...
+        // "TYPE=type1;TYPE=type2;TYPE=type3"
+        foreach ($split as $full) {
+            
+            // split the full parameter at the equal sign so we can tell
+            // the parameter name from the parameter value
+            $tmp = explode("=", $full);
+            
+            // the key is the left portion of the parameter (before
+            // '='). if in 2.1 format, the key may in fact be the
+            // parameter value, not the parameter name.
+            $key = strtoupper(trim($tmp[0]));
+            
+            // get the parameter name by checking to see if it's in
+            // vCard 2.1 or 3.0 format.
+            $name = $this->_getParamName($key);
+            
+            // list of all parameter values
+            $listall = trim($tmp[1]);
+            
+            // if there is a value-list for this parameter, they are
+            // separated by commas, so split them out too.
+            $list = $this->splitByComma($listall);
+            
+            // now loop through each value in the parameter and retain
+            // it.  if the value is blank, that means it's a 2.1-style
+            // param, and the key itself is the value.
+            foreach ($list as $val) {
+                if (trim($val) != '') {
+                    // 3.0 formatted parameter
+                    $params[$name][] = trim($val);
+                } else {
+                    // 2.1 formatted parameter
+                    $params[$name][] = $key;
+                }
+            }
+            
+            // if, after all this, there are no parameter values for the
+            // parameter name, retain no info about the parameter (saves
+            // ram and checking-time later).
+            if (count($params[$name]) == 0) {
+                unset($params[$name]);
+            }
+        }
+        
+        // return the parameters array.
+        return $params;
+    }
+    
+    
+    /**
+    *
+    * Looks at the parameters of a vCard line; if one of them is
+    * ENCODING[] => QUOTED-PRINTABLE then decode the text in-place.
+    * 
+    * @access private
+    * 
+    * @param array $params A parameter array from a vCard line.
+    * 
+    * @param string $text A right-part (after-the-colon part) from a
+    * vCard line.
+    *
+    * @return void
+    * 
+    */
+    
+    function _decode_qp(&$params, &$text)
+    {
+        // loop through each parameter
+        foreach ($params as $param_key => $param_val) {
+            
+            // check to see if it's an encoding param
+            if (trim(strtoupper($param_key)) == 'ENCODING') {
+            
+                // loop through each encoding param value
+                foreach ($param_val as $enc_key => $enc_val) {
+                
+                    // if any of the values are QP, decode the text
+                    // in-place and return
+                    if (trim(strtoupper($enc_val)) == 'QUOTED-PRINTABLE') {
+                        $text = quoted_printable_decode($text);
+                        return;
+                    }
+                }
+            }
+        }
+    }
+    
+    
+    /**
+    * 
+    * Returns parameter names from 2.1-formatted vCards.
+    *
+    * The vCard 2.1 specification allows parameter values without a
+    * name. The parameter name is then determined from the unique
+    * parameter value.
+    * 
+    * Shamelessly lifted from Frank Hellwig <frank@hellwig.org> and his
+    * vCard PHP project <http://vcardphp.sourceforge.net>.
+    * 
+    * @access private
+    * 
+    * @param string $value The first element in a parameter name-value
+    * pair.
+    * 
+    * @return string The proper parameter name (TYPE, ENCODING, or
+    * VALUE).
+    * 
+    */
+     
+    function _getParamName($value)
+    {
+        static $types = array (
+            'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
+            'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER',
+            'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
+            'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD',
+            'INTERNET', 'IBMMAIL', 'MCIMAIL',
+            'POWERSHARE', 'PRODIGY', 'TLX', 'X400',
+            'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB',
+            'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME',
+            'MPEG', 'MPEG2', 'AVI',
+            'WAVE', 'AIFF', 'PCM',
+            'X509', 'PGP'
+        );
+        
+        // CONTENT-ID added by pmj
+        static $values = array (
+            'INLINE', 'URL', 'CID', 'CONTENT-ID'
+        );
+        
+        // 8BIT added by pmj
+        static $encodings = array (
+            '7BIT', '8BIT', 'QUOTED-PRINTABLE', 'BASE64'
+        );
+        
+        // changed by pmj to the following so that the name defaults to
+        // whatever the original value was.  Frank Hellwig's original
+        // code was "$name = 'UNKNOWN'".
+        $name = $value;
+        
+        if (in_array($value, $types)) {
+            $name = 'TYPE';
+        } elseif (in_array($value, $values)) {
+            $name = 'VALUE';
+        } elseif (in_array($value, $encodings)) {
+            $name = 'ENCODING';
+        }
+        
+        return $name;
+    }
+    
+    
+    /**
+    *
+    * Parses a vCard line value identified as being of the "N"
+    * (structured name) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    */
+    
+    function _parseN($text)
+    {
+        $tmp = $this->splitBySemi($text);
+        return array(
+            $this->splitByComma($tmp[0]), // family (last)
+            $this->splitByComma($tmp[1]), // given (first)
+            $this->splitByComma($tmp[2]), // addl (middle)
+            $this->splitByComma($tmp[3]), // prefix
+            $this->splitByComma($tmp[4])  // suffix
+        );
+    }
+    
+    
+    /**
+    *
+    * Parses a vCard line value identified as being of the "ADR"
+    * (structured address) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    */
+    
+    function _parseADR($text)
+    {
+        $tmp = $this->splitBySemi($text);
+        return array(
+            $this->splitByComma($tmp[0]), // pob
+            $this->splitByComma($tmp[1]), // extend
+            $this->splitByComma($tmp[2]), // street
+            $this->splitByComma($tmp[3]), // locality (city)
+            $this->splitByComma($tmp[4]), // region (state)
+            $this->splitByComma($tmp[5]), // postcode (ZIP)
+            $this->splitByComma($tmp[6])  // country
+        );
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "NICKNAME"
+    * (informal or descriptive name) type-defintion.
+    *
+    * @access private
+    * 
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of nicknames.
+    *
+    */
+    
+    function _parseNICKNAME($text)
+    {
+        return array($this->splitByComma($text));
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "ORG"
+    * (organizational info) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of organizations; each element of the array
+    * is itself an array, which indicates primary organization and
+    * sub-organizations.
+    *
+    */
+    
+    function _parseORG($text)
+    {
+        $tmp = $this->splitbySemi($text);
+        $list = array();
+        foreach ($tmp as $val) {
+            $list[] = array($val);
+        }
+        
+        return $list;
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "CATEGORIES"
+    * (card-category) type-defintion.
+    *
+    * @access private
+    * 
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return mixed An array of categories.
+    *
+    */
+    
+    function _parseCATEGORIES($text)
+    {
+        return array($this->splitByComma($text));
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "GEO"
+    * (geographic coordinate) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return mixed An array of lat-lon geocoords.
+    *
+    */
+    
+    function _parseGEO($text)
+    {
+        $tmp = $this->splitBySemi($text);
+        return array(
+            array($tmp[0]), // lat
+            array($tmp[1])  // lon
+        );
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..a69db64
--- /dev/null
+++ b/README
@@ -0,0 +1,107 @@
+=== ABOUT ===
+
+LDAPab is a webbased address book for small companies. It features a public
+address book which is writable for all company staff and a personal address
+book for each staff member. LDAPab require an already setup LDAP server to
+authenticate users.
+
+=== REQUIREMENTS ===
+
+You need to have a running LDAP Server with some users (objectClass=posixUser)
+in it. It is important that you already have these users because LDAPab uses
+them to authenticate.
+
+You need a webserver (peferable Apache) with PHP4. PHP4 needs to be compiled
+with LDAP support (compiled in or as extension)
+
+=== SETUP LDAPab ===
+
+Copy the contents of the .tgz file to a directory below your webserver document
+root. Make sure the webserver is able to write to the cache directory which is
+used for the template engines cache files. A 'chmod 777 cache' will do.
+
+Open the config.php file in your favourite editor and edit the options
+according to your needs. Some knowlege about LDAP may come in handy.
+
+=== SETUP THE LDAP SERVER ===
+
+The following only describes what to do for open-ldap 2.0! If you use any
+other LDAP server you're left to our own.
+
+You need to include the inetOrgPerson schema in you slapd.conf:
+
+  include /etc/ldap/schema/inetorgperson.schema
+
+If youwant to use the extended schema (currently only for birthday support) you
+need to copy the ldapab.schema file to /etc/ldap/schema/ and include it, too.
+Be sure to enable the extended option in config.php.
+
+  include         /etc/ldap/schema/ldapab.schema
+
+To setup the access rights add the following statement to your slapd.conf. Of
+course you have to replace "o=cosmocode,c=de" with your own root DN. The first
+entry may have to adjusted also if your users aren't stored in a ou=people
+entry. Make sure you include these lines before other access control lists that
+match these parts of the ldap tree. If you are unsure have a look at the
+included slapd.example file
+
+  access  to dn="(.*,)?ou=contacts,cn=([^,]+),ou=people,(.*)$"
+          by dn="cn=$2,ou=people,$3" write
+          by * none
+
+  access to dn.subtree="ou=contacts,o=cosmocode,c=de"
+         by users write
+         by * read
+
+After this modifications you have to restart the slapd server.
+
+The last thing is to add the contacts leafs to your LDAP tree. You'll need to
+add a global contacts leaf at top.
+
+  eg: 'ou=contacts,o=cosmocode,c=de'
+  
+And a contacts leaf for every user.
+
+  eg: 'ou=contacts,cn=Joe Schmoe,ou=people,o=cosmocode,c=de'
+
+
+If you made everything correct you can point your webbrowser to the newly
+installed LDAPab and use it :-) If you encounter problems try to enable logging
+in your LDAP server to see what the problem is.
+
+=== FEEDBACK, PATCHES, SUPPORT ===
+
+This software was developed to satisfy a need in our company. We give it to the
+public because it may be helpful to other companies with the same needs.
+However this is not a supported product. We like to get feedback and patches
+but we can't promise to answer, to fix bugs or include feature wishes. However
+don't hesitate to contact us but understand that our core business has priority
+over this project.
+
+To contact us send a mail to ldapab@cosmocode.de
+
+=== KNOWN BUGS ===
+
+LDAPab was only tested with open-ldap 2.1 - there may be problems with other
+LDAP Servers.
+
+=== LICENSE ===
+
+LDAPab - An LDAP based Company Address Book
+Copyright 2004 - CosmoCode GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+      
+See COPYING for details
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..10730a1
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+
+Photoupload
+
+marker
diff --git a/config.php b/config.php
new file mode 100644 (file)
index 0000000..31419dc
--- /dev/null
@@ -0,0 +1,34 @@
+<?
+
+  // Which LDAP Server to use?
+  $conf[ldapserver]  = 'ldap';
+
+  // What is the root dn on this Server?
+  $conf[ldaprootdn]  = 'o=cosmocode, c=de';
+
+  // Where are the user Accounts stored?
+  $conf[usertree]    = 'ou=people, '.$conf[ldaprootdn];
+
+  // How to match users? %u is replaced by the given login
+  $conf[userfilter]  = '(&(uid=%u)(objectClass=posixAccount))';
+
+  // Which language to use (see lang directory)
+  $conf[lang]        = 'de';
+
+  // Where to store public contacts?
+  $conf[publicbook]  = 'ou=contacts, '.$conf[ldaprootdn];
+
+  // Where to store private contacts (relative to $conf[usertree])
+  $conf[privatebook] = 'ou=contacts';
+
+  // Should the additional schema ldapab.schema be used? (0|1)
+  $conf[extended]    = 1;
+
+  // Dateformat for birthdays when using extended schema
+  // see http://www.php.net/manual/en/function.strftime.php
+  #$conf[dateformat]  = '%Y/%m/%d';
+  $conf[dateformat]  = '%d.%m.%Y';
+
+  // Force recompilation of smarty templates?
+  $conf[smartycompile] = 0;
+?>
diff --git a/entries.conf b/entries.conf
new file mode 100644 (file)
index 0000000..0809369
--- /dev/null
@@ -0,0 +1,20 @@
+dn                                  dn
+sn                                  name
+givenName                           givenname
+title                               title
+o                                   organization
+physicalDeliveryOfficeName          office
+postalAddress                       street
+postalCode                          zip
+l                                   location
+telephoneNumber                     phone
+facsimileTelephoneNumber            fax
+mobile                              mobile
+pager                               pager
+homePhone                           homephone
+homePostalAddress                   homestreet
+photo                               photo
+jpegPhoto                           photo
+labeledURI                          url
+description                         note
+manager                             manager
diff --git a/entry.php b/entry.php
new file mode 100644 (file)
index 0000000..a855053
--- /dev/null
+++ b/entry.php
@@ -0,0 +1,186 @@
+<?
+  require_once('init.php');
+  ldap_login();
+
+  $users = get_users();
+
+  //select template to use
+  if( $_SESSION[ldapab][username] &&
+     ($_REQUEST[mode]=='edit' || $_REQUEST[mode]=='copy')){
+    $template='entry_edit.tpl';
+  }elseif($_REQUEST[mode]=='vcf'){
+    $template='entry_vcf.tpl';
+  }else{
+    $template='entry_show.tpl';
+  }
+
+  $dn = $_REQUEST[dn];
+  #$dn = 'cn=bar foo, ou=contacts, o=cosmocode, c=de';
+
+  //save data if asked for
+  if($_SESSION[ldapab][username] && $_REQUEST[save]){
+    $_REQUEST[entry][jpegPhoto][]=_getUploadData();
+    $dn = _saveData();
+  }
+
+  if(empty($dn)){
+    if(!$_REQUEST[mode]=='edit'){
+      $smarty->assign('error','No dn was given');
+      $template = 'error.tpl';
+    }
+  }elseif($_REQUEST[del]){
+    _delEntry($dn);
+  }elseif(!_fetchData($dn)){
+    $smarty->assign('error',"The requested entry '$dn' was not found");
+    $template = 'error.tpl';
+  }
+
+  //prepare templates
+  $smarty->assign('dn',$dn);
+  $smarty->assign('managers',$users);
+  tpl_std();
+  tpl_orgs();
+  tpl_markers();
+  //display templates
+  if($_REQUEST[mode]=='vcf'){
+    $entry = $smarty->get_template_vars('entry');
+    $filename = $entry[givenname].'_'.$entry[name].'.vcf';
+    header("Content-Disposition: attachment; filename=\"$filename\"");
+    header("Content-type: text/x-vcard; name=\"$filename\"");
+    $smarty->display($template);
+  }else{
+    $smarty->display('header.tpl');
+    $smarty->display($template);
+    $smarty->display('footer.tpl');
+  }
+
+  //--------------------------------------------------------------
+
+  /**
+   * fetches the Data from the LDAP directory and assigns it to
+   * the global smarty object using tpl_entry()
+   */
+  function _fetchData($dn){
+    global $LDAP_CON;
+    global $conf;
+    global $smarty;
+    global $users; //contains the users for manager role
+
+    $sr = ldap_search($LDAP_CON,$dn,'(objectClass=inetOrgPerson)');
+    if(!ldap_count_entries($LDAP_CON,$sr)){
+      return false;
+    }
+    $result = ldap_get_binentries($LDAP_CON, $sr);
+    $entry  = $result[0];
+
+    //remove dn from entry when copy
+    if($_REQUEST[mode] == 'copy'){
+      $entry[dn]='';
+    }
+
+    //assign entry to template:
+    tpl_entry($entry);
+
+/*print '<pre>';
+print_r($entry);
+print '</pre>';*/
+
+    // make username from dn for manager:
+    $smarty->assign('managername',$users[$entry[manager][0]]);
+    return true;
+  }
+
+  /**
+   * saves the data from $_REQUEST[entry] to the LDAP directory
+   *
+   * returns given or constructed dn
+   */
+  function _saveData(){
+    global $LDAP_CON;
+    global $conf;
+    $entries = namedentries();
+    $entries['mail']='mail';  //special field mail isn't in entries so we add it here
+    if($conf[extended]){
+      $entries['marker']='marker'; //same for marker inextended schema
+    }
+
+    $entry = $_REQUEST[entry];
+    $dn    = $_REQUEST[dn];
+    //construct new dn
+    $now    = time();
+    $newdn  = 'uid='.$now;
+    if($_REQUEST[type] == 'private'){
+      $newdn .= ', '.$conf[privatebook].', '.$_SESSION[ldapab][binddn];
+    }else{
+      $newdn .= ', '.$conf[publicbook];
+    }
+    $entry[cn]          = $entry[givenname].' '.$entry[name];;
+    $entry = prepare_ldap_entry($entry);
+
+/*print '<pre>';
+print_r($entry);
+print '</pre>';*/
+
+    if(empty($dn)){
+      //new entry
+      $entry[uid][] = $now;
+      $r = ldap_add($LDAP_CON,$newdn,$entry);
+      tpl_ldaperror();
+      return $newdn;
+    }else{
+      // in extended mode we have to make sure the right classes are set
+      if($conf[extended]){
+        ldap_store_objectclasses($dn,array('inetOrgPerson','contactPerson'));
+      }
+      //modify entry (touches only our attributes)
+      foreach (array_keys($entries) as $key){
+        if($key == 'dn'){
+          continue;
+        }elseif(empty($entry[$key])){
+          if($key == 'jpegPhoto' && !$_REQUEST['delphoto']){
+            continue;
+          }
+          unset($del);
+          $del[$key]=array();
+          $r = @ldap_mod_replace($LDAP_CON,$dn,$del);
+          tpl_ldaperror("del $key");
+        }else{
+          unset($add);
+          $add[$key]=$entry[$key];
+          $r = @ldap_mod_replace($LDAP_CON,$dn,$add);
+          tpl_ldaperror("mod $key");
+        }
+      }
+      return $dn;
+    }
+  }
+
+  /**
+   * does as the name says - delete the whole entry
+   */
+  function _delEntry($dn){
+    global $LDAP_CON;
+    if(ldap_full_delete($LDAP_CON,$dn,true)){
+      header("Location: index.php");
+      exit;
+    }
+  }
+
+  /**
+   * gets the binary data from an uploaded file
+   */
+  function _getUploadData(){
+    $file = $_FILES[photoupload];
+
+    if (is_uploaded_file($file[tmp_name])) {
+      if(preg_match('=image/p?jpe?g=',$file[type])){
+        $fh = fopen($file[tmp_name],'r');
+        $data = fread($fh,$file[size]);
+        fclose($fh);
+        unlink($file[tmp_name]);
+        return $data;
+      }
+    }
+    return '';
+  }
+?>
diff --git a/functions.php b/functions.php
new file mode 100644 (file)
index 0000000..e8ac472
--- /dev/null
@@ -0,0 +1,345 @@
+<?
+
+/**
+ * assigns some standard variables to smarty templates
+ */
+function smarty_std(){
+  global $smarty;
+  $smarty->assign('USER',$_SESSION[ldapab][username]);
+}
+
+/**
+ * Uses Username and Password from Session to initialize the LDAP handle
+ * If it fails it redirects to login.php
+ */
+function ldap_login(){
+  if(!empty($_SESSION[ldapab][username])){
+    //existing session! Check if valid
+    if($_COOKIE[ldapabconid] != $_SESSION[ldapab][conid]){
+      //session hijacking detected
+       header('Location: login.php?username=');
+       exit;
+    }
+  }
+
+  if(!do_ldap_bind($_SESSION[ldapab][username],
+                   $_SESSION[ldapab][password],
+                   $_SESSION[ldapab][binddn])){
+    header('Location: login.php?username=');
+    exit;
+  }
+}
+
+/**
+ * Creates a global LDAP connection handle called $LDAP_CON
+ */
+function do_ldap_bind($user,$pass,$dn=""){
+  global $conf;
+  global $LDAP_CON;
+  
+  //create global connection to LDAP if nessessary
+  if(!$LDAP_CON){
+    $LDAP_CON = ldap_connect($conf[ldapserver]);
+    if(!$LDAP_CON){
+      die("couldn't connect to LDAP server");
+    }
+  }
+
+  if(empty($dn)){
+    //anonymous bind to lookup users
+    if(!ldap_bind($LDAP_CON)){
+      die("can not bind anonymously");
+    }
+  
+    //when no user was given stay connected anonymous
+    if(empty($user)){
+      set_session('','','');
+      return true;
+    }
+
+    //get dn for given user
+    $filter = str_replace('%u',$user,$conf[userfilter]);
+    $sr = ldap_search($LDAP_CON, $conf[usertree], $filter);;
+    $result = ldap_get_entries($LDAP_CON, $sr);
+    if($result['count'] != 1){
+      set_session('','','');
+      return false;
+    }
+    $dn = $result[0]['dn'];
+  }
+
+  //bind with dn
+  if(ldap_bind($LDAP_CON,$dn,$pass)){
+    //bind successful -> set up session
+    set_session($user,$pass,$dn);
+    return true;
+  }
+  //bind failed -> remove session
+  set_session('','','');
+  return false;
+}
+
+/**
+ * saves user data to Session
+ */
+function set_session($user,$pass,$dn){
+  $rand = rand();
+  $_SESSION[ldapab][username]=$user;
+  $_SESSION[ldapab][binddn]  =$dn;
+  $_SESSION[ldapab][password]=$pass;
+  $_SESSION[ldapab][conid]   =$rand;
+  setcookie('ldapabconid',$rand,time()+60*60*24);
+}
+
+/**
+ * binary safe function to get all search result data.
+ * It will use ldap_get_values_len() instead and build the array
+ * note: it's similar with the array returned by ldap_get_entries()
+ * except it has no "count" elements
+ *
+ * @author: Original code by Ovidiu Geaboc <ogeaboc@rdanet.com>
+ */
+function ldap_get_binentries($conn,$srchRslt){
+  if(!@ldap_count_entries($conn,$srchRslt)){
+    return null;
+  }
+  $entry = ldap_first_entry($conn, $srchRslt);
+  $i=0;
+  do {
+    $dn = ldap_get_dn($conn,$entry);
+    $attrs = ldap_get_attributes($conn, $entry);
+    for($j=0; $j<$attrs['count']; $j++) {
+      $vals = ldap_get_values_len($conn, $entry,$attrs[$j]);
+      for($k=0; $k<$vals['count']; $k++){
+        $data[$i][$attrs[$j]][$k]=$vals[$k];
+      }
+    }
+    $data[$i]['dn']=$dn;
+    $i++;
+  }while ($entry = ldap_next_entry($conn, $entry));
+
+  return $data;
+}
+
+/**
+ * loads ldap names and their cleartext meanings from
+ * entries.conf file and returns it as hash
+ */
+function namedentries($flip=false){
+  global $conf;
+
+  $entries[dn]                         = 'dn';
+  $entries[sn]                         = 'name';
+  $entries[givenName]                  = 'givenname';
+  $entries[title]                      = 'title';
+  $entries[o]                          = 'organization';
+  $entries[physicalDeliveryOfficeName] = 'office';
+  $entries[postalAddress]              = 'street';
+  $entries[postalCode]                 = 'zip';
+  $entries[l]                          = 'location';
+  $entries[telephoneNumber]            = 'phone';
+  $entries[facsimileTelephoneNumber]   = 'fax';
+  $entries[mobile]                     = 'mobile';
+  $entries[pager]                      = 'pager';
+  $entries[homePhone]                  = 'homephone';
+  $entries[homePostalAddress]          = 'homestreet';
+  $entries[jpegPhoto]                  = 'photo';
+  $entries[labeledURI]                 = 'url';
+  $entries[description]                = 'note';
+  $entries[manager]                    = 'manager';
+  $entries[cn]                         = 'displayname';
+
+  if($conf[extended]){
+    $entries[anniversary]              = 'anniversary';
+  }
+
+  if($flip){
+    $entries = array_reverse($entries);
+    $entries = array_flip($entries);
+  }
+  return $entries;
+}
+
+/**
+ * Creates an array for submission to ldap from websitedata
+ */
+function prepare_ldap_entry($in){
+  global $conf;
+
+  //check dateformat
+  if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in[anniversary])){
+    $in[anniversary]='';
+  }
+
+  $entries = namedentries(true);
+  foreach(array_keys($in) as $key){
+    if(empty($entries[$key])){
+      $keyname=$key;
+    }else{
+      $keyname=$entries[$key];
+    }
+    if(is_array($in[$key])){
+      $out[$keyname] = $in[$key];
+    }else{
+      $out[$keyname][] = $in[$key];
+    }
+  }
+
+  //standard Objectclass
+  $out[objectclass][] = 'inetOrgPerson';
+  if($conf[extended]){
+    $out[objectclass][] = 'contactPerson';
+  }
+
+  utf8_encode_array($out);
+
+  return clear_array($out);
+}
+
+/**
+ * remove empty element from arrays recursively
+ *
+ * @author Original by <xntx@msn.com>
+ */
+function clear_array ( $a ) {
+  if ($a !== array()) {
+    $b = array();
+    foreach ( $a as $key => $value ) {
+        if (is_array($value)) {
+          if (clear_array($value) !== false) {
+            $b[$key] = clear_array ( $value );
+          }
+        } elseif ($value !== '') {
+          $b[$key] = $value;
+        }
+    }
+    if ($b !== array()) {
+        return $b;
+    } else {
+        return false;
+    }
+  } else {
+    return false;
+  }
+}
+
+/**
+ * deletes an entryfrom ldap - optional with recursion
+ *
+ * @author Original by <gabriel@hrz.uni-marburg.de>
+ */
+function ldap_full_delete($ds,$dn,$recursive=false){
+  if($recursive == false){
+    return(ldap_delete($ds,$dn));
+  }else{
+    //searching for sub entries
+    $sr=ldap_list($ds,$dn,"ObjectClass=*",array(""));
+    $info = ldap_get_entries($ds, $sr);
+    for($i=0;$i<$info['count'];$i++){
+      //deleting recursively sub entries
+      $result=myldap_delete($ds,$info[$i]['dn'],$recursive);
+      if(!$result){
+        //return result code, if delete fails
+        return($result);
+      }
+    }
+    return(ldap_delete($ds,$dn));
+  }
+}
+
+/**
+ * Returns all User Accounts as assoziative array
+ */
+function get_users(){
+  global $conf;
+  global $LDAP_CON;
+
+  $sr = ldap_list($LDAP_CON,$conf[usertree],"ObjectClass=inetOrgPerson");
+  $result = ldap_get_binentries($LDAP_CON, $sr);
+  if(count($result)){
+    foreach ($result as $entry){
+      if(!empty($entry[sn][0])){
+        $users[$entry[dn]] = $entry[givenName][0]." ".$entry[sn][0];
+      }
+    }
+  }
+  return $users; 
+}
+
+/**
+ * makes sure the given DN contains exactly one space
+ * after each ,
+ */
+function normalize_dn($dn){
+  $dn = preg_replace('/,/',', ',$dn);
+  $dn = preg_replace('/,\s+/',', ',$dn);
+  return $dn;
+}
+
+/**
+ * Merges the given classes with the existing ones
+ */
+function ldap_store_objectclasses($dn,$classes){
+  global $conf;
+  global $LDAP_CON;
+
+  $sr     = ldap_search($LDAP_CON,$dn,"objectClass=*",array('objectClass'));
+  $result = ldap_get_binentries($LDAP_CON, $sr);
+  $set    = $result[0][objectClass];
+  $set    = array_unique_renumber(array_merge($set,$classes));
+  $add[objectClass] = $set;
+
+  $r = @ldap_mod_replace($LDAP_CON,$dn,$add);
+  tpl_ldaperror();
+
+/*  print '<pre>';
+  print_r($set);
+  print '</pre>';*/
+}
+
+/**
+ * Makes array unique and renumbers the entries
+ *
+ * @author <kay_rules@yahoo.com>
+ */
+function array_unique_renumber($somearray){
+   $tmparr = array_unique($somearray);
+   $i=0;
+   foreach ($tmparr as $v) {
+       $newarr[$i] = $v;
+       $i++;
+   }
+   return $newarr;
+}
+
+/**
+ * Decodes UTF8 recursivly for the given array
+ */
+function utf8_decode_array(&$array) {
+  foreach (array_keys($array) as $key) {
+    if($key === 'dn') continue;
+    if($key === 'jpegPhoto') continue;
+    if (is_array($array[$key])) {
+      utf8_decode_array($array[$key]);
+    }else {
+      $array[$key] = utf8_decode($array[$key]);
+    }
+  }
+}
+
+/**
+ * Encodes the given array to UTF8 recursively
+ */
+function utf8_encode_array(&$array) {
+  foreach (array_keys($array) as $key) {
+    if($key === 'dn') continue;
+    if($key === 'jpegPhoto') continue;
+    if (is_array($array[$key])) {
+      utf8_encode_array($array[$key]);
+    }else {
+      $array[$key] = utf8_encode($array[$key]);
+    }
+  }
+}
+
+?>
diff --git a/help.php b/help.php
new file mode 100644 (file)
index 0000000..eaa5817
--- /dev/null
+++ b/help.php
@@ -0,0 +1,12 @@
+<?
+  require_once('init.php');
+  ldap_login();
+
+  //prepare templates
+  tpl_std();
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('help.tpl');
+  $smarty->display('footer.tpl');
+
+?>
diff --git a/img.php b/img.php
new file mode 100644 (file)
index 0000000..993a4fc
--- /dev/null
+++ b/img.php
@@ -0,0 +1,16 @@
+<?
+  require_once('init.php');
+  ldap_login();
+
+  $dn = $_REQUEST[dn];
+
+  $sr = ldap_search($LDAP_CON,$dn,'(objectClass=inetOrgPerson)',array('jpegPhoto'));
+  if(!ldap_count_entries($LDAP_CON,$sr)){
+    exit;
+  }
+  $result = ldap_get_binentries($LDAP_CON, $sr);
+  $entry  = $result[0];
+
+  header("Content-type: image/jpeg");
+  print $entry['jpegPhoto'][0];
+?>
diff --git a/import.php b/import.php
new file mode 100644 (file)
index 0000000..d1753cf
--- /dev/null
@@ -0,0 +1,166 @@
+<?
+  require_once('init.php');
+  require_once('Contact_Vcard_Parse.php');
+  require_once('xml.php');
+  ldap_login();
+
+  if(! $_SESSION[ldapab][username] ){
+    header("Location: login.php");
+    exit;
+  }
+
+  $error = '';
+  if(isset($_FILES['userfile'])){
+    if (is_uploaded_file($_FILES['userfile']['tmp_name'])){
+      if(preg_match('/\.vcf$/i', $_FILES['userfile']['name'])){
+        //parse VCF
+        $vcfparser = new Contact_Vcard_Parse();
+        $vcards    = $vcfparser->fromFile($_FILES['userfile']['tmp_name']);
+      }elseif(preg_match('/^addressbook\.xml$/i', $_FILES['userfile']['name'])){
+        //parse Zaurus Addressbook
+        $contacts  = XML_unserialize(@join('',@file($_FILES['userfile']['tmp_name'])));
+      }else{
+          $error = "Only *.vcf or addressbook.xml are accepted";
+      }
+    }else{
+      switch($_FILES['userfile']['error']){
+       case 0: //no error; possible file attack!
+         $error = "There was a problem with your upload.";
+         break;
+       case 1: //uploaded file exceeds the upload_max_filesize directive in php.ini
+         $error = "The file you are trying to upload is too big.";
+         break;
+       case 2: //uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
+         $error = "The file you are trying to upload is too big.";
+         break;
+       case 3: //uploaded file was only partially uploaded
+         $error = "The file you are trying upload was only partially uploaded.";
+         break;
+       case 4: //no file was uploaded
+         $error = "You must select a VCF file for upload.";
+         break;
+       default: //a default error, just in case!  :)
+         $error = "There was a problem with your upload.";
+         break;
+      }
+    }
+  }
+
+  //prepare templates for all found entries
+  $list = '';
+  if(count($vcards)){
+    foreach ($vcards as $vcard){
+      $entry = vcard_entry($vcard);
+      $smarty->clear_all_assign();
+      tpl_std();
+      $smarty->assign('entry',$entry);
+      $list .= $smarty->fetch('import_entry.tpl');
+    }
+  }elseif(count($contacts)){
+    foreach ($contacts['AddressBook']['Contacts']['Contact'] as $contact){
+      if (!is_array($contact)) continue;
+      $entry = zaurus_entry($contact);
+      $smarty->clear_all_assign();
+      tpl_std();
+      $smarty->assign('entry',$entry);
+      $list .= $smarty->fetch('import_entry.tpl');
+    }
+  }
+
+  //prepare templates
+  tpl_std();
+  tpl_orgs();
+  tpl_markers();
+  $smarty->assign('error',$error);
+  $smarty->assign('list',$list);
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('import.tpl');
+  $smarty->display('footer.tpl');
+
+
+function zaurus_entry($cnct){
+  $entry['name']         = $cnct['LastName'];
+  $entry['givenname']    = trim($cnct['FirstName'].' '.$cnct['MiddleName']);
+  $entry['title']        = $cnct['JobTitle'];
+  $entry['organization'] = $cnct['Company'];
+  $entry['office']       = $cnct['Office'];
+  $entry['note']         = $cnct['Notes'];
+  $entry['mail']         = split(' ',$cnct['Emails']);
+  $entry['street']       = $cnct['BusinessStreet'];
+  $entry['location']     = $cnct['BusinessCity'];
+  $entry['zip']          = $cnct['BusinessZip'];
+  $entry['homestreet']   = $cnct['HomeStreet']."\n". //str
+                           $cnct['HomeZip']." ".     //plz
+                           $cnct['HomeCity'];        //ort
+  $entry['homephone']    = $cnct['HomePhone'];
+  $entry['phone']        = $cnct['BusinessPhone'];
+  $entry['fax']          = empty($cnct['BusinessFax']) ? $cnct['HomeFax'] : $cnct['BusinessFax'];
+  $entry['mobile']       = empty($cnct['HomeMobile']) ? $cnct['BusinessMobile'] : $cnct['HomeMobile'];
+  $entry['fax']          = empty($cnct['BusinessFax']) ? $cnct['HomeFax'] : $cnct['BusinessFax'];
+
+#  $entry['anniversary'] = $cnt[''];
+
+  utf8_decode_array($entry);
+  return $entry;
+}
+
+function vcard_entry($vcf){
+  $entry['name']         = $vcf['N'][0]['value'][0][0];
+  $entry['givenname']    = trim($vcf['N'][0]['value'][1][0].' '.$vcf['N'][0]['value'][2][0]);
+  $entry['title']        = $vcf['N'][0]['value'][3][0];
+  $entry['organization'] = $vcf['ORG'][0]['value'][0][0];
+  $entry['office']       = $vcf['ORG'][0]['value'][1][0];
+  $entry['note']         = $vcf['NOTE'][0]['value'][0][0];
+  $entry['url']          = $vcf['URL'][0]['value'][0][0];
+  $bday                  = $vcf['BDAY'][0]['value'][0][0];
+  $entry['anniversary']  = substr($bday,0,4).'-'.substr($bday,4,2).'-'.substr($bday,6,2);
+
+  foreach($vcf['TEL'] as $tel){
+    if(   empty($entry['phone']) &&
+          array_search('WORK',$tel['param']['TYPE']) !== FALSE &&
+          array_search('VOICE',$tel['param']['TYPE']) !== FALSE){
+      // Work phone
+      $entry['phone'] = $tel['value'][0][0];
+    }elseif(empty($entry['fax']) &&
+            array_search('FAX',$tel['param']['TYPE']) !== FALSE){
+      $entry['fax'] = $tel['value'][0][0];
+    }elseif(empty($entry['mobile']) &&
+            array_search('CELL',$tel['param']['TYPE']) !== FALSE){
+      $entry['mobile'] = $tel['value'][0][0];
+    }elseif(empty($entry['pager']) &&
+            array_search('PAGER',$tel['param']['TYPE']) !== FALSE){
+      $entry['pager'] = $tel['value'][0][0];
+    }elseif(empty($entry['homephone']) &&
+            array_search('HOME',$tel['param']['TYPE']) !== FALSE &&
+            array_search('VOICE',$tel['param']['TYPE']) !== FALSE){
+      $entry['homephone'] = $tel['value'][0][0];
+    }
+  }
+  foreach($vcf['EMAIL'] as $mail){
+    $entry['mail'][] = $mail['value'][0][0];
+  }
+  foreach($vcf['ADR'] as $adr){
+    if(array_search('HOME',$adr['param']['TYPE']) !== FALSE){
+      $entry['homestreet'] = $adr['value'][2][0]."\n". //str
+                             $adr['value'][5][0]." ". //plz
+                             $adr['value'][3][0];      //ort
+
+    }elseif(array_search('WORK',$adr['param']['TYPE']) !== FALSE){
+      $entry['street']   = $adr['value'][2][0];
+      $entry['location'] = $adr['value'][3][0];
+      $entry['zip']      = $adr['value'][5][0];
+    }
+  }
+
+/*
+  print '<pre>';
+  print_r($entry);
+  print '</pre>';
+*/
+
+  return $entry;
+}
+
+
+?>
diff --git a/importVCF.php b/importVCF.php
new file mode 100644 (file)
index 0000000..bc9ea65
--- /dev/null
@@ -0,0 +1,127 @@
+<?
+  require_once('init.php');
+  require_once('Contact_Vcard_Parse.php');
+  ldap_login();
+
+  if(! $_SESSION[ldapab][username] ){
+    header("Location: login.php");
+    exit;
+  }
+
+  $error = '';
+  if(isset($_FILES['userfile'])){
+    if(preg_match('/\.vcf$/i', $_FILES['userfile']['name'])){
+      if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
+        //parse VCF
+                               $vcfparser = new Contact_Vcard_Parse();
+                               $vcards = $vcfparser->fromFile($_FILES['userfile']['tmp_name']);
+      }else{
+        switch($_FILES['userfile']['error']){
+         case 0: //no error; possible file attack!
+           $error = "There was a problem with your upload.";
+           break;
+         case 1: //uploaded file exceeds the upload_max_filesize directive in php.ini
+           $error = "The file you are trying to upload is too big.";
+           break;
+         case 2: //uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
+           $error = "The file you are trying to upload is too big.";
+           break;
+         case 3: //uploaded file was only partially uploaded
+           $error = "The file you are trying upload was only partially uploaded.";
+           break;
+         case 4: //no file was uploaded
+           $error = "You must select a VCF file for upload.";
+           break;
+         default: //a default error, just in case!  :)
+           $error = "There was a problem with your upload.";
+           break;
+                 }
+      }
+    }else{
+      $error = "Only VCF extension is allowed";
+    }
+  }
+
+  //prepare templates for all found VCARDs
+  $list = '';
+  if (count($vcards)){
+         foreach ($vcards as $vcard){
+         $entry = vcard_entry($vcard);
+       $smarty->clear_all_assign();
+               tpl_std();
+           $smarty->assign('entry',$entry);
+         $list .= $smarty->fetch('importVCF_entry.tpl');
+         }
+       }
+
+  //prepare templates
+  tpl_std();
+  tpl_orgs();
+  tpl_markers();
+  $smarty->assign('error',$error);
+  $smarty->assign('list',$list);
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('importVCF.tpl');
+  $smarty->display('footer.tpl');
+
+
+function vcard_entry($vcf){
+  $entry['name'] = $vcf['N'][0]['value'][0][0];
+  $entry['givenname'] = trim($vcf['N'][0]['value'][1][0].' '.$vcf['N'][0]['value'][2][0]);
+  $entry['title'] = $vcf['N'][0]['value'][3][0];
+  $entry['organization'] = $vcf['ORG'][0]['value'][0][0];
+  $entry['office'] = $vcf['ORG'][0]['value'][1][0];
+  $entry['note'] = $vcf['NOTE'][0]['value'][0][0];
+  $entry['url'] = $vcf['URL'][0]['value'][0][0];
+  $bday = $vcf['BDAY'][0]['value'][0][0];
+  $entry['anniversary'] = substr($bday,0,4).'-'.substr($bday,4,2).'-'.substr($bday,6,2);
+
+  foreach($vcf['TEL'] as $tel){
+    if(   empty($entry['phone']) &&
+          array_search('WORK',$tel['param']['TYPE']) !== FALSE &&
+          array_search('VOICE',$tel['param']['TYPE']) !== FALSE){
+      // Work phone
+                       $entry['phone'] = $tel['value'][0][0];
+    }elseif(empty($entry['fax']) &&
+            array_search('FAX',$tel['param']['TYPE']) !== FALSE){
+                       $entry['fax'] = $tel['value'][0][0];
+    }elseif(empty($entry['mobile']) &&
+            array_search('CELL',$tel['param']['TYPE']) !== FALSE){
+                       $entry['mobile'] = $tel['value'][0][0];
+    }elseif(empty($entry['pager']) &&
+            array_search('PAGER',$tel['param']['TYPE']) !== FALSE){
+                       $entry['pager'] = $tel['value'][0][0];
+    }elseif(empty($entry['homephone']) &&
+            array_search('HOME',$tel['param']['TYPE']) !== FALSE &&
+            array_search('VOICE',$tel['param']['TYPE']) !== FALSE){
+                       $entry['homephone'] = $tel['value'][0][0];
+    }
+  }
+  foreach($vcf['EMAIL'] as $mail){
+    $entry['mail'][] = $mail['value'][0][0];
+  }
+  foreach($vcf['ADR'] as $adr){
+    if(array_search('HOME',$adr['param']['TYPE']) !== FALSE){
+      $entry['homestreet'] = $adr['value'][2][0]."\n". //str
+                                                                                                                $adr['value'][5][0]." ". //plz
+                             $adr['value'][3][0];      //ort
+
+    }elseif(array_search('WORK',$adr['param']['TYPE']) !== FALSE){
+      $entry['street']   = $adr['value'][2][0];
+      $entry['location'] = $adr['value'][3][0];
+      $entry['plz']      = $adr['value'][5][0];
+    }
+  }
+
+/*
+  print '<pre>';
+  print_r($entry);
+  print '</pre>';
+*/
+
+  return $entry;
+}
+
+
+?>
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..3f09573
--- /dev/null
+++ b/index.php
@@ -0,0 +1,102 @@
+<?
+  require_once('init.php');
+  ldap_login();
+
+  //prepare filter
+  $ldapfilter = _makeldapfilter();
+
+  //check public addressbook
+  $sr = ldap_list($LDAP_CON,$conf[publicbook],$ldapfilter);
+  $result1 = ldap_get_binentries($LDAP_CON, $sr);
+  //check users private addressbook
+  if(!empty($_SESSION[ldapab][binddn])){
+    $sr = @ldap_list($LDAP_CON,
+                    $conf[privatebook].','.$_SESSION[ldapab][binddn],
+                    $ldapfilter);
+    $result2 = ldap_get_binentries($LDAP_CON, $sr);
+  }
+  
+  $result = array_merge($result1,$result2);
+
+  $list = '';
+  if(count($result)==1 && $_REQUEST[search]){
+    //only one result on a search -> display page
+    header("Location: entry.php?dn=".$result[0][dn]);
+    exit;
+  }elseif(count($result)){
+    $keys = array_keys($result);
+    uksort($keys,"_namesort");
+    foreach($keys as $key){
+      tpl_entry($result[$key]);
+      $list .= $smarty->fetch('list_entry.tpl');
+    }
+  }
+
+  //save location in session
+  $_SESSION[ldapab][lastlocation]=$_SERVER["REQUEST_URI"];
+   
+  //prepare templates
+  tpl_std();
+  tpl_markers();
+  $smarty->assign('list',$list);
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('list_filter.tpl');
+  $smarty->display('list.tpl');
+  $smarty->display('footer.tpl');
+
+  //------- functions -----------//
+
+  /**
+   * callback function to sort entries by name
+   * uses global $result
+   */
+  function _namesort($a,$b){
+    global $result;
+    $x = $result[$a][sn][0].$result[$a][givenName][0];
+    $y = $result[$b][sn][0].$result[$b][givenName][0];
+    return(strcasecmp($x,$y));
+  }
+
+
+  /**
+   * Creates an LDAP filter from given request variables search or filter
+   */
+  function _makeldapfilter(){
+    //handle given filter
+
+    $filter = $_REQUEST['filter'];
+    $search = $_REQUEST['search'];
+    $org    = $_REQUEST['org'];
+    $marker = $_REQUEST['marker'];
+    $_SESSION[ldapab][filter] = $filter;
+    if(empty($filter)) $filter='a';
+
+    if(!empty($marker)){
+      $marker = utf8_encode($marker);
+      $ldapfilter = "(&(objectClass=contactPerson)(marker=$marker))";
+    }elseif(!empty($search)){
+      $search = trim($search);
+      $words=preg_split('/\s+/',$search);
+      $filter='';
+      foreach($words as $word){
+        $word = utf8_encode($word);
+        $filter .= "(|(|(sn=*$word*)(givenName=*$word*))(o=*$word*))";
+      }
+      $ldapfilter = "(&(objectClass=inetOrgPerson)$filter)";
+    }elseif(!empty($org)){
+      $org = utf8_encode($org);
+      $ldapfilter = "(&(objectClass=inetOrgPerson)(o=$org))";
+    }elseif($filter=='other'){
+      $other='';
+      for ($i=ord('a');$i<=ord('z');$i++){
+        $other .= '(!(sn='.chr($i).'*))';
+      }
+      $ldapfilter = "(&(objectClass=inetOrgPerson)$other)";
+    }else{
+      $filter = utf8_encode($filter);
+      $ldapfilter = "(&(objectClass=inetOrgPerson)(sn=$filter*))";
+    }
+    return $ldapfilter;
+  }
+?>
diff --git a/init.php b/init.php
new file mode 100644 (file)
index 0000000..14bd24f
--- /dev/null
+++ b/init.php
@@ -0,0 +1,39 @@
+<?
+  require_once('config.php');
+  require_once('lang/'.$conf[lang].'.php');
+  require_once('functions.php');
+  require_once('template.php');
+  require_once('smarty/Smarty.class.php');
+
+  //init session
+  session_name("ldapab");
+  session_start();
+  
+  //kill magic quotes
+  if (get_magic_quotes_gpc()) {
+    if (!empty($_GET))    remove_magic_quotes($_GET);
+    if (!empty($_POST))   remove_magic_quotes($_POST);
+    if (!empty($_COOKIE)) remove_magic_quotes($_COOKIE);
+    if (!empty($_REQUEST)) remove_magic_quotes($_REQUEST);
+    if (!empty($_SESSION)) remove_magic_quotes($_SESSION);
+    ini_set('magic_quotes_gpc', 0);
+  }
+  set_magic_quotes_runtime(0);
+
+  function remove_magic_quotes(&$array) {
+    foreach (array_keys($array) as $key) {
+      if (is_array($array[$key])) {
+        remove_magic_quotes($array[$key]);
+      }else {
+        $array[$key] = stripslashes($array[$key]);
+      }
+    } 
+  } 
+
+  //prepare SMARTY object
+  $smarty = new Smarty;
+  $smarty->compile_dir   = './cache';
+  $smarty->use_sub_dirs  = 0;
+  $smarty->template_dir  = './templates';
+  $smarty->force_compile = $conf[smartycompile];
+?>
diff --git a/lang/de.php b/lang/de.php
new file mode 100644 (file)
index 0000000..10bc0a5
--- /dev/null
@@ -0,0 +1,69 @@
+<?
+
+$lang[ldapab]         = 'Das LDAP Adressbuch';
+
+$lang[name]           = 'Name';
+$lang[givenname]      = 'Vorname';
+$lang[title]          = 'Titel';
+$lang[organization]   = 'Firma';
+$lang[office]         = 'Abteilung';
+$lang[street]         = 'Stra&szlig;e, Nr.';
+$lang[zip]            = 'Postleitzahl';
+$lang[location]       = 'Ort';
+$lang[phone]          = 'Telefon';
+$lang[fax]            = 'FAX';
+$lang[mobile]         = 'Handy';
+$lang[pager]          = 'Pieper';
+$lang[homephone]      = 'Telefon (priv.)';
+$lang[homestreet]     = 'Adresse (priv.)';
+$lang[photo]          = 'Foto';
+$lang[url]            = 'Webseite';
+$lang[note]           = 'Notizen';
+$lang[mail]           = 'E-Mail';
+$lang[manager]        = 'Key Account';
+
+$lang[anniversary]    = 'Geburtstag';
+$lang[marker]         = 'Kategorie';
+
+$lang[business]       = 'Geschäftlich';
+$lang[private]        = 'Privat';
+$lang[extended]       = 'Zus&auml;tzliche Infos';
+
+$lang[delphoto]       = 'Vorhandenes Foto l&ouml;schen';
+
+$lang[notloggedin]    = 'Nicht angemeldet';
+$lang[loggedinas]     = 'Angemeldet als';
+$lang[login]          = 'Anmelden';
+$lang[logout]         = 'Abmelden';
+$lang[edit]           = 'Bearbeiten';
+$lang[show]           = 'Anzeigen';
+$lang['new']          = 'Neu';
+$lang['delete']       = 'Löschen';
+$lang['copy']         = 'Kopieren';
+$lang[vcfexport]      = 'VCard';
+$lang[vcfimport]                       = 'Import';
+$lang[search]         = 'Suchen';
+$lang[submit]         = 'Absenden';
+$lang[help]           = 'Hilfe';
+$lang[select]         = 'Ausw&auml;hlen';
+$lang[orgs]           = 'Firmen';
+$lang[upload]         = 'Hochladen';
+
+$lang[publicbook]     = '&Ouml;ffentliches Adressbuch';
+$lang[privatebook]    = 'Privates Adressbuch';
+
+
+$lang[username]       = 'Benutzername';
+$lang[password]       = 'Passwort';
+
+$lang[msg_login]      = 'Bitte melden sie sich an!';
+$lang[msg_loginfail]  = 'Anmeldung fehlgeschlagen!';
+$lang[msg_reallydel]  = 'Eintrag wirklich l&ouml;schen?';
+$lang[msg_addto]      = 'Zu welchem Adressbuch soll der Eintrag hinzugef&uuml;gt werden?';
+$lang[msg_jpegonly]   = 'Nur JPEG';
+$lang[msg_dateformat] = 'JJJJ-MM-TT';
+$lang[msg_uploadvcf]  = 'Upload VCard File';
+
+$lang[err_noentries]  = 'Keine Eintr&auml;ge';
+$lang[err_ldap]       = 'Der LDAP Server lieferte folgende Fehlermeldungen';
+?>
diff --git a/lang/en.php b/lang/en.php
new file mode 100644 (file)
index 0000000..b8838ca
--- /dev/null
@@ -0,0 +1,68 @@
+<?
+
+$lang[ldapab]         = 'The LDAP address book';
+
+$lang[name]           = 'Name';
+$lang[givenname]      = 'First Name';
+$lang[title]          = 'Title';
+$lang[organization]   = 'Company';
+$lang[office]         = 'Office';
+$lang[street]         = 'Street No.';
+$lang[zip]            = 'Postal Code';
+$lang[location]       = 'City';
+$lang[phone]          = 'Phone';
+$lang[fax]            = 'FAX';
+$lang[mobile]         = 'Mobile';
+$lang[pager]          = 'Pager';
+$lang[homephone]      = 'Phone (priv.)';
+$lang[homestreet]     = 'Address (priv.)';
+$lang[photo]          = 'Picture';
+$lang[url]            = 'Website';
+$lang[note]           = 'Notes';
+$lang[mail]           = 'E-Mail';
+$lang[manager]        = 'Key Account';
+
+$lang[anniversary]    = 'Birthday';
+$lang[marker]         = 'Category';
+
+$lang[business]       = 'Business';
+$lang[private]        = 'Private';
+$lang[extended]       = 'Additional Information';
+
+$lang[delphoto]       = 'Delete existing photo';
+
+$lang[notloggedin]    = 'Not logged in';
+$lang[loggedinas]     = 'Logged in as';
+$lang[login]          = 'Log in';
+$lang[logout]         = 'Log out';
+$lang[edit]           = 'Edit';
+$lang[show]           = 'Show';
+$lang['new']          = 'New';
+$lang['delete']       = 'Delete';
+$lang['copy']         = 'Copy';
+$lang[vcfexport]      = 'VCard';
+$lang[vcfimport]                       = 'Import';
+$lang[search]         = 'Search';
+$lang[submit]         = 'Send';
+$lang[help]           = 'Help';
+$lang[select]         = 'Select';
+$lang[orgs]           = 'Companies';
+$lang[upload]         = 'Upload';
+
+$lang[publicbook]     = 'Public Addressbook';
+$lang[privatebook]    = 'Private Addressbook';
+
+$lang[username]       = 'Username';
+$lang[password]       = 'Password';
+
+$lang[msg_login]      = 'Please log in!';
+$lang[msg_loginfail]  = 'Log in failed!';
+$lang[msg_reallydel]  = 'Really delete entry?';
+$lang[msg_addto]      = 'Select address book to which the entry should be added:';
+$lang[msg_jpegonly]   = 'JPEG only';
+$lang[msg_dateformat] = 'YYYY-MM-DD';
+$lang[msg_uploadvcf]  = 'Upload VCard File';
+
+$lang[err_noentries]  = 'No entries';
+$lang[err_ldap]       = 'The LDAP server returned the following errors';
+?>
diff --git a/ldapab.schema b/ldapab.schema
new file mode 100644 (file)
index 0000000..19f2060
--- /dev/null
@@ -0,0 +1,51 @@
+# LDAP 2 Schema
+#
+# This is a schema extension for the LDAPab Addressbook by
+# CosmoCode GmbH www.cosmocode.de
+#
+# CosmoCode owns the following MIB Block:
+# iso.org.dod.internet.private.enterprise.16331.*
+#
+# CosmoCode currently uses the following OID hierachy to organize the IDs:
+#
+# defined in cosmo4ML.schema
+# 1.3.6.1.4.1.16331.1           formel CMS (cosmo4ML prefix)
+# 1.3.6.1.4.1.16331.1.1           SNMP (not used)
+# 1.3.6.1.4.1.16331.1.2           LDAP
+# 1.3.6.1.4.1.16331.1.2.1           AttributeTypes
+# 1.3.6.1.4.1.16331.1.2.2           ObjectClasses
+#
+# defined in ldapab.schema:
+# 1.3.6.1.4.1.16331.2          LDAPab
+# 1.3.6.1.4.1.16331.2.1                  SNMP (not used)
+# 1.3.6.1.4.1.16331.2.2                LDAP
+# 1.3.6.1.4.1.16331.2.2.1           AttributeTypes
+# 1.3.6.1.4.1.16331.2.2.2           ObjectClasses
+
+
+# OID macros for ease of use:
+objectIdentifier cosmocodeOID 1.3.6.1.4.1.16331
+objectIdentifier LDAPab       cosmocodeOID:2.2
+
+
+attributetype ( 1.3.6.1.4.1.16331.2.2.1.1 NAME 'anniversary'
+                DESC 'Holds Birthdays etc.'
+                EQUALITY caseIgnoreMatch
+                ORDERING caseIgnoreOrderingMatch
+                SUBSTR caseIgnoreSubstringsMatch
+                SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+                SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.16331.2.2.1.2 NAME 'marker'
+                DESC 'marking flag'
+                EQUALITY caseIgnoreMatch
+                ORDERING caseIgnoreOrderingMatch
+                SUBSTR caseIgnoreSubstringsMatch
+                SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256}
+               )
+
+objectclass ( 1.3.6.1.4.1.16331.2.2.2.1 NAME 'contactPerson'
+             DESC 'Contact - Addressbook entry'
+              AUXILIARY
+              MAY  ( anniversary $ marker )
+            )
diff --git a/login.php b/login.php
new file mode 100644 (file)
index 0000000..f7ad08e
--- /dev/null
+++ b/login.php
@@ -0,0 +1,26 @@
+<?
+  require_once('init.php');
+
+  $msg = $lang[msg_login];
+  if(isset($_REQUEST[username])){
+    if (do_ldap_bind($_REQUEST[username],$_REQUEST[password])){
+      //forward to next page
+      if(!empty($_SESSION[ldapab][lastlocation])){
+        header('Location: '.$_SESSION[ldapab][lastlocation]);
+      }else{
+        header('Location: index.php');
+      }
+      exit;
+    }else{
+      $msg = $lang[msg_loginfail];;
+    }
+  }
+
+  //prepare templates
+  tpl_std();
+  $smarty->assign('msg',$msg);
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('login.tpl');
+  $smarty->display('footer.tpl');
+?>
diff --git a/orgs.php b/orgs.php
new file mode 100644 (file)
index 0000000..272c972
--- /dev/null
+++ b/orgs.php
@@ -0,0 +1,14 @@
+<?
+  require_once('init.php');
+  ldap_login();
+
+  //prepare templates
+  tpl_std();
+  tpl_orgs();
+  //display templates
+  $smarty->display('header.tpl');
+  $smarty->display('orgs.tpl');
+  $smarty->display('footer.tpl');
+
+
+?>
diff --git a/pix/copy.png b/pix/copy.png
new file mode 100644 (file)
index 0000000..eac4d8c
Binary files /dev/null and b/pix/copy.png differ
diff --git a/pix/delete.png b/pix/delete.png
new file mode 100644 (file)
index 0000000..6fb193f
Binary files /dev/null and b/pix/delete.png differ
diff --git a/pix/edit.png b/pix/edit.png
new file mode 100644 (file)
index 0000000..ce8b226
Binary files /dev/null and b/pix/edit.png differ
diff --git a/pix/help.png b/pix/help.png
new file mode 100644 (file)
index 0000000..3626ba7
Binary files /dev/null and b/pix/help.png differ
diff --git a/pix/image.png b/pix/image.png
new file mode 100644 (file)
index 0000000..fdf5370
Binary files /dev/null and b/pix/image.png differ
diff --git a/pix/import.png b/pix/import.png
new file mode 100644 (file)
index 0000000..df0f16c
Binary files /dev/null and b/pix/import.png differ
diff --git a/pix/login.png b/pix/login.png
new file mode 100644 (file)
index 0000000..b490469
Binary files /dev/null and b/pix/login.png differ
diff --git a/pix/logout.png b/pix/logout.png
new file mode 100644 (file)
index 0000000..119e5ef
Binary files /dev/null and b/pix/logout.png differ
diff --git a/pix/new.png b/pix/new.png
new file mode 100644 (file)
index 0000000..7be37e8
Binary files /dev/null and b/pix/new.png differ
diff --git a/pix/org.png b/pix/org.png
new file mode 100644 (file)
index 0000000..b5c8949
Binary files /dev/null and b/pix/org.png differ
diff --git a/pix/orgs.png b/pix/orgs.png
new file mode 100644 (file)
index 0000000..7596e67
Binary files /dev/null and b/pix/orgs.png differ
diff --git a/pix/private.png b/pix/private.png
new file mode 100644 (file)
index 0000000..55fb6dd
Binary files /dev/null and b/pix/private.png differ
diff --git a/pix/public.png b/pix/public.png
new file mode 100644 (file)
index 0000000..212c6cc
Binary files /dev/null and b/pix/public.png differ
diff --git a/pix/search.png b/pix/search.png
new file mode 100644 (file)
index 0000000..4f880ab
Binary files /dev/null and b/pix/search.png differ
diff --git a/pix/show.png b/pix/show.png
new file mode 100644 (file)
index 0000000..40762a0
Binary files /dev/null and b/pix/show.png differ
diff --git a/pix/vcard.png b/pix/vcard.png
new file mode 100644 (file)
index 0000000..a8b9429
Binary files /dev/null and b/pix/vcard.png differ
diff --git a/slapd.example b/slapd.example
new file mode 100644 (file)
index 0000000..cd7643a
--- /dev/null
@@ -0,0 +1,111 @@
+# This is the main ldapd configuration file. See slapd.conf(5) for more
+# info on the configuration options.
+
+# Schema and objectClass definitions
+include         /etc/ldap/schema/core.schema
+include         /etc/ldap/schema/cosine.schema
+include         /etc/ldap/schema/nis.schema
+include         /etc/ldap/schema/inetorgperson.schema
+include                /etc/ldap/schema/samba.schema
+include                /etc/ldap/schema/courierimap.schema
+include                /etc/ldap/schema/phpgwaccount.schema
+include                /etc/ldap/schema/phpgwcontact.schema
+include                /etc/ldap/schema/ldapab.schema
+
+
+# Schema check allows for forcing entries to
+# match schemas for their objectClasses's
+schemacheck     on
+
+# Where the pid file is put. The init.d script
+# will not stop the server if you change this.
+pidfile         /var/run/slapd.pid
+
+# List of arguments that were passed to the server
+argsfile        /var/run/slapd.args
+
+# Where to store the replica logs
+replogfile     /var/lib/ldap/replog
+
+# Read slapd.conf(5) for possible values
+#loglevel        256
+loglevel       0
+
+#######################################################################
+# ldbm database definitions
+#######################################################################
+
+# The backend type, ldbm, is the default standard
+database        ldbm
+
+# The base of your directory
+suffix          "o=cosmocode,c=de"
+
+# Where the database file are physically stored
+directory       "/var/lib/ldap"
+
+# Indexing options
+index objectClass eq
+
+# Folgende Indizies sind im samba-LDAP-HOWTO empfohlen;
+
+## support pbb_getsampwnam()
+index uid           pres,eq
+## support pdb_getsampwrid()
+index rid           eq
+
+## uncomment these if you are storing posixAccount and
+## posixGroup entries in the directory as well
+index uidNumber     eq
+index gidNumber     eq
+index cn            eq
+index memberUid     eq
+
+# Save the time that the entry gets modified
+lastmod on
+
+# The userPassword by default can be changed
+# by the entry owning it if they are authenticated.
+# Others should not be able to see it, except the
+# admin entry below
+access to attribute=userPassword
+        by dn="cn=admin,o=cosmocode,c=de" write
+        by anonymous auth
+        by self write
+        by * none
+
+access to attribute=lmPassword
+        by dn="cn=admin,o=cosmocode,c=de" write
+        by anonymous auth
+        by self write
+        by * none
+
+access to attribute=ntPassword
+        by dn="cn=admin,o=cosmocode,c=de" write
+        by anonymous auth
+        by self write
+        by * none
+
+# private LDAP Addressbook is readable and writable for the owner only
+access to dn="(.*,)?ou=contacts,cn=([^,]+),ou=people,(.*)$"
+        by dn="cn=$2,ou=people,$3" write
+        by * none
+
+# global LDAP Addressbook is writable for all authenticated users
+# This entry has to be _before_ any other entry that matches the contact
+# tree eg. the * entry
+access to dn.subtree="ou=contacts,o=cosmocode,c=de"
+       by users write
+       by * read
+
+# The admin dn has full write access
+access to *
+        by dn="cn=admin,o=cosmocode,c=de" write
+        by * read
+
+# For Netscape Roaming support, each user gets a roaming
+# profile for which they have write access to
+#access to dn=".*,ou=Roaming,o=morsnet"
+#        by dn="cn=admin,o=cosmocode,c=de" write
+#        by dnattr=owner write
+
diff --git a/smarty/Config_File.class.php b/smarty/Config_File.class.php
new file mode 100644 (file)
index 0000000..f0297cd
--- /dev/null
@@ -0,0 +1,365 @@
+<?php
+
+/**
+ * Config_File class.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * You may contact the author of Config_File by e-mail at:
+ * {@link andrei@php.net}
+ *
+ * The latest version of Config_File can be obtained from:
+ * http://smarty.php.net/
+ *
+ * @link http://smarty.php.net/
+ * @version 2.6.0
+ * @copyright Copyright: 2001-2003 ispi of Lincoln, Inc.
+ * @author Andrei Zmievski <andrei@php.net>
+ * @access public
+ * @package Smarty
+ */
+
+/* $Id: Config_File.class.php,v 1.1.1.1 2004/01/13 16:02:46 gohr Exp $ */
+/**
+ * Config file reading class
+ * @package Smarty
+ */
+class Config_File {
+    /**#@+
+     * Options
+     * @var boolean
+     */
+    /**
+     * Controls whether variables with the same name overwrite each other.
+     */
+    var $overwrite        =    true;
+
+    /**
+     * Controls whether config values of on/true/yes and off/false/no get
+     * converted to boolean values automatically.
+     */
+    var $booleanize        =    true;
+
+    /**
+     * Controls whether hidden config sections/vars are read from the file.
+     */
+    var $read_hidden     =    true;
+
+    /**
+     * Controls whether or not to fix mac or dos formatted newlines.
+     * If set to true, \r or \r\n will be changed to \n.
+     */
+    var $fix_newlines =    true;
+    /**#@-*/
+
+    /** @access private */
+    var $_config_path    = "";
+    var $_config_data    = array();
+    /**#@-*/
+
+    /**
+     * Constructs a new config file class.
+     *
+     * @param string $config_path (optional) path to the config files
+     */
+    function Config_File($config_path = NULL)
+    {
+        if (isset($config_path))
+            $this->set_path($config_path);
+    }
+
+
+    /**
+     * Set the path where configuration files can be found.
+     *
+     * @param string $config_path path to the config files
+     */
+    function set_path($config_path)
+    {
+        if (!empty($config_path)) {
+            if (!is_string($config_path) || !file_exists($config_path) || !is_dir($config_path)) {
+                $this->_trigger_error_msg("Bad config file path '$config_path'");
+                return;
+            }
+            if(substr($config_path, -1) != DIRECTORY_SEPARATOR) {
+                $config_path .= DIRECTORY_SEPARATOR;
+            }
+
+            $this->_config_path = $config_path;
+        }
+    }
+
+
+    /**
+     * Retrieves config info based on the file, section, and variable name.
+     *
+     * @param string $file_name config file to get info for
+     * @param string $section_name (optional) section to get info for
+     * @param string $var_name (optional) variable to get info for
+     * @return string|array a value or array of values
+     */
+    function &get($file_name, $section_name = NULL, $var_name = NULL)
+    {
+        if (empty($file_name)) {
+            $this->_trigger_error_msg('Empty config file name');
+            return;
+        } else {
+            $file_name = $this->_config_path . $file_name;
+            if (!isset($this->_config_data[$file_name]))
+                $this->load_file($file_name, false);
+        }
+
+        if (!empty($var_name)) {
+            if (empty($section_name)) {
+                return $this->_config_data[$file_name]["vars"][$var_name];
+            } else {
+                if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name]))
+                    return $this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name];
+                else
+                    return array();
+            }
+        } else {
+            if (empty($section_name)) {
+                return (array)$this->_config_data[$file_name]["vars"];
+            } else {
+                if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"]))
+                    return (array)$this->_config_data[$file_name]["sections"][$section_name]["vars"];
+                else
+                    return array();
+            }
+        }
+    }
+
+
+    /**
+     * Retrieves config info based on the key.
+     *
+     * @param $file_name string config key (filename/section/var)
+     * @return string|array same as get()
+     * @uses get() retrieves information from config file and returns it
+     */
+    function &get_key($config_key)
+    {
+        list($file_name, $section_name, $var_name) = explode('/', $config_key, 3);
+        $result = &$this->get($file_name, $section_name, $var_name);
+        return $result;
+    }
+
+    /**
+     * Get all loaded config file names.
+     *
+     * @return array an array of loaded config file names
+     */
+    function get_file_names()
+    {
+        return array_keys($this->_config_data);
+    }
+
+
+    /**
+     * Get all section names from a loaded file.
+     *
+     * @param string $file_name config file to get section names from
+     * @return array an array of section names from the specified file
+     */
+    function get_section_names($file_name)
+    {
+        $file_name = $this->_config_path . $file_name;
+        if (!isset($this->_config_data[$file_name])) {
+            $this->_trigger_error_msg("Unknown config file '$file_name'");
+            return;
+        }
+
+        return array_keys($this->_config_data[$file_name]["sections"]);
+    }
+
+
+    /**
+     * Get all global or section variable names.
+     *
+     * @param string $file_name config file to get info for
+     * @param string $section_name (optional) section to get info for
+     * @return array an array of variables names from the specified file/section
+     */
+    function get_var_names($file_name, $section = NULL)
+    {
+        if (empty($file_name)) {
+            $this->_trigger_error_msg('Empty config file name');
+            return;
+        } else if (!isset($this->_config_data[$file_name])) {
+            $this->_trigger_error_msg("Unknown config file '$file_name'");
+            return;
+        }
+
+        if (empty($section))
+            return array_keys($this->_config_data[$file_name]["vars"]);
+        else
+            return array_keys($this->_config_data[$file_name]["sections"][$section]["vars"]);
+    }
+
+
+    /**
+     * Clear loaded config data for a certain file or all files.
+     *
+     * @param string $file_name file to clear config data for
+     */
+    function clear($file_name = NULL)
+    {
+        if ($file_name === NULL)
+            $this->_config_data = array();
+        else if (isset($this->_config_data[$file_name]))
+            $this->_config_data[$file_name] = array();
+    }
+
+
+    /**
+     * Load a configuration file manually.
+     *
+     * @param string $file_name file name to load
+     * @param boolean $prepend_path whether current config path should be
+     *                              prepended to the filename
+     */
+    function load_file($file_name, $prepend_path = true)
+    {
+        if ($prepend_path && $this->_config_path != "")
+            $config_file = $this->_config_path . $file_name;
+        else
+            $config_file = $file_name;
+
+        ini_set('track_errors', true);
+        $fp = @fopen($config_file, "r");
+        if (!is_resource($fp)) {
+            $this->_trigger_error_msg("Could not open config file '$config_file'");
+            return false;
+        }
+
+        $contents = fread($fp, filesize($config_file));
+        fclose($fp);
+
+        if($this->fix_newlines) {
+            // fix mac/dos formatted newlines
+            $contents = preg_replace('!\r\n?!',"\n",$contents);
+        }
+
+        $config_data = array();
+
+        /* replace all multi-line values by placeholders */
+        if (preg_match_all('/"""(.*)"""/Us', $contents, $match)) {
+            $_triple_quotes = $match[1];
+            $_i = 0;
+            $contents = preg_replace('/""".*"""/Use', '"\x1b\x1b\x1b".$_i++."\x1b\x1b\x1b"', $contents);
+        } else {
+            $_triple_quotes = null;
+        }
+
+        /* Get global variables first. */
+        if ($contents{0} != '[' && preg_match("/^(.*?)(\n\[|\Z)/s", $contents, $match))
+            $config_data["vars"] = $this->_parse_config_block($match[1], $_triple_quotes);
+
+        /* Get section variables. */
+        $config_data["sections"] = array();
+        preg_match_all("/^\[(.*?)\]/m", $contents, $match);
+        foreach ($match[1] as $section) {
+            if ($section{0} == '.' && !$this->read_hidden)
+                continue;
+            if (preg_match("/\[".preg_quote($section, '/')."\](.*?)(\n\[|\Z)/s", $contents, $match))
+                if ($section{0} == '.')
+                    $section = substr($section, 1);
+                $config_data["sections"][$section]["vars"] = $this->_parse_config_block($match[1], $_triple_quotes);
+        }
+
+        $this->_config_data[$config_file] = $config_data;
+
+        return true;
+    }
+
+    /**#@+ @access private */
+    /**
+     * @var string $config_block
+     */
+    function _parse_config_block($config_block, $triple_quotes)
+    {
+        $vars = array();
+
+        /* First we grab the multi-line values. */
+        if (preg_match_all("/^([^=\n]+)=\s*\x1b\x1b\x1b(\d+)\x1b\x1b\x1b\s*$/ms", $config_block, $match, PREG_SET_ORDER)) {
+            for ($i = 0; $i < count($match); $i++) {
+                $this->_set_config_var($vars, trim($match[$i][1]), $triple_quotes[$match[$i][2]], false);
+            }
+            $config_block = preg_replace("/^[^=\n]+=\s*\x1b\x1b\x1b\d+\x1b\x1b\x1b\s*$/ms", "", $config_block);
+        }
+
+
+        $config_lines = preg_split("/\n+/", $config_block);
+
+        foreach ($config_lines as $line) {
+            if (preg_match("/^\s*(\.?\w+)\s*=(.*)/", $line, $match)) {
+                $var_value = preg_replace('/^([\'"])(.*)\1$/', '\2', trim($match[2]));
+                $this->_set_config_var($vars, trim($match[1]), $var_value, $this->booleanize);
+            }
+        }
+
+        return $vars;
+    }
+
+    /**
+     * @param array &$container
+     * @param string $var_name
+     * @param mixed $var_value
+     * @param boolean $booleanize determines whether $var_value is converted to
+     *                            to true/false
+     */
+    function _set_config_var(&$container, $var_name, $var_value, $booleanize)
+    {
+        if ($var_name{0} == '.') {
+            if (!$this->read_hidden)
+                return;
+            else
+                $var_name = substr($var_name, 1);
+        }
+
+        if (!preg_match("/^[a-zA-Z_]\w*$/", $var_name)) {
+            $this->_trigger_error_msg("Bad variable name '$var_name'");
+            return;
+        }
+
+        if ($booleanize) {
+            if (preg_match("/^(on|true|yes)$/i", $var_value))
+                $var_value = true;
+            else if (preg_match("/^(off|false|no)$/i", $var_value))
+                $var_value = false;
+        }
+
+        if (!isset($container[$var_name]) || $this->overwrite)
+            $container[$var_name] = $var_value;
+        else {
+            settype($container[$var_name], 'array');
+            $container[$var_name][] = $var_value;
+        }
+    }
+
+    /**
+     * @uses trigger_error() creates a PHP warning/error
+     * @param string $error_msg
+     * @param integer $error_type one of
+     */
+    function _trigger_error_msg($error_msg, $error_type = E_USER_WARNING)
+    {
+        trigger_error("Config_File error: $error_msg", $error_type);
+    }
+    /**#@-*/
+}
+
+?>
diff --git a/smarty/Smarty.class.php b/smarty/Smarty.class.php
new file mode 100644 (file)
index 0000000..7388dc3
--- /dev/null
@@ -0,0 +1,2010 @@
+<?php
+
+/**
+ * Project:     Smarty: the PHP compiling template engine
+ * File:        Smarty.class.php
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For questions, help, comments, discussion, etc., please join the
+ * Smarty mailing list. Send a blank e-mail to
+ * smarty-general-subscribe@lists.php.net
+ *
+ * You may contact the authors of Smarty by e-mail at:
+ * monte@ispi.net
+ * andrei@php.net
+ *
+ * Or, write to:
+ * Monte Ohrt
+ * Director of Technology, ispi
+ * 237 S. 70th suite 220
+ * Lincoln, NE 68510
+ *
+ * The latest version of Smarty can be obtained from:
+ * http://smarty.php.net/
+ *
+ * @link http://smarty.php.net/
+ * @copyright 2001-2003 ispi of Lincoln, Inc.
+ * @author Monte Ohrt <monte@ispi.net>
+ * @author Andrei Zmievski <andrei@php.net>
+ * @package Smarty
+ * @version 2.6.0
+ */
+
+/* $Id: Smarty.class.php,v 1.1.1.1 2004/01/13 16:02:46 gohr Exp $ */
+
+/**
+ * DIR_SEP isn't used anymore, but third party apps might
+ */
+if(!defined('DIR_SEP')) {
+    define('DIR_SEP', DIRECTORY_SEPARATOR);
+}
+
+/**
+ * set SMARTY_DIR to absolute path to Smarty library files.
+ * if not defined, include_path will be used. Sets SMARTY_DIR only if user
+ * application has not already defined it.
+ */
+
+if (!defined('SMARTY_DIR')) {
+    define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);
+}
+
+define('SMARTY_PHP_PASSTHRU',   0);
+define('SMARTY_PHP_QUOTE',      1);
+define('SMARTY_PHP_REMOVE',     2);
+define('SMARTY_PHP_ALLOW',      3);
+
+/**
+ * @package Smarty
+ */
+class Smarty
+{
+    /**#@+
+     * Smarty Configuration Section
+     */
+
+    /**
+     * The name of the directory where templates are located.
+     *
+     * @var string
+     */
+    var $template_dir    =  'templates';
+
+    /**
+     * The directory where compiled templates are located.
+     *
+     * @var string
+     */
+    var $compile_dir     =  'templates_c';
+
+    /**
+     * The directory where config files are located.
+     *
+     * @var string
+     */
+    var $config_dir      =  'configs';
+
+    /**
+     * An array of directories searched for plugins.
+     *
+     * @var array
+     */
+    var $plugins_dir     =  array('plugins');
+
+    /**
+     * If debugging is enabled, a debug console window will display
+     * when the page loads (make sure your browser allows unrequested
+     * popup windows)
+     *
+     * @var boolean
+     */
+    var $debugging       =  false;
+
+    /**
+     * This is the path to the debug console template. If not set,
+     * the default one will be used.
+     *
+     * @var string
+     */
+    var $debug_tpl       =  '';
+
+    /**
+     * This determines if debugging is enable-able from the browser.
+     * <ul>
+     *  <li>NONE => no debugging control allowed</li>
+     *  <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li>
+     * </ul>
+     * @link http://www.foo.dom/index.php?SMARTY_DEBUG
+     * @var string
+     */
+    var $debugging_ctrl  =  'NONE';
+
+    /**
+     * This tells Smarty whether to check for recompiling or not. Recompiling
+     * does not need to happen unless a template or config file is changed.
+     * Typically you enable this during development, and disable for
+     * production.
+     *
+     * @var boolean
+     */
+    var $compile_check   =  true;
+
+    /**
+     * This forces templates to compile every time. Useful for development
+     * or debugging.
+     *
+     * @var boolean
+     */
+    var $force_compile   =  false;
+
+    /**
+     * This enables template caching.
+     * <ul>
+     *  <li>0 = no caching</li>
+     *  <li>1 = use class cache_lifetime value</li>
+     *  <li>2 = use cache_lifetime in cache file</li>
+     * </ul>
+     * @var integer
+     */
+    var $caching         =  0;
+
+    /**
+     * The name of the directory for cache files.
+     *
+     * @var string
+     */
+    var $cache_dir       =  'cache';
+
+    /**
+     * This is the number of seconds cached content will persist.
+     * <ul>
+     *  <li>0 = always regenerate cache</li>
+     *  <li>-1 = never expires</li>
+     * </ul>
+     *
+     * @var integer
+     */
+    var $cache_lifetime  =  3600;
+
+    /**
+     * Only used when $caching is enabled. If true, then If-Modified-Since headers
+     * are respected with cached content, and appropriate HTTP headers are sent.
+     * This way repeated hits to a cached page do not send the entire page to the
+     * client every time.
+     *
+     * @var boolean
+     */
+    var $cache_modified_check = false;
+
+    /**
+     * This determines how Smarty handles "<?php ... ?>" tags in templates.
+     * possible values:
+     * <ul>
+     *  <li>SMARTY_PHP_PASSTHRU -> print tags as plain text</li>
+     *  <li>SMARTY_PHP_QUOTE    -> escape tags as entities</li>
+     *  <li>SMARTY_PHP_REMOVE   -> remove php tags</li>
+     *  <li>SMARTY_PHP_ALLOW    -> execute php tags</li>
+     * </ul>
+     *
+     * @var integer
+     */
+    var $php_handling    =  SMARTY_PHP_PASSTHRU;
+
+    /**
+     * This enables template security. When enabled, many things are restricted
+     * in the templates that normally would go unchecked. This is useful when
+     * untrusted parties are editing templates and you want a reasonable level
+     * of security. (no direct execution of PHP in templates for example)
+     *
+     * @var boolean
+     */
+    var $security       =   false;
+
+    /**
+     * This is the list of template directories that are considered secure. This
+     * is used only if {@link $security} is enabled. One directory per array
+     * element.  {@link $template_dir} is in this list implicitly.
+     *
+     * @var array
+     */
+    var $secure_dir     =   array();
+
+    /**
+     * These are the security settings for Smarty. They are used only when
+     * {@link $security} is enabled.
+     *
+     * @var array
+     */
+    var $security_settings  = array(
+                                    'PHP_HANDLING'    => false,
+                                    'IF_FUNCS'        => array('array', 'list',
+                                                               'isset', 'empty',
+                                                               'count', 'sizeof',
+                                                               'in_array', 'is_array',
+                                                               'true','false'),
+                                    'INCLUDE_ANY'     => false,
+                                    'PHP_TAGS'        => false,
+                                    'MODIFIER_FUNCS'  => array('count'),
+                                    'ALLOW_CONSTANTS' => false
+                                   );
+
+    /**
+     * This is an array of directories where trusted php scripts reside.
+     * {@link $security} is disabled during their inclusion/execution.
+     *
+     * @var array
+     */
+    var $trusted_dir        = array();
+
+    /**
+     * The left delimiter used for the template tags.
+     *
+     * @var string
+     */
+    var $left_delimiter  =  '{';
+
+    /**
+     * The right delimiter used for the template tags.
+     *
+     * @var string
+     */
+    var $right_delimiter =  '}';
+
+    /**
+     * The order in which request variables are registered, similar to
+     * variables_order in php.ini E = Environment, G = GET, P = POST,
+     * C = Cookies, S = Server
+     *
+     * @var string
+     */
+    var $request_vars_order    = "EGPCS";
+
+    /**
+     * Indicates wether $HTTP_*_VARS[] (request_use_auto_globals=false)
+     * are uses as request-vars or $_*[]-vars. note: if
+     * request_use_auto_globals is true, then $request_vars_order has
+     * no effect, but the php-ini-value "gpc_order"
+     *
+     * @var boolean
+     */
+    var $request_use_auto_globals      = false;
+
+    /**
+     * Set this if you want different sets of compiled files for the same
+     * templates. This is useful for things like different languages.
+     * Instead of creating separate sets of templates per language, you
+     * set different compile_ids like 'en' and 'de'.
+     *
+     * @var string
+     */
+    var $compile_id            = null;
+
+    /**
+     * This tells Smarty whether or not to use sub dirs in the cache/ and
+     * templates_c/ directories. sub directories better organized, but
+     * may not work well with PHP safe mode enabled.
+     *
+     * @var boolean
+     *
+     */
+    var $use_sub_dirs          = true;
+
+    /**
+     * This is a list of the modifiers to apply to all template variables.
+     * Put each modifier in a separate array element in the order you want
+     * them applied. example: <code>array('escape:"htmlall"');</code>
+     *
+     * @var array
+     */
+    var $default_modifiers        = array();
+
+    /**
+     * This is the resource type to be used when not specified
+     * at the beginning of the resource path. examples:
+     * $smarty->display('file:index.tpl');
+     * $smarty->display('db:index.tpl');
+     * $smarty->display('index.tpl'); // will use default resource type
+     * {include file="file:index.tpl"}
+     * {include file="db:index.tpl"}
+     * {include file="index.tpl"} {* will use default resource type *}
+     *
+     * @var array
+     */
+    var $default_resource_type    = 'file';
+
+    /**
+     * The function used for cache file handling. If not set, built-in caching is used.
+     *
+     * @var null|string function name
+     */
+    var $cache_handler_func   = null;
+
+    /**
+     * These are the variables from the globals array that are
+     * assigned to all templates automatically. This isn't really
+     * necessary any more, you can use the $smarty var to access them
+     * directly.
+     *
+     * @var array
+     */
+    var $global_assign   =  array('HTTP_SERVER_VARS' => array('SCRIPT_NAME'));
+
+    /**
+     * The value of "undefined". Leave it alone :-)
+     *
+     * @var null
+     */
+    var $undefined       =  null;
+
+    /**
+     * This indicates which filters are automatically loaded into Smarty.
+     *
+     * @var array array of filter names
+     */
+    var $autoload_filters = array();
+
+    /**#@+
+     * @var boolean
+     */
+    /**
+     * This tells if config file vars of the same name overwrite each other or not.
+     * if disabled, same name variables are accumulated in an array.
+     */
+    var $config_overwrite = true;
+
+    /**
+     * This tells whether or not to automatically booleanize config file variables.
+     * If enabled, then the strings "on", "true", and "yes" are treated as boolean
+     * true, and "off", "false" and "no" are treated as boolean false.
+     */
+    var $config_booleanize = true;
+
+    /**
+     * This tells whether hidden sections [.foobar] are readable from the
+     * tempalates or not. Normally you would never allow this since that is
+     * the point behind hidden sections: the application can access them, but
+     * the templates cannot.
+     */
+    var $config_read_hidden = false;
+
+    /**
+     * This tells whether or not automatically fix newlines in config files.
+     * It basically converts \r (mac) or \r\n (dos) to \n
+     */
+    var $config_fix_newlines = true;
+    /**#@-*/
+
+    /**
+     * If a template cannot be found, this PHP function will be executed.
+     * Useful for creating templates on-the-fly or other special action.
+     *
+     * @var string function name
+     */
+    var $default_template_handler_func = '';
+
+    /**
+     * The file that contains the compiler class. This can a full
+     * pathname, or relative to the php_include path.
+     *
+     * @var string
+     */
+    var $compiler_file        =    'Smarty_Compiler.class.php';
+
+    /**
+     * The class used for compiling templates.
+     *
+     * @var string
+     */
+    var $compiler_class        =   'Smarty_Compiler';
+
+    /**
+     * The class used to load config vars.
+     *
+     * @var string
+     */
+    var $config_class          =   'Config_File';
+
+/**#@+
+ * END Smarty Configuration Section
+ * There should be no need to touch anything below this line.
+ * @access private
+ */
+    /**
+     * error messages. true/false
+     *
+     * @var boolean
+     */
+    var $_error_msg            = false;
+
+    /**
+     * where assigned template vars are kept
+     *
+     * @var array
+     */
+    var $_tpl_vars             = array();
+
+    /**
+     * stores run-time $smarty.* vars
+     *
+     * @var null|array
+     */
+    var $_smarty_vars          = null;
+
+    /**
+     * keeps track of sections
+     *
+     * @var array
+     */
+    var $_sections             = array();
+
+    /**
+     * keeps track of foreach blocks
+     *
+     * @var array
+     */
+    var $_foreach              = array();
+
+    /**
+     * keeps track of tag hierarchy
+     *
+     * @var array
+     */
+    var $_tag_stack            = array();
+
+    /**
+     * configuration object
+     *
+     * @var Config_file
+     */
+    var $_conf_obj             = null;
+
+    /**
+     * loaded configuration settings
+     *
+     * @var array
+     */
+    var $_config               = array(array('vars'  => array(), 'files' => array()));
+
+    /**
+     * md5 checksum of the string 'Smarty'
+     *
+     * @var string
+     */
+    var $_smarty_md5           = 'f8d698aea36fcbead2b9d5359ffca76f';
+
+    /**
+     * Smarty version number
+     *
+     * @var string
+     */
+    var $_version              = '2.6.0';
+
+    /**
+     * current template inclusion depth
+     *
+     * @var integer
+     */
+    var $_inclusion_depth      = 0;
+
+    /**
+     * for different compiled templates
+     *
+     * @var string
+     */
+    var $_compile_id           = null;
+
+    /**
+     * text in URL to enable debug mode
+     *
+     * @var string
+     */
+    var $_smarty_debug_id      = 'SMARTY_DEBUG';
+
+    /**
+     * debugging information for debug console
+     *
+     * @var array
+     */
+    var $_smarty_debug_info    = array();
+
+    /**
+     * info that makes up a cache file
+     *
+     * @var array
+     */
+    var $_cache_info           = array();
+
+    /**
+     * default file permissions
+     *
+     * @var integer
+     */
+    var $_file_perms           = 0644;
+
+    /**
+     * default dir permissions
+     *
+     * @var integer
+     */
+    var $_dir_perms               = 0771;
+
+    /**
+     * registered objects
+     *
+     * @var array
+     */
+    var $_reg_objects           = array();
+
+    /**
+     * table keeping track of plugins
+     *
+     * @var array
+     */
+    var $_plugins              = array(
+                                       'modifier'      => array(),
+                                       'function'      => array(),
+                                       'block'         => array(),
+                                       'compiler'      => array(),
+                                       'prefilter'     => array(),
+                                       'postfilter'    => array(),
+                                       'outputfilter'  => array(),
+                                       'resource'      => array(),
+                                       'insert'        => array());
+
+
+    /**
+     * cache serials
+     *
+     * @var array
+     */
+    var $_cache_serials = array();
+
+    /**
+     * name of optional cache include file
+     *
+     * @var string
+     */
+    var $_cache_include = null;
+
+    /**
+     * indicate if the current code is used in a compiled
+     * include
+     *
+     * @var string
+     */
+    var $_cache_including = false;
+
+    /**#@-*/
+    /**
+     * The class constructor.
+     *
+     * @uses $global_assign uses {@link assign()} to assign each corresponding
+     *                      value from $GLOBALS to the template vars
+     */
+    function Smarty()
+    {
+        foreach ($this->global_assign as $key => $var_name) {
+            if (is_array($var_name)) {
+                foreach ($var_name as $var) {
+                    if (isset($GLOBALS[$key][$var])) {
+                        $this->assign($var, $GLOBALS[$key][$var]);
+                    } else {
+                        $this->assign($var, $this->undefined);
+                    }
+                }
+            } else {
+                if (isset($GLOBALS[$var_name])) {
+                    $this->assign($var_name, $GLOBALS[$var_name]);
+                } else {
+                    $this->assign($var_name, $this->undefined);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * assigns values to template variables
+     *
+     * @param array|string $tpl_var the template variable name(s)
+     * @param mixed $value the value to assign
+     */
+    function assign($tpl_var, $value = null)
+    {
+        if (is_array($tpl_var)){
+            foreach ($tpl_var as $key => $val) {
+                if ($key != '') {
+                    $this->_tpl_vars[$key] = $val;
+                }
+            }
+        } else {
+            if ($tpl_var != '')
+                $this->_tpl_vars[$tpl_var] = $value;
+        }
+    }
+
+    /**
+     * assigns values to template variables by reference
+     *
+     * @param string $tpl_var the template variable name
+     * @param mixed $value the referenced value to assign
+     */
+    function assign_by_ref($tpl_var, &$value)
+    {
+        if ($tpl_var != '')
+            $this->_tpl_vars[$tpl_var] = &$value;
+    }
+
+    /**
+     * appends values to template variables
+     *
+     * @param array|string $tpl_var the template variable name(s)
+     * @param mixed $value the value to append
+     */
+    function append($tpl_var, $value=null, $merge=false)
+    {
+        if (is_array($tpl_var)) {
+            // $tpl_var is an array, ignore $value
+            foreach ($tpl_var as $_key => $_val) {
+                if ($_key != '') {
+                    if(!@is_array($this->_tpl_vars[$_key])) {
+                        settype($this->_tpl_vars[$_key],'array');
+                    }
+                    if($merge && is_array($_val)) {
+                        foreach($_val as $_mkey => $_mval) {
+                            $this->_tpl_vars[$_key][$_mkey] = $_mval;
+                        }
+                    } else {
+                        $this->_tpl_vars[$_key][] = $_val;
+                    }
+                }
+            }
+        } else {
+            if ($tpl_var != '' && isset($value)) {
+                if(!@is_array($this->_tpl_vars[$tpl_var])) {
+                    settype($this->_tpl_vars[$tpl_var],'array');
+                }
+                if($merge && is_array($value)) {
+                    foreach($value as $_mkey => $_mval) {
+                        $this->_tpl_vars[$tpl_var][$_mkey] = $_mval;
+                    }
+                } else {
+                    $this->_tpl_vars[$tpl_var][] = $value;
+                }
+            }
+        }
+    }
+
+    /**
+     * appends values to template variables by reference
+     *
+     * @param string $tpl_var the template variable name
+     * @param mixed $value the referenced value to append
+     */
+    function append_by_ref($tpl_var, &$value, $merge=false)
+    {
+        if ($tpl_var != '' && isset($value)) {
+            if(!@is_array($this->_tpl_vars[$tpl_var])) {
+             settype($this->_tpl_vars[$tpl_var],'array');
+            }
+            if ($merge && is_array($value)) {
+                foreach($value as $_key => $_val) {
+                    $this->_tpl_vars[$tpl_var][$_key] = &$value[$_key];
+                }
+            } else {
+                $this->_tpl_vars[$tpl_var][] = &$value;
+            }
+        }
+    }
+
+
+    /**
+     * clear the given assigned template variable.
+     *
+     * @param string $tpl_var the template variable to clear
+     */
+    function clear_assign($tpl_var)
+    {
+        if (is_array($tpl_var))
+            foreach ($tpl_var as $curr_var)
+                unset($this->_tpl_vars[$curr_var]);
+        else
+            unset($this->_tpl_vars[$tpl_var]);
+    }
+
+
+    /**
+     * Registers custom function to be used in templates
+     *
+     * @param string $function the name of the template function
+     * @param string $function_impl the name of the PHP function to register
+     */
+    function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null)
+    {
+        $this->_plugins['function'][$function] =
+            array($function_impl, null, null, false, $cacheable, $cache_attrs);
+
+    }
+
+    /**
+     * Unregisters custom function
+     *
+     * @param string $function name of template function
+     */
+    function unregister_function($function)
+    {
+        unset($this->_plugins['function'][$function]);
+    }
+
+    /**
+     * Registers object to be used in templates
+     *
+     * @param string $object name of template object
+     * @param object &$object_impl the referenced PHP object to register
+     * @param null|array $allowed list of allowed methods (empty = all)
+     * @param boolean $smarty_args smarty argument format, else traditional
+     * @param null|array $block_functs list of methods that are block format
+     */
+    function register_object($object, &$object_impl, $allowed = array(), $smarty_args = true, $block_methods = array())
+    {
+        settype($allowed, 'array');
+        settype($smarty_args, 'boolean');
+        $this->_reg_objects[$object] =
+            array(&$object_impl, $allowed, $smarty_args, $block_methods);
+    }
+
+    /**
+     * Unregisters object
+     *
+     * @param string $object name of template object
+     */
+    function unregister_object($object)
+    {
+        unset($this->_reg_objects[$object]);
+    }
+
+
+    /**
+     * Registers block function to be used in templates
+     *
+     * @param string $block name of template block
+     * @param string $block_impl PHP function to register
+     */
+    function register_block($block, $block_impl, $cacheable=true, $cache_attrs=null)
+    {
+        $this->_plugins['block'][$block] =
+            array($block_impl, null, null, false, $cacheable, $cache_attrs);
+    }
+
+    /**
+     * Unregisters block function
+     *
+     * @param string $block name of template function
+     */
+    function unregister_block($block)
+    {
+        unset($this->_plugins['block'][$block]);
+    }
+
+    /**
+     * Registers compiler function
+     *
+     * @param string $function name of template function
+     * @param string $function_impl name of PHP function to register
+     */
+    function register_compiler_function($function, $function_impl, $cacheable=true)
+    {
+        $this->_plugins['compiler'][$function] =
+            array($function_impl, null, null, false, $cacheable);
+    }
+
+    /**
+     * Unregisters compiler function
+     *
+     * @param string $function name of template function
+     */
+    function unregister_compiler_function($function)
+    {
+        unset($this->_plugins['compiler'][$function]);
+    }
+
+    /**
+     * Registers modifier to be used in templates
+     *
+     * @param string $modifier name of template modifier
+     * @param string $modifier_impl name of PHP function to register
+     */
+    function register_modifier($modifier, $modifier_impl)
+    {
+        $this->_plugins['modifier'][$modifier] =
+            array($modifier_impl, null, null, false);
+    }
+
+    /**
+     * Unregisters modifier
+     *
+     * @param string $modifier name of template modifier
+     */
+    function unregister_modifier($modifier)
+    {
+        unset($this->_plugins['modifier'][$modifier]);
+    }
+
+    /**
+     * Registers a resource to fetch a template
+     *
+     * @param string $type name of resource
+     * @param array $functions array of functions to handle resource
+     */
+    function register_resource($type, $functions)
+    {
+        if (count($functions)==4) {
+            $this->_plugins['resource'][$type] =
+                array($functions, false);
+
+        } elseif (count($functions)==5) {
+            $this->_plugins['resource'][$type] =
+                array(array(array(&$functions[0], $functions[1])
+                            ,array(&$functions[0], $functions[2])
+                            ,array(&$functions[0], $functions[3])
+                            ,array(&$functions[0], $functions[4]))
+                      ,false);
+
+        } else {
+            $this->trigger_error("malformed function-list for '$type' in register_resource");
+
+        }
+    }
+
+    /**
+     * Unregisters a resource
+     *
+     * @param string $type name of resource
+     */
+    function unregister_resource($type)
+    {
+        unset($this->_plugins['resource'][$type]);
+    }
+
+    /**
+     * Registers a prefilter function to apply
+     * to a template before compiling
+     *
+     * @param string $function name of PHP function to register
+     */
+    function register_prefilter($function)
+    {
+    $_name = (is_array($function)) ? $function[1] : $function;
+        $this->_plugins['prefilter'][$_name]
+            = array($function, null, null, false);
+    }
+
+    /**
+     * Unregisters a prefilter function
+     *
+     * @param string $function name of PHP function
+     */
+    function unregister_prefilter($function)
+    {
+        unset($this->_plugins['prefilter'][$function]);
+    }
+
+    /**
+     * Registers a postfilter function to apply
+     * to a compiled template after compilation
+     *
+     * @param string $function name of PHP function to register
+     */
+    function register_postfilter($function)
+    {
+    $_name = (is_array($function)) ? $function[1] : $function;
+        $this->_plugins['postfilter'][$_name]
+            = array($function, null, null, false);
+    }
+
+    /**
+     * Unregisters a postfilter function
+     *
+     * @param string $function name of PHP function
+     */
+    function unregister_postfilter($function)
+    {
+        unset($this->_plugins['postfilter'][$function]);
+    }
+
+    /**
+     * Registers an output filter function to apply
+     * to a template output
+     *
+     * @param string $function name of PHP function
+     */
+    function register_outputfilter($function)
+    {
+    $_name = (is_array($function)) ? $function[1] : $function;
+        $this->_plugins['outputfilter'][$_name]
+            = array($function, null, null, false);
+    }
+
+    /**
+     * Unregisters an outputfilter function
+     *
+     * @param string $function name of PHP function
+     */
+    function unregister_outputfilter($function)
+    {
+        unset($this->_plugins['outputfilter'][$function]);
+    }
+
+    /**
+     * load a filter of specified type and name
+     *
+     * @param string $type filter type
+     * @param string $name filter name
+     */
+    function load_filter($type, $name)
+    {
+        switch ($type) {
+            case 'output':
+                $_params = array('plugins' => array(array($type . 'filter', $name, null, null, false)));
+                require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
+                smarty_core_load_plugins($_params, $this);
+                break;
+
+            case 'pre':
+            case 'post':
+                if (!isset($this->_plugins[$type . 'filter'][$name]))
+                    $this->_plugins[$type . 'filter'][$name] = false;
+                break;
+        }
+    }
+
+    /**
+     * clear cached content for the given template and cache id
+     *
+     * @param string $tpl_file name of template file
+     * @param string $cache_id name of cache_id
+     * @param string $compile_id name of compile_id
+     * @param string $exp_time expiration time
+     * @return boolean
+     */
+    function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null)
+    {
+
+        if (!isset($compile_id))
+            $compile_id = $this->compile_id;
+
+    if (!isset($tpl_file))
+        $compile_id = null;
+
+    $_auto_id = $this->_get_auto_id($cache_id, $compile_id);
+
+        if (!empty($this->cache_handler_func)) {
+            return call_user_func_array($this->cache_handler_func,
+                                  array('clear', &$this, &$dummy, $tpl_file, $cache_id, $compile_id, $exp_time));
+        } else {
+            $_params = array('auto_base' => $this->cache_dir,
+                            'auto_source' => $tpl_file,
+                            'auto_id' => $_auto_id,
+                            'exp_time' => $exp_time);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rm_auto.php');
+            return smarty_core_rm_auto($_params, $this);
+        }
+
+    }
+
+
+    /**
+     * clear the entire contents of cache (all templates)
+     *
+     * @param string $exp_time expire time
+     * @return boolean results of {@link smarty_core_rm_auto()}
+     */
+    function clear_all_cache($exp_time = null)
+    {
+        if (!empty($this->cache_handler_func)) {
+            $dummy = null;
+            call_user_func_array($this->cache_handler_func,
+                           array('clear', &$this, &$dummy, null, null, null, $exp_time));
+        } else {
+            $_params = array('auto_base' => $this->cache_dir,
+                            'auto_source' => null,
+                            'auto_id' => null,
+                            'exp_time' => $exp_time);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rm_auto.php');
+            return smarty_core_rm_auto($_params, $this);
+        }
+    }
+
+
+    /**
+     * test to see if valid cache exists for this template
+     *
+     * @param string $tpl_file name of template file
+     * @param string $cache_id
+     * @param string $compile_id
+     * @return string|false results of {@link _read_cache_file()}
+     */
+    function is_cached($tpl_file, $cache_id = null, $compile_id = null)
+    {
+        if (!$this->caching)
+            return false;
+
+        if (!isset($compile_id))
+            $compile_id = $this->compile_id;
+
+        $_params = array(
+            'tpl_file' => $tpl_file,
+            'cache_id' => $cache_id,
+            'compile_id' => $compile_id
+        );
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.read_cache_file.php');
+        return smarty_core_read_cache_file($_params, $this);
+    }
+
+
+    /**
+     * clear all the assigned template variables.
+     *
+     */
+    function clear_all_assign()
+    {
+        $this->_tpl_vars = array();
+    }
+
+    /**
+     * clears compiled version of specified template resource,
+     * or all compiled template files if one is not specified.
+     * This function is for advanced use only, not normally needed.
+     *
+     * @param string $tpl_file
+     * @param string $compile_id
+     * @param string $exp_time
+     * @return boolean results of {@link smarty_core_rm_auto()}
+     */
+    function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null)
+    {
+        if (!isset($compile_id)) {
+            $compile_id = $this->compile_id;
+        }
+        $_params = array('auto_base' => $this->compile_dir,
+                        'auto_source' => $tpl_file,
+                        'auto_id' => $compile_id,
+                        'exp_time' => $exp_time,
+                        'extensions' => array('.inc', '.php'));
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rm_auto.php');
+        return smarty_core_rm_auto($_params, $this);
+    }
+
+    /**
+     * Checks whether requested template exists.
+     *
+     * @param string $tpl_file
+     * @return boolean
+     */
+    function template_exists($tpl_file)
+    {
+        $_params = array('resource_name' => $tpl_file, 'quiet'=>true, 'get_source'=>false);
+        return $this->_fetch_resource_info($_params);
+    }
+
+    /**
+     * Returns an array containing template variables
+     *
+     * @param string $name
+     * @param string $type
+     * @return array
+     */
+    function &get_template_vars($name=null)
+    {
+        if(!isset($name)) {
+            return $this->_tpl_vars;
+        }
+        if(isset($this->_tpl_vars[$name])) {
+            return $this->_tpl_vars[$name];
+        }
+    }
+
+    /**
+     * Returns an array containing config variables
+     *
+     * @param string $name
+     * @param string $type
+     * @return array
+     */
+    function &get_config_vars($name=null)
+    {
+        if(!isset($name) && is_array($this->_config[0])) {
+            return $this->_config[0]['vars'];
+        } else if(isset($this->_config[0]['vars'][$name])) {
+            return $this->_config[0]['vars'][$name];
+        }
+    }
+
+    /**
+     * trigger Smarty error
+     *
+     * @param string $error_msg
+     * @param integer $error_type
+     */
+    function trigger_error($error_msg, $error_type = E_USER_WARNING)
+    {
+        trigger_error("Smarty error: $error_msg", $error_type);
+    }
+
+
+    /**
+     * executes & displays the template results
+     *
+     * @param string $resource_name
+     * @param string $cache_id
+     * @param string $compile_id
+     */
+    function display($resource_name, $cache_id = null, $compile_id = null)
+    {
+        $this->fetch($resource_name, $cache_id, $compile_id, true);
+    }
+
+    /**
+     * executes & returns or displays the template results
+     *
+     * @param string $resource_name
+     * @param string $cache_id
+     * @param string $compile_id
+     * @param boolean $display
+     */
+    function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false)
+    {
+        static $_cache_info = array();
+
+        $_smarty_old_error_level = $this->debugging ? error_reporting() : error_reporting(error_reporting() & ~E_NOTICE);
+
+        if (!$this->debugging && $this->debugging_ctrl == 'URL'
+               && @strstr($GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'], $this->_smarty_debug_id)) {
+            // enable debugging from URL
+            $this->debugging = true;
+        }
+
+        if ($this->debugging) {
+            // capture time for debugging info
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $_debug_start_time = smarty_core_get_microtime($_params, $this);
+            $this->_smarty_debug_info[] = array('type'      => 'template',
+                                                'filename'  => $resource_name,
+                                                'depth'     => 0);
+            $_included_tpls_idx = count($this->_smarty_debug_info) - 1;
+        }
+
+        if (!isset($compile_id)) {
+            $compile_id = $this->compile_id;
+        }
+
+        $this->_compile_id = $compile_id;
+        $this->_inclusion_depth = 0;
+
+        if ($this->caching) {
+            // save old cache_info, initialize cache_info
+            array_push($_cache_info, $this->_cache_info);
+            $this->_cache_info = array();
+            $_params = array(
+                'tpl_file' => $resource_name,
+                'cache_id' => $cache_id,
+                'compile_id' => $compile_id,
+                'results' => null
+            );
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.read_cache_file.php');
+            if (smarty_core_read_cache_file($_params, $this)) {
+                $_smarty_results = $_params['results'];
+                if (@count($this->_cache_info['insert_tags'])) {
+                    $_params = array('plugins' => $this->_cache_info['insert_tags']);
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
+                    smarty_core_load_plugins($_params, $this);
+                    $_params = array('results' => $_smarty_results);
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.process_cached_inserts.php');
+                    $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
+                }
+                if (@count($this->_cache_info['cache_serials'])) {
+                    $_params = array('results' => $_smarty_results);
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.process_compiled_include.php');
+                    $_smarty_results = smarty_core_process_compiled_include($_params, $this);
+                }
+
+
+                if ($display) {
+                    if ($this->debugging)
+                    {
+                        // capture time for debugging info
+                        $_params = array();
+                        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+                        $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $_debug_start_time;
+                        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.display_debug_console.php');
+                        $_smarty_results .= smarty_core_display_debug_console($_params, $this);
+                    }
+                    if ($this->cache_modified_check) {
+                        $_last_modified_date = @substr($GLOBALS['HTTP_SERVER_VARS']['HTTP_IF_MODIFIED_SINCE'], 0, strpos($GLOBALS['HTTP_SERVER_VARS']['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3);
+                        $_gmt_mtime = gmdate('D, d M Y H:i:s', $this->_cache_info['timestamp']).' GMT';
+                        if (@count($this->_cache_info['insert_tags']) == 0
+                            && !$this->_cache_serials
+                            && $_gmt_mtime == $_last_modified_date) {
+                            if (php_sapi_name()=='cgi')
+                                header("Status: 304 Not Modified");
+                            else
+                                header("HTTP/1.1 304 Not Modified");
+
+                        } else {
+                            header("Last-Modified: ".$_gmt_mtime);
+                            echo $_smarty_results;
+                        }
+                    } else {
+                            echo $_smarty_results;
+                    }
+                    error_reporting($_smarty_old_error_level);
+                    // restore initial cache_info
+                    $this->_cache_info = array_pop($_cache_info);
+                    return true;
+                } else {
+                    error_reporting($_smarty_old_error_level);
+                    // restore initial cache_info
+                    $this->_cache_info = array_pop($_cache_info);
+                    return $_smarty_results;
+                }
+            } else {
+                $this->_cache_info['template'][$resource_name] = true;
+                if ($this->cache_modified_check) {
+                    header("Last-Modified: ".gmdate('D, d M Y H:i:s', time()).' GMT');
+                }
+            }
+        }
+
+        // load filters that are marked as autoload
+        if (count($this->autoload_filters)) {
+            foreach ($this->autoload_filters as $_filter_type => $_filters) {
+                foreach ($_filters as $_filter) {
+                    $this->load_filter($_filter_type, $_filter);
+                }
+            }
+        }
+
+        $_smarty_compile_path = $this->_get_compile_path($resource_name);
+
+        // if we just need to display the results, don't perform output
+        // buffering - for speed
+        $_cache_including = $this->_cache_including;
+        $this->_cache_including = false;
+        if ($display && !$this->caching && count($this->_plugins['outputfilter']) == 0) {
+            if ($this->_is_compiled($resource_name, $_smarty_compile_path)
+                    || $this->_compile_resource($resource_name, $_smarty_compile_path))
+            {
+                include($_smarty_compile_path);
+            }
+        } else {
+            ob_start();
+            if ($this->_is_compiled($resource_name, $_smarty_compile_path)
+                    || $this->_compile_resource($resource_name, $_smarty_compile_path))
+            {
+                include($_smarty_compile_path);
+            }
+            $_smarty_results = ob_get_contents();
+            ob_end_clean();
+
+            foreach ((array)$this->_plugins['outputfilter'] as $_output_filter) {
+                $_smarty_results = call_user_func_array($_output_filter[0], array($_smarty_results, &$this));
+            }
+        }
+
+        if ($this->caching) {
+            $_params = array('tpl_file' => $resource_name,
+                        'cache_id' => $cache_id,
+                        'compile_id' => $compile_id,
+                        'results' => $_smarty_results);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_cache_file.php');
+            smarty_core_write_cache_file($_params, $this);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.process_cached_inserts.php');
+            $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
+
+            if ($this->_cache_serials) {
+                // strip nocache-tags from output
+                $_smarty_results = preg_replace('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!s'
+                                                ,''
+                                                ,$_smarty_results);
+            }
+            // restore initial cache_info
+            $this->_cache_info = array_pop($_cache_info);
+        }
+        $this->_cache_including = $_cache_including;
+
+        if ($display) {
+            if (isset($_smarty_results)) { echo $_smarty_results; }
+            if ($this->debugging) {
+                // capture time for debugging info
+                $_params = array();
+                require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+                $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = (smarty_core_get_microtime($_params, $this) - $_debug_start_time);
+                require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.display_debug_console.php');
+                echo smarty_core_display_debug_console($_params, $this);
+            }
+            error_reporting($_smarty_old_error_level);
+            return;
+        } else {
+            error_reporting($_smarty_old_error_level);
+            if (isset($_smarty_results)) { return $_smarty_results; }
+        }
+    }
+
+    /**
+     * load configuration values
+     *
+     * @param string $file
+     * @param string $section
+     * @param string $scope
+     */
+    function config_load($file, $section = null, $scope = 'global')
+    {
+        require_once($this->_get_plugin_filepath('function', 'config_load'));
+        smarty_function_config_load(array('file' => $file, 'section' => $section, 'scope' => $scope), $this);
+    }
+
+    /**
+     * return a reference to a registered object
+     *
+     * @param string $name
+     * @return object
+     */
+    function &get_registered_object($name) {
+        if (!isset($this->_reg_objects[$name]))
+        $this->_trigger_fatal_error("'$name' is not a registered object");
+
+        if (!is_object($this->_reg_objects[$name][0]))
+        $this->_trigger_fatal_error("registered '$name' is not an object");
+
+        return $this->_reg_objects[$name][0];
+    }
+
+    /**
+     * clear configuration values
+     *
+     * @param string $var
+     */
+    function clear_config($var = null)
+    {
+        if(!isset($var)) {
+            // clear all values
+            $this->_config = array(array('vars'  => array(),
+                                         'files' => array()));
+        } else {
+            unset($this->_config[0]['vars'][$var]);
+        }
+    }
+
+    /**
+     * get filepath of requested plugin
+     *
+     * @param string $type
+     * @param string $name
+     * @return string|false
+     */
+    function _get_plugin_filepath($type, $name)
+    {
+        $_params = array('type' => $type, 'name' => $name);
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assemble_plugin_filepath.php');
+        return smarty_core_assemble_plugin_filepath($_params, $this);
+    }
+
+   /**
+     * test if resource needs compiling
+     *
+     * @param string $resource_name
+     * @param string $compile_path
+     * @return boolean
+     */
+    function _is_compiled($resource_name, $compile_path)
+    {
+        if (!$this->force_compile && file_exists($compile_path)) {
+            if (!$this->compile_check) {
+                // no need to check compiled file
+                return true;
+            } else {
+                // get file source and timestamp
+                $_params = array('resource_name' => $resource_name, 'get_source'=>false);
+                if (!$this->_fetch_resource_info($_params, $this)) {
+                    return false;
+                }
+                if ($_params['resource_timestamp'] <= filemtime($compile_path)) {
+                    // template not expired, no recompile
+                    return true;
+                } else {
+                    // compile template
+                    return false;
+                }
+            }
+        } else {
+            // compiled template does not exist, or forced compile
+            return false;
+        }
+    }
+
+   /**
+     * compile the template
+     *
+     * @param string $resource_name
+     * @param string $compile_path
+     * @return boolean
+     */
+    function _compile_resource($resource_name, $compile_path)
+    {
+
+        $_params = array('resource_name' => $resource_name);
+        if (!$this->_fetch_resource_info($_params)) {
+            return false;
+        }
+
+        $_source_content = $_params['source_content'];
+        $_resource_timestamp = $_params['resource_timestamp'];
+        $_cache_include    = substr($compile_path, 0, -4).'.inc';
+
+        if ($this->_compile_source($resource_name, $_source_content, $_compiled_content, $_cache_include)) {
+            // if a _cache_serial was set, we also have to write an include-file:
+            if ($this->_cache_include_info) {
+                require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_compiled_include.php');
+                smarty_core_write_compiled_include(array_merge($this->_cache_include_info, array('compiled_content'=>$_compiled_content)),  $this);
+            }
+
+            $_params = array('compile_path'=>$compile_path, 'compiled_content' => $_compiled_content, 'resource_timestamp' => $_resource_timestamp);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_compiled_resource.php');
+            smarty_core_write_compiled_resource($_params, $this);
+
+            return true;
+        } else {
+            $this->trigger_error($smarty_compiler->_error_msg);
+            return false;
+        }
+
+    }
+
+   /**
+     * compile the given source
+     *
+     * @param string $resource_name
+     * @param string $source_content
+     * @param string $compiled_content
+     * @return boolean
+     */
+    function _compile_source($resource_name, &$source_content, &$compiled_content, $cache_include_path=null)
+    {
+        if (file_exists(SMARTY_DIR . $this->compiler_file)) {
+            require_once(SMARTY_DIR . $this->compiler_file);
+        } else {
+            // use include_path
+            require_once($this->compiler_file);
+        }
+
+
+        $smarty_compiler = new $this->compiler_class;
+
+        $smarty_compiler->template_dir      = $this->template_dir;
+        $smarty_compiler->compile_dir       = $this->compile_dir;
+        $smarty_compiler->plugins_dir       = $this->plugins_dir;
+        $smarty_compiler->config_dir        = $this->config_dir;
+        $smarty_compiler->force_compile     = $this->force_compile;
+        $smarty_compiler->caching           = $this->caching;
+        $smarty_compiler->php_handling      = $this->php_handling;
+        $smarty_compiler->left_delimiter    = $this->left_delimiter;
+        $smarty_compiler->right_delimiter   = $this->right_delimiter;
+        $smarty_compiler->_version          = $this->_version;
+        $smarty_compiler->security          = $this->security;
+        $smarty_compiler->secure_dir        = $this->secure_dir;
+        $smarty_compiler->security_settings = $this->security_settings;
+        $smarty_compiler->trusted_dir       = $this->trusted_dir;
+        $smarty_compiler->_reg_objects      = &$this->_reg_objects;
+        $smarty_compiler->_plugins          = &$this->_plugins;
+        $smarty_compiler->_tpl_vars         = &$this->_tpl_vars;
+        $smarty_compiler->default_modifiers = $this->default_modifiers;
+        $smarty_compiler->compile_id        = $this->_compile_id;
+        $smarty_compiler->_config            = $this->_config;
+        $smarty_compiler->request_use_auto_globals  = $this->request_use_auto_globals;
+
+        $smarty_compiler->_cache_serial = null;
+        $smarty_compiler->_cache_include = $cache_include_path;
+
+
+        $_results = $smarty_compiler->_compile_file($resource_name, $source_content, $compiled_content);
+
+        if ($smarty_compiler->_cache_serial) {
+            $this->_cache_include_info = array(
+                'cache_serial'=>$smarty_compiler->_cache_serial
+                ,'plugins_code'=>$smarty_compiler->_plugins_code
+                ,'include_file_path' => $cache_include_path);
+
+        } else {
+            $this->_cache_include_info = null;
+
+        }
+
+        return $_results;
+    }
+
+    /**
+     * Get the compile path for this resource
+     *
+     * @param string $resource_name
+     * @return string results of {@link _get_auto_filename()}
+     */
+    function _get_compile_path($resource_name)
+    {
+        return $this->_get_auto_filename($this->compile_dir, $resource_name,
+                                         $this->_compile_id) . '.php';
+    }
+
+    /**
+     * fetch the template info. Gets timestamp, and source
+     * if get_source is true
+     *
+     * sets $source_content to the source of the template, and
+     * $resource_timestamp to its time stamp
+     * @param string $resource_name
+     * @param string $source_content
+     * @param integer $resource_timestamp
+     * @param boolean $get_source
+     * @param boolean $quiet
+     * @return boolean
+     */
+
+    function _fetch_resource_info(&$params)
+    {
+        if(!isset($params['get_source'])) { $params['get_source'] = true; }
+        if(!isset($params['quiet'])) { $params['quiet'] = false; }
+
+        $_return = false;
+        $_params = array('resource_name' => $params['resource_name']) ;
+        if (isset($params['resource_base_path']))
+            $_params['resource_base_path'] = $params['resource_base_path'];
+
+        if ($this->_parse_resource_name($_params)) {
+            $_resource_type = $_params['resource_type'];
+            $_resource_name = $_params['resource_name'];
+            switch ($_resource_type) {
+                case 'file':
+                    if ($params['get_source']) {
+                        $params['source_content'] = $this->_read_file($_resource_name);
+                    }
+                    $params['resource_timestamp'] = filemtime($_resource_name);
+                    $_return = is_file($_resource_name);
+                    break;
+
+                default:
+                    // call resource functions to fetch the template source and timestamp
+                    if ($params['get_source']) {
+                        $_source_return = isset($this->_plugins['resource'][$_resource_type]) &&
+                            call_user_func_array($this->_plugins['resource'][$_resource_type][0][0],
+                                                 array($_resource_name, &$params['source_content'], &$this));
+                    } else {
+                        $_source_return = true;
+                    }
+
+                    $_timestamp_return = isset($this->_plugins['resource'][$_resource_type]) &&
+                        call_user_func_array($this->_plugins['resource'][$_resource_type][0][1],
+                                             array($_resource_name, &$params['resource_timestamp'], &$this));
+
+                    $_return = $_source_return && $_timestamp_return;
+                    break;
+            }
+        }
+
+        if (!$_return) {
+            // see if we can get a template with the default template handler
+            if (!empty($this->default_template_handler_func)) {
+                if (!is_callable($this->default_template_handler_func)) {
+                    $this->trigger_error("default template handler function \"$this->default_template_handler_func\" doesn't exist.");
+                } else {
+                    $_return = call_user_func_array(
+                        $this->default_template_handler_func,
+                        array($_params['resource_type'], $_params['resource_name'], &$params['source_content'], &$params['resource_timestamp'], &$this));
+                }
+            }
+        }
+
+        if (!$_return) {
+            if (!$params['quiet']) {
+                $this->trigger_error('unable to read resource: "' . $params['resource_name'] . '"');
+            }
+        } else if ($_return && $this->security) {
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.is_secure.php');
+            if (!smarty_core_is_secure($_params, $this)) {
+                if (!$params['quiet'])
+                    $this->trigger_error('(secure mode) accessing "' . $params['resource_name'] . '" is not allowed');
+                $params['source_content'] = null;
+                $params['resource_timestamp'] = null;
+                return false;
+            }
+        }
+        return $_return;
+    }
+
+
+    /**
+     * parse out the type and name from the resource
+     *
+     * @param string $resource_base_path
+     * @param string $resource_name
+     * @param string $resource_type
+     * @param string $resource_name
+     * @return boolean
+     */
+
+    function _parse_resource_name(&$params)
+    {
+
+        // split tpl_path by the first colon
+        $_resource_name_parts = explode(':', $params['resource_name'], 2);
+
+        if (count($_resource_name_parts) == 1) {
+            // no resource type given
+            $params['resource_type'] = $this->default_resource_type;
+            $params['resource_name'] = $_resource_name_parts[0];
+        } else {
+            if(strlen($_resource_name_parts[0]) == 1) {
+                // 1 char is not resource type, but part of filepath
+                $params['resource_type'] = $this->default_resource_type;
+                $params['resource_name'] = $params['resource_name'];
+            } else {
+                $params['resource_type'] = $_resource_name_parts[0];
+                $params['resource_name'] = $_resource_name_parts[1];
+            }
+        }
+
+        if ($params['resource_type'] == 'file') {
+            if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $params['resource_name'])) {
+                // relative pathname to $params['resource_base_path']
+                // use the first directory where the file is found
+                if (isset($params['resource_base_path'])) {
+                    $_resource_base_path = (array)$params['resource_base_path'];
+                } else {
+                    $_resource_base_path = (array)$this->template_dir;
+                    $_resource_base_path[] = '.';
+                }
+                foreach ($_resource_base_path as $_curr_path) {
+                    $_fullpath = $_curr_path . DIRECTORY_SEPARATOR . $params['resource_name'];
+                    if (file_exists($_fullpath) && is_file($_fullpath)) {
+                        $params['resource_name'] = $_fullpath;
+                        return true;
+                    }
+                    // didn't find the file, try include_path
+                    $_params = array('file_path' => $_fullpath);
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_include_path.php');
+                    if(smarty_core_get_include_path($_params, $this)) {
+                        $params['resource_name'] = $_params['new_file_path'];
+                        return true;
+                    }
+                }
+                return false;
+            }
+        } elseif (empty($this->_plugins['resource'][$params['resource_type']])) {
+            $_params = array('type' => $params['resource_type']);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_resource_plugin.php');
+            smarty_core_load_resource_plugin($_params, $this);
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Handle modifiers
+     *
+     * @param string|null $modifier_name
+     * @param array|null $map_array
+     * @return string result of modifiers
+     */
+    function _run_mod_handler()
+    {
+        $_args = func_get_args();
+        list($_modifier_name, $_map_array) = array_splice($_args, 0, 2);
+        list($_func_name, $_tpl_file, $_tpl_line) =
+            $this->_plugins['modifier'][$_modifier_name];
+
+        $_var = $_args[0];
+        foreach ($_var as $_key => $_val) {
+            $_args[0] = $_val;
+            $_var[$_key] = call_user_func_array($_func_name, $_args);
+        }
+        return $_var;
+    }
+
+    /**
+     * Remove starting and ending quotes from the string
+     *
+     * @param string $string
+     * @return string
+     */
+    function _dequote($string)
+    {
+        if (($string{0} == "'" || $string{0} == '"') &&
+            $string{strlen($string)-1} == $string{0})
+            return substr($string, 1, -1);
+        else
+            return $string;
+    }
+
+
+    /**
+     * read in a file from line $start for $lines.
+     * read the entire file if $start and $lines are null.
+     *
+     * @param string $filename
+     * @param integer $start
+     * @param integer $lines
+     * @return string
+     */
+    function _read_file($filename, $start=null, $lines=null)
+    {
+        if (!($fd = @fopen($filename, 'r'))) {
+            return false;
+        }
+        flock($fd, LOCK_SH);
+        if ($start == null && $lines == null) {
+            // read the entire file
+            $contents = fread($fd, filesize($filename));
+        } else {
+            if ( $start > 1 ) {
+                // skip the first lines before $start
+                for ($loop=1; $loop < $start; $loop++) {
+                    fgets($fd, 65536);
+                }
+            }
+            if ( $lines == null ) {
+                // read the rest of the file
+                while (!feof($fd)) {
+                    $contents .= fgets($fd, 65536);
+                }
+            } else {
+                // read up to $lines lines
+                for ($loop=0; $loop < $lines; $loop++) {
+                    $contents .= fgets($fd, 65536);
+                    if (feof($fd)) {
+                        break;
+                    }
+                }
+            }
+        }
+        fclose($fd);
+        return $contents;
+    }
+
+    /**
+     * get a concrete filename for automagically created content
+     *
+     * @param string $auto_base
+     * @param string $auto_source
+     * @param string $auto_id
+     * @return string
+     * @staticvar string|null
+     * @staticvar string|null
+     */
+    function _get_auto_filename($auto_base, $auto_source = null, $auto_id = null)
+    {
+        $_compile_dir_sep =  $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^';
+
+        if(@is_dir($auto_base)) {
+            $_return = $auto_base . DIRECTORY_SEPARATOR;
+        } else {
+            // auto_base not found, try include_path
+            $_params = array('file_path' => $auto_base);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_include_path.php');
+            smarty_core_get_include_path($_params, $this);
+            $_return = isset($_params['new_file_path']) ? $_params['new_file_path'] . DIRECTORY_SEPARATOR : null;
+        }
+
+        if(isset($auto_id)) {
+            // make auto_id safe for directory names
+            $auto_id = str_replace('%7C',$_compile_dir_sep,(urlencode($auto_id)));
+            // split into separate directories
+            $_return .= $auto_id . $_compile_dir_sep;
+        }
+
+        if(isset($auto_source)) {
+            // make source name safe for filename
+            $_filename = urlencode(basename($auto_source));
+            $_crc32 = crc32($auto_source) . $_compile_dir_sep;
+            // prepend %% to avoid name conflicts with
+            // with $params['auto_id'] names
+            $_crc32 = '%%' . substr($_crc32,0,3) . $_compile_dir_sep . '%%' . $_crc32;
+            $_return .= $_crc32 . $_filename;
+        }
+
+        return $_return;
+    }
+
+    /**
+     * unlink a file, possibly using expiration time
+     *
+     * @param string $resource
+     * @param integer $exp_time
+     */
+    function _unlink($resource, $exp_time = null)
+    {
+        if(isset($exp_time)) {
+            if(time() - @filemtime($resource) >= $exp_time) {
+                return @unlink($resource);
+            }
+        } else {
+            return @unlink($resource);
+        }
+    }
+
+    /**
+     * returns an auto_id for auto-file-functions
+     *
+     * @param string $cache_id
+     * @param string $compile_id
+     * @return string|null
+     */
+    function _get_auto_id($cache_id=null, $compile_id=null) {
+    if (isset($cache_id))
+        return (isset($compile_id)) ? $cache_id . '|' . $compile_id  : $cache_id;
+    elseif(isset($compile_id))
+        return $compile_id;
+    else
+        return null;
+    }
+
+    /**
+     * trigger Smarty plugin error
+     *
+     * @param string $error_msg
+     * @param string $tpl_file
+     * @param integer $tpl_line
+     * @param string $file
+     * @param integer $line
+     * @param integer $error_type
+     */
+    function _trigger_fatal_error($error_msg, $tpl_file = null, $tpl_line = null,
+            $file = null, $line = null, $error_type = E_USER_ERROR)
+    {
+        if(isset($file) && isset($line)) {
+            $info = ' ('.basename($file).", line $line)";
+        } else {
+            $info = null;
+        }
+        if (isset($tpl_line) && isset($tpl_file)) {
+            trigger_error("Smarty error: [in " . $tpl_file . " line " .
+                          $tpl_line . "]: $error_msg$info", $error_type);
+        } else {
+            trigger_error("Smarty error: $error_msg$info", $error_type);
+        }
+    }
+
+
+    /**
+     * callback function for preg_replace, to call a non-cacheable block
+     * @return string
+     */
+    function _process_compiled_include_callback($match) {
+        $_func = '_smarty_tplfunc_'.$match[2].'_'.$match[3];
+        ob_start();
+        $_func($this);
+        $_ret = ob_get_contents();
+        ob_end_clean();
+        return $_ret;
+    }
+
+
+    /**
+     * called for included templates
+     *
+     * @param string $_smarty_include_tpl_file
+     * @param string $_smarty_include_vars
+     */
+
+    // $_smarty_include_tpl_file, $_smarty_include_vars
+
+    function _smarty_include($params)
+    {
+        if ($this->debugging) {
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $debug_start_time = smarty_core_get_microtime($_params, $this);
+            $this->_smarty_debug_info[] = array('type'      => 'template',
+                                                  'filename'  => $params['smarty_include_tpl_file'],
+                                                  'depth'     => ++$this->_inclusion_depth);
+            $included_tpls_idx = count($this->_smarty_debug_info) - 1;
+        }
+
+        $this->_tpl_vars = array_merge($this->_tpl_vars, $params['smarty_include_vars']);
+
+        // config vars are treated as local, so push a copy of the
+        // current ones onto the front of the stack
+        array_unshift($this->_config, $this->_config[0]);
+
+        $_smarty_compile_path = $this->_get_compile_path($params['smarty_include_tpl_file']);
+
+
+        if ($this->_is_compiled($params['smarty_include_tpl_file'], $_smarty_compile_path)
+            || $this->_compile_resource($params['smarty_include_tpl_file'], $_smarty_compile_path))
+        {
+            include($_smarty_compile_path);
+        }
+
+        // pop the local vars off the front of the stack
+        array_shift($this->_config);
+
+        $this->_inclusion_depth--;
+
+        if ($this->debugging) {
+            // capture time for debugging info
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $this->_smarty_debug_info[$included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $debug_start_time;
+        }
+
+        if ($this->caching) {
+            $this->_cache_info['template'][$params['smarty_include_tpl_file']] = true;
+        }
+    }
+
+
+    /**
+     * get or set an array of cached attributes for function that is
+     * not cacheable
+     * @return array
+     */
+    function &_smarty_cache_attrs($cache_serial, $count) {
+        $_cache_attrs =& $this->_cache_info['cache_attrs'][$cache_serial][$count];
+
+        if ($this->_cache_including) {
+            /* return next set of cache_attrs */
+            $_return =& current($_cache_attrs);
+            next($_cache_attrs);
+            return $_return;
+
+        } else {
+            /* add a reference to a new set of cache_attrs */
+            $_cache_attrs[] = array();
+            return $_cache_attrs[count($_cache_attrs)-1];
+
+        }
+
+    }
+
+
+    /**
+     * wrapper for include() retaining $this
+     * @return mixed
+     */
+    function _include($filename, $once=false, $params=null)
+    {
+        if ($once) {
+            return include_once($filename);
+        } else {
+            return include($filename);
+        }
+    }
+
+
+    /**
+     * wrapper for eval() retaining $this
+     * @return mixed
+     */
+    function _eval($code, $params=null)
+    {
+        return eval($code);
+    }
+    /**#@-*/
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/Smarty_Compiler.class.php b/smarty/Smarty_Compiler.class.php
new file mode 100644 (file)
index 0000000..e7ee4a8
--- /dev/null
@@ -0,0 +1,2123 @@
+<?php
+
+/**
+ * Project:     Smarty: the PHP compiling template engine
+ * File:        Smarty_Compiler.class.php
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * You may contact the authors of Smarty by e-mail at:
+ * monte@ispi.net
+ * andrei@php.net
+ *
+ * Or, write to:
+ * Monte Ohrt
+ * Director of Technology, ispi
+ * 237 S. 70th suite 220
+ * Lincoln, NE 68510
+ *
+ * The latest version of Smarty can be obtained from:
+ * http://smarty.php.net/
+ *
+ * @link http://smarty.php.net/
+ * @author Monte Ohrt <monte@ispi.net>
+ * @author Andrei Zmievski <andrei@php.net>
+ * @version 2.6.0
+ * @copyright 2001-2003 ispi of Lincoln, Inc.
+ * @package Smarty
+ */
+
+/* $Id: Smarty_Compiler.class.php,v 1.1.1.1 2004/01/13 16:02:46 gohr Exp $ */
+
+/**
+ * Template compiling class
+ * @package Smarty
+ */
+class Smarty_Compiler extends Smarty {
+
+    // internal vars
+    /**#@+
+     * @access private
+     */
+    var $_sectionelse_stack     =   array();    // keeps track of whether section had 'else' part
+    var $_foreachelse_stack     =   array();    // keeps track of whether foreach had 'else' part
+    var $_literal_blocks        =   array();    // keeps literal template blocks
+    var $_php_blocks            =   array();    // keeps php code blocks
+    var $_current_file          =   null;       // the current template being compiled
+    var $_current_line_no       =   1;          // line number for error messages
+    var $_capture_stack         =   array();    // keeps track of nested capture buffers
+    var $_plugin_info           =   array();    // keeps track of plugins to load
+    var $_init_smarty_vars      =   false;
+    var $_permitted_tokens      =   array('true','false','yes','no','on','off','null');
+    var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
+    var $_si_qstr_regexp        =   null;
+    var $_qstr_regexp           =   null;
+    var $_func_regexp           =   null;
+    var $_var_bracket_regexp    =   null;
+    var $_dvar_guts_regexp      =   null;
+    var $_dvar_regexp           =   null;
+    var $_cvar_regexp           =   null;
+    var $_svar_regexp           =   null;
+    var $_avar_regexp           =   null;
+    var $_mod_regexp            =   null;
+    var $_var_regexp            =   null;
+    var $_parenth_param_regexp  =   null;
+    var $_func_call_regexp      =   null;
+    var $_obj_ext_regexp        =   null;
+    var $_obj_start_regexp      =   null;
+    var $_obj_params_regexp     =   null;
+    var $_obj_call_regexp       =   null;
+    var $_cacheable_state       =   0;
+    var $_cache_attrs_count     =   0;
+    var $_nocache_count         =   0;
+    var $_cache_serial          =   null;
+    var $_cache_include         =   null;
+
+    var $_strip_depth           =   0;
+    var $_additional_newline    =   "\n";
+
+    /**#@-*/
+    /**
+     * The class constructor.
+     */
+    function Smarty_Compiler()
+    {
+        // matches double quoted strings:
+        // "foobar"
+        // "foo\"bar"
+        $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
+
+        // matches single quoted strings:
+        // 'foobar'
+        // 'foo\'bar'
+        $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
+
+        // matches single or double quoted strings
+        $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
+
+        // matches bracket portion of vars
+        // [0]
+        // [foo]
+        // [$bar]
+        $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
+
+        // matches $ vars (not objects):
+        // $foo
+        // $foo.bar
+        // $foo.bar.foobar
+        // $foo[0]
+        // $foo[$bar]
+        // $foo[5][blah]
+        // $foo[5].bar[$foobar][4]
+        $this->_dvar_math_regexp = '[\+\-\*\/\%]';
+        $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
+        $this->_dvar_num_var_regexp = '\-?\d+(?:\.\d+)?' . $this->_dvar_math_var_regexp;
+        $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
+                . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:\-?\d+(?:\.\d+)?|' . $this->_dvar_math_var_regexp . ')*)?';
+        $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
+
+        // matches config vars:
+        // #foo#
+        // #foobar123_foo#
+        $this->_cvar_regexp = '\#\w+\#';
+
+        // matches section vars:
+        // %foo.bar%
+        $this->_svar_regexp = '\%\w+\.\w+\%';
+
+        // matches all valid variables (no quotes, no modifiers)
+        $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
+           . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
+
+        // matches valid variable syntax:
+        // $foo
+        // $foo
+        // #foo#
+        // #foo#
+        // "text"
+        // "text"
+        $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
+
+        // matches valid object call (no objects allowed in parameters):
+        // $foo->bar
+        // $foo->bar()
+        // $foo->bar("text")
+        // $foo->bar($foo, $bar, "text")
+        // $foo->bar($foo, "foo")
+        // $foo->bar->foo()
+        // $foo->bar->foo->bar()
+        $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
+        $this->_obj_params_regexp = '\((?:\w+|'
+                . $this->_var_regexp . '(?:\s*,\s*(?:(?:\w+|'
+                . $this->_var_regexp . ')))*)?\)';
+        $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
+        $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?)';
+
+        // matches valid modifier syntax:
+        // |foo
+        // |@foo
+        // |foo:"bar"
+        // |foo:$bar
+        // |foo:"bar":$foobar
+        // |foo|bar
+        // |foo:$foo->bar
+        $this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|'
+           . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
+
+        // matches valid function name:
+        // foo123
+        // _foo_bar
+        $this->_func_regexp = '[a-zA-Z_]\w*';
+
+        // matches valid registered object:
+        // foo->bar
+        $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
+
+        // matches valid parameter values:
+        // true
+        // $foo
+        // $foo|bar
+        // #foo#
+        // #foo#|bar
+        // "text"
+        // "text"|bar
+        // $foo->bar
+        $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
+           . $this->_var_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
+
+        // matches valid parenthesised function parameters:
+        //
+        // "text"
+        //    $foo, $bar, "text"
+        // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
+        $this->_parenth_param_regexp = '(?:\((?:\w+|'
+                . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
+                . $this->_param_regexp . ')))*)?\))';
+
+        // matches valid function call:
+        // foo()
+        // foo_bar($foo)
+        // _foo_bar($foo,"bar")
+        // foo123($foo,$foo->bar(),"foo")
+        $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
+           . $this->_parenth_param_regexp . '))';
+    }
+
+    /**
+     * compile a resource
+     *
+     * sets $compiled_content to the compiled source
+     * @param string $resource_name
+     * @param string $source_content
+     * @param string $compiled_content
+     * @return true
+     */
+    function _compile_file($resource_name, $source_content, &$compiled_content)
+    {
+
+        if ($this->security) {
+            // do not allow php syntax to be executed unless specified
+            if ($this->php_handling == SMARTY_PHP_ALLOW &&
+                !$this->security_settings['PHP_HANDLING']) {
+                $this->php_handling = SMARTY_PHP_PASSTHRU;
+            }
+        }
+
+        $this->_load_filters();
+
+        $this->_current_file = $resource_name;
+        $this->_current_line_no = 1;
+        $ldq = preg_quote($this->left_delimiter, '!');
+        $rdq = preg_quote($this->right_delimiter, '!');
+
+        // run template source through prefilter functions
+        if (count($this->_plugins['prefilter']) > 0) {
+            foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
+                if ($prefilter === false) continue;
+                if ($prefilter[3] || is_callable($prefilter[0])) {
+                    $source_content = call_user_func_array($prefilter[0],
+                                                            array($source_content, &$this));
+                    $this->_plugins['prefilter'][$filter_name][3] = true;
+                } else {
+                    $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
+                }
+            }
+        }
+
+        /* Annihilate the comments. */
+        $source_content = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se",
+                                        "'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'",
+                                        $source_content);
+
+        /* Pull out the literal blocks. */
+        preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $source_content, $_match);
+        $this->_literal_blocks = $_match[1];
+        $source_content = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s",
+                                        $this->_quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $source_content);
+
+        /* Pull out the php code blocks. */
+        preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $source_content, $_match);
+        $this->_php_blocks = $_match[1];
+        $source_content = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s",
+                                        $this->_quote_replace($this->left_delimiter.'php'.$this->right_delimiter), $source_content);
+
+        /* Gather all template tags. */
+        preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
+        $template_tags = $_match[1];
+        /* Split content by template tags to obtain non-template content. */
+        $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
+
+        /* loop through text blocks */
+        for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
+            /* match anything resembling php tags */
+            if (preg_match_all('!(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)!is', $text_blocks[$curr_tb], $sp_match)) {
+                /* replace tags with placeholders to prevent recursive replacements */
+                $sp_match[1] = array_unique($sp_match[1]);
+                usort($sp_match[1], '_smarty_sort_length');
+                for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
+                    $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
+                }
+                /* process each one */
+                for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
+                    if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
+                        /* echo php contents */
+                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
+                    } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
+                        /* quote php tags */
+                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
+                    } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
+                        /* remove php tags */
+                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
+                    } else {
+                        /* SMARTY_PHP_ALLOW, but echo non php starting tags */
+                        $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
+                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
+                    }
+                }
+            }
+        }
+
+        /* Compile the template tags into PHP code. */
+        $compiled_tags = array();
+        for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
+            $this->_current_line_no += substr_count($text_blocks[$i], "\n");
+            $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
+            $this->_current_line_no += substr_count($template_tags[$i], "\n");
+        }
+
+        $compiled_content = '';
+
+        /* Interleave the compiled contents and text blocks to get the final result. */
+        for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
+            if ($compiled_tags[$i] == '') {
+                // tag result empty, remove first newline from following text block
+                $text_blocks[$i+1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+1]);
+            }
+            $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
+        }
+        $compiled_content .= $text_blocks[$i];
+
+        /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
+        if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
+            $strip_tags = $_match[0];
+            $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
+            $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
+            for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
+                $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
+                                                  $this->_quote_replace($strip_tags_modified[$i]),
+                                                  $compiled_content, 1);
+        }
+
+        // remove \n from the end of the file, if any
+        if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
+            $compiled_content = substr($compiled_content, 0, -1);
+        }
+
+        if (!empty($this->_cache_serial)) {
+            $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
+        }
+
+        // remove unnecessary close/open tags
+        $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
+
+        // run compiled template through postfilter functions
+        if (count($this->_plugins['postfilter']) > 0) {
+            foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
+                if ($postfilter === false) continue;
+                if ($postfilter[3] || is_callable($postfilter[0])) {
+                    $compiled_content = call_user_func_array($postfilter[0],
+                                                              array($compiled_content, &$this));
+                    $this->_plugins['postfilter'][$filter_name][3] = true;
+                } else {
+                    $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
+                }
+            }
+        }
+
+        // put header at the top of the compiled template
+        $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
+        $template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
+
+        /* Emit code to load needed plugins. */
+        $this->_plugins_code = '';
+        if (count($this->_plugin_info)) {
+            $_plugins_params = "array('plugins' => array(";
+            foreach ($this->_plugin_info as $plugin_type => $plugins) {
+                foreach ($plugins as $plugin_name => $plugin_info) {
+                    $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
+                    $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
+                }
+            }
+            $_plugins_params .= '))';
+            $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
+            $template_header .= $plugins_code;
+            $this->_plugin_info = array();
+            $this->_plugins_code = $plugins_code;
+        }
+
+        if ($this->_init_smarty_vars) {
+            $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
+            $this->_init_smarty_vars = false;
+        }
+
+        $compiled_content = $template_header . $compiled_content;
+
+        return true;
+    }
+
+    /**
+     * Compile a template tag
+     *
+     * @param string $template_tag
+     * @return string
+     */
+    function _compile_tag($template_tag)
+    {
+        /* Matched comment. */
+        if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
+            return '';
+
+        /* Split tag into two three parts: command, command modifiers and the arguments. */
+        if(! preg_match('/^(?:(' . $this->_obj_call_regexp . '|' . $this->_var_regexp
+                . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
+                      (?:\s+(.*))?$
+                    /xs', $template_tag, $match)) {
+            $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        $tag_command = $match[1];
+        $tag_modifier = isset($match[2]) ? $match[2] : null;
+        $tag_args = isset($match[3]) ? $match[3] : null;
+
+        if (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$!', $tag_command)) {
+            /* tag name is a variable or object */
+            $_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args));
+            if(isset($_tag_attrs['assign'])) {
+                return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n";
+            } else {
+                return "<?php echo $_return; ?>" . $this->_additional_newline;
+            }
+        }
+
+        /* If the tag name is a registered object, we process it. */
+        if (preg_match('!^\/?' . $this->_reg_obj_regexp . '$!', $tag_command)) {
+            return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
+        }
+
+        switch ($tag_command) {
+            case 'include':
+                return $this->_compile_include_tag($tag_args);
+
+            case 'include_php':
+                return $this->_compile_include_php_tag($tag_args);
+
+            case 'if':
+                return $this->_compile_if_tag($tag_args);
+
+            case 'else':
+                return '<?php else: ?>';
+
+            case 'elseif':
+                return $this->_compile_if_tag($tag_args, true);
+
+            case '/if':
+                return '<?php endif; ?>';
+
+            case 'capture':
+                return $this->_compile_capture_tag(true, $tag_args);
+
+            case '/capture':
+                return $this->_compile_capture_tag(false);
+
+            case 'ldelim':
+                return $this->left_delimiter;
+
+            case 'rdelim':
+                return $this->right_delimiter;
+
+            case 'section':
+                array_push($this->_sectionelse_stack, false);
+                return $this->_compile_section_start($tag_args);
+
+            case 'sectionelse':
+                $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
+                return "<?php endfor; else: ?>";
+
+            case '/section':
+                if (array_pop($this->_sectionelse_stack))
+                    return "<?php endif; ?>";
+                else
+                    return "<?php endfor; endif; ?>";
+
+            case 'foreach':
+                array_push($this->_foreachelse_stack, false);
+                return $this->_compile_foreach_start($tag_args);
+                break;
+
+            case 'foreachelse':
+                $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
+                return "<?php endforeach; unset(\$_from); else: ?>";
+
+            case '/foreach':
+                if (array_pop($this->_foreachelse_stack))
+                    return "<?php endif; ?>";
+                else
+                    return "<?php endforeach; unset(\$_from); endif; ?>";
+
+            case 'strip':
+            case '/strip':
+                if ($tag_command{0}=='/') {
+                    if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
+                        $this->_additional_newline = "\n";
+                        return $this->left_delimiter.$tag_command.$this->right_delimiter;
+                    }
+                } else {
+                    if ($this->_strip_depth++==0) { /* outermost opening {strip} */
+                        $this->_additional_newline = "";
+                        return $this->left_delimiter.$tag_command.$this->right_delimiter;
+                    }
+                }
+                return '';
+
+            case 'literal':
+                list (,$literal_block) = each($this->_literal_blocks);
+                $this->_current_line_no += substr_count($literal_block, "\n");
+                return "<?php echo '".str_replace("'", "\'", str_replace("\\", "\\\\", $literal_block))."'; ?>" . $this->_additional_newline;
+
+            case 'php':
+                if ($this->security && !$this->security_settings['PHP_TAGS']) {
+                    $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
+                    return;
+                }
+                list (,$php_block) = each($this->_php_blocks);
+                $this->_current_line_no += substr_count($php_block, "\n");
+                return '<?php '.$php_block.' ?>';
+
+            case 'insert':
+                return $this->_compile_insert_tag($tag_args);
+
+            default:
+                if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
+                    return $output;
+                } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
+                    return $output;
+                } else {
+                    return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
+                }
+        }
+    }
+
+
+    /**
+     * compile the custom compiler tag
+     *
+     * sets $output to the compiled custom compiler tag
+     * @param string $tag_command
+     * @param string $tag_args
+     * @param string $output
+     * @return boolean
+     */
+    function _compile_compiler_tag($tag_command, $tag_args, &$output)
+    {
+        $found = false;
+        $have_function = true;
+
+        /*
+         * First we check if the compiler function has already been registered
+         * or loaded from a plugin file.
+         */
+        if (isset($this->_plugins['compiler'][$tag_command])) {
+            $found = true;
+            $plugin_func = $this->_plugins['compiler'][$tag_command][0];
+            if (!is_callable($plugin_func)) {
+                $message = "compiler function '$tag_command' is not implemented";
+                $have_function = false;
+            }
+        }
+        /*
+         * Otherwise we need to load plugin file and look for the function
+         * inside it.
+         */
+        else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
+            $found = true;
+
+            include_once $plugin_file;
+
+            $plugin_func = 'smarty_compiler_' . $tag_command;
+            if (!is_callable($plugin_func)) {
+                $message = "plugin function $plugin_func() not found in $plugin_file\n";
+                $have_function = false;
+            } else {
+                $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
+            }
+        }
+
+        /*
+         * True return value means that we either found a plugin or a
+         * dynamically registered function. False means that we didn't and the
+         * compiler should now emit code to load custom function plugin for this
+         * tag.
+         */
+        if ($found) {
+            if ($have_function) {
+                $output = call_user_func_array($plugin_func, array($tag_args, &$this));
+                if($output != '') {
+                $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
+                                   . $output
+                                   . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
+                }
+            } else {
+                $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * compile block function tag
+     *
+     * sets $output to compiled block function tag
+     * @param string $tag_command
+     * @param string $tag_args
+     * @param string $tag_modifier
+     * @param string $output
+     * @return boolean
+     */
+    function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
+    {
+        if ($tag_command{0} == '/') {
+            $start_tag = false;
+            $tag_command = substr($tag_command, 1);
+        } else
+            $start_tag = true;
+
+        $found = false;
+        $have_function = true;
+
+        /*
+         * First we check if the block function has already been registered
+         * or loaded from a plugin file.
+         */
+        if (isset($this->_plugins['block'][$tag_command])) {
+            $found = true;
+            $plugin_func = $this->_plugins['block'][$tag_command][0];
+            if (!is_callable($plugin_func)) {
+                $message = "block function '$tag_command' is not implemented";
+                $have_function = false;
+            }
+        }
+        /*
+         * Otherwise we need to load plugin file and look for the function
+         * inside it.
+         */
+        else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
+            $found = true;
+
+            include_once $plugin_file;
+
+            $plugin_func = 'smarty_block_' . $tag_command;
+            if (!function_exists($plugin_func)) {
+                $message = "plugin function $plugin_func() not found in $plugin_file\n";
+                $have_function = false;
+            } else {
+                $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
+
+            }
+        }
+
+        if (!$found) {
+            return false;
+        } else if (!$have_function) {
+            $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
+            return true;
+        }
+
+        /*
+         * Even though we've located the plugin function, compilation
+         * happens only once, so the plugin will still need to be loaded
+         * at runtime for future requests.
+         */
+        $this->_add_plugin('block', $tag_command);
+
+        if ($start_tag) {
+            $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
+            $attrs = $this->_parse_attrs($tag_args);
+            $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
+            $output .= "$_cache_attrs\$_params = \$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
+            $output .= $this->_compile_plugin_call('block', $tag_command).'($_params[1], null, $this, $_block_repeat=true); unset($_params);';
+            $output .= 'while ($_block_repeat) { ob_start(); ?>';
+        } else {
+            $output = '<?php $this->_block_content = ob_get_contents(); ob_end_clean(); ';
+            $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $this->_block_content, $this, $_block_repeat=false)';
+            if ($tag_modifier != '') {
+                $this->_parse_modifiers($_out_tag_text, $tag_modifier);
+            }
+            $output .= 'echo '.$_out_tag_text.'; } ';
+            $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
+        }
+
+        return true;
+    }
+
+
+    /**
+     * compile custom function tag
+     *
+     * @param string $tag_command
+     * @param string $tag_args
+     * @param string $tag_modifier
+     * @return string
+     */
+    function _compile_custom_tag($tag_command, $tag_args, $tag_modifier)
+    {
+        $this->_add_plugin('function', $tag_command);
+
+        $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
+        $attrs = $this->_parse_attrs($tag_args);
+        $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
+
+        $_return = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
+        if($tag_modifier != '') {
+            $this->_parse_modifiers($_return, $tag_modifier);
+        }
+
+        if($_return != '') {
+            $_return =  '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $_return . ';'
+                . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
+        }
+
+        return $_return;
+    }
+
+    /**
+     * compile a registered object tag
+     *
+     * @param string $tag_command
+     * @param array $attrs
+     * @param string $tag_modifier
+     * @return string
+     */
+    function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
+    {
+        if ($tag_command{0} == '/') {
+            $start_tag = false;
+            $tag_command = substr($tag_command, 1);
+        } else {
+            $start_tag = true;
+        }
+
+        list($object, $obj_comp) = explode('->', $tag_command);
+
+        $arg_list = array();
+        if(count($attrs)) {
+            $_assign_var = false;
+            foreach ($attrs as $arg_name => $arg_value) {
+                if($arg_name == 'assign') {
+                    $_assign_var = $arg_value;
+                    unset($attrs['assign']);
+                    continue;
+                }
+                if (is_bool($arg_value))
+                    $arg_value = $arg_value ? 'true' : 'false';
+                $arg_list[] = "'$arg_name' => $arg_value";
+            }
+        }
+
+        if($this->_reg_objects[$object][2]) {
+            // smarty object argument format
+            $args = "array(".implode(',', (array)$arg_list)."), \$this";
+        } else {
+            // traditional argument format
+            $args = implode(',', array_values($attrs));
+            if (empty($args)) {
+                $args = 'null';
+            }
+        }
+
+        $prefix = '';
+        $postfix = '';
+        $newline = '';
+        if(!is_object($this->_reg_objects[$object][0])) {
+            $this->_trigger_fatal_error("registered '$object' is not an object");
+        } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
+            $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'");
+        } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
+            // method
+            if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
+                // block method
+                if ($start_tag) {
+                    $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
+                    $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
+                    $prefix .= "while (\$_block_repeat) { ob_start();";
+                    $return = null;
+                    $postfix = '';
+            } else {
+                    $prefix = "\$this->_obj_block_content = ob_get_contents(); ob_end_clean(); ";
+                    $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_obj_block_content, \$this, \$_block_repeat=false)";
+                    $postfix = "} array_pop(\$this->_tag_stack);";
+                }
+            } else {
+                // non-block method
+                $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
+            }
+        } else {
+            // property
+            $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
+        }
+
+        if($return != null) {
+            if($tag_modifier != '') {
+                $this->_parse_modifiers($return, $tag_modifier);
+            }
+
+            if(!empty($_assign_var)) {
+                $output = "\$this->assign('" . $this->_dequote($_assign_var) ."',  $return);";
+            } else {
+                $output = 'echo ' . $return . ';';
+                $newline = $this->_additional_newline;
+            }
+        } else {
+            $output = '';
+        }
+
+        return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
+    }
+
+    /**
+     * Compile {insert ...} tag
+     *
+     * @param string $tag_args
+     * @return string
+     */
+    function _compile_insert_tag($tag_args)
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+        $name = $this->_dequote($attrs['name']);
+
+        if (empty($name)) {
+            $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        if (!empty($attrs['script'])) {
+            $delayed_loading = true;
+        } else {
+            $delayed_loading = false;
+        }
+
+        foreach ($attrs as $arg_name => $arg_value) {
+            if (is_bool($arg_value))
+                $arg_value = $arg_value ? 'true' : 'false';
+            $arg_list[] = "'$arg_name' => $arg_value";
+        }
+
+        $this->_add_plugin('insert', $name, $delayed_loading);
+
+        $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
+
+        return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
+    }
+
+    /**
+     * Compile {include ...} tag
+     *
+     * @param string $tag_args
+     * @return string
+     */
+    function _compile_include_tag($tag_args)
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+        $arg_list = array();
+
+        if (empty($attrs['file'])) {
+            $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        foreach ($attrs as $arg_name => $arg_value) {
+            if ($arg_name == 'file') {
+                $include_file = $arg_value;
+                continue;
+            } else if ($arg_name == 'assign') {
+                $assign_var = $arg_value;
+                continue;
+            }
+            if (is_bool($arg_value))
+                $arg_value = $arg_value ? 'true' : 'false';
+            $arg_list[] = "'$arg_name' => $arg_value";
+        }
+
+        $output = '<?php ';
+
+        if (isset($assign_var)) {
+            $output .= "ob_start();\n";
+        }
+
+        $output .=
+            "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
+
+
+        $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
+        $output .= "\$this->_smarty_include($_params);\n" .
+        "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
+        "unset(\$_smarty_tpl_vars);\n";
+
+        if (isset($assign_var)) {
+            $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
+        }
+
+        $output .= ' ?>';
+
+        return $output;
+
+    }
+
+    /**
+     * Compile {include ...} tag
+     *
+     * @param string $tag_args
+     * @return string
+     */
+    function _compile_include_php_tag($tag_args)
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+
+        if (empty($attrs['file'])) {
+            $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
+        $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
+
+        foreach($attrs as $arg_name => $arg_value) {
+            if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
+                if(is_bool($arg_value))
+                    $arg_value = $arg_value ? 'true' : 'false';
+                $arg_list[] = "'$arg_name' => $arg_value";
+            }
+        }
+
+        $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
+
+        return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
+    }
+
+
+    /**
+     * Compile {section ...} tag
+     *
+     * @param string $tag_args
+     * @return string
+     */
+    function _compile_section_start($tag_args)
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+        $arg_list = array();
+
+        $output = '<?php ';
+        $section_name = $attrs['name'];
+        if (empty($section_name)) {
+            $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        $output .= "if (isset(\$this->_sections[$section_name])) unset(\$this->_sections[$section_name]);\n";
+        $section_props = "\$this->_sections[$section_name]";
+
+        foreach ($attrs as $attr_name => $attr_value) {
+            switch ($attr_name) {
+                case 'loop':
+                    $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
+                    break;
+
+                case 'show':
+                    if (is_bool($attr_value))
+                        $show_attr_value = $attr_value ? 'true' : 'false';
+                    else
+                        $show_attr_value = "(bool)$attr_value";
+                    $output .= "{$section_props}['show'] = $show_attr_value;\n";
+                    break;
+
+                case 'name':
+                    $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
+                    break;
+
+                case 'max':
+                case 'start':
+                    $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
+                    break;
+
+                case 'step':
+                    $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
+                    break;
+
+                default:
+                    $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
+                    break;
+            }
+        }
+
+        if (!isset($attrs['show']))
+            $output .= "{$section_props}['show'] = true;\n";
+
+        if (!isset($attrs['loop']))
+            $output .= "{$section_props}['loop'] = 1;\n";
+
+        if (!isset($attrs['max']))
+            $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
+        else
+            $output .= "if ({$section_props}['max'] < 0)\n" .
+                       "    {$section_props}['max'] = {$section_props}['loop'];\n";
+
+        if (!isset($attrs['step']))
+            $output .= "{$section_props}['step'] = 1;\n";
+
+        if (!isset($attrs['start']))
+            $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
+        else {
+            $output .= "if ({$section_props}['start'] < 0)\n" .
+                       "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
+                       "else\n" .
+                       "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
+        }
+
+        $output .= "if ({$section_props}['show']) {\n";
+        if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
+            $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
+        } else {
+            $output .= "    {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
+        }
+        $output .= "    if ({$section_props}['total'] == 0)\n" .
+                   "        {$section_props}['show'] = false;\n" .
+                   "} else\n" .
+                   "    {$section_props}['total'] = 0;\n";
+
+        $output .= "if ({$section_props}['show']):\n";
+        $output .= "
+            for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
+                 {$section_props}['iteration'] <= {$section_props}['total'];
+                 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
+        $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
+        $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
+        $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
+        $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
+        $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
+
+        $output .= "?>";
+
+        return $output;
+    }
+
+
+    /**
+     * Compile {foreach ...} tag.
+     *
+     * @param string $tag_args
+     * @return string
+     */
+    function _compile_foreach_start($tag_args)
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+        $arg_list = array();
+
+        if (empty($attrs['from'])) {
+            $this->_syntax_error("missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        if (empty($attrs['item'])) {
+            $this->_syntax_error("missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        $from = $attrs['from'];
+        $item = $this->_dequote($attrs['item']);
+        if (isset($attrs['name']))
+            $name = $attrs['name'];
+
+        $output = '<?php ';
+        if (isset($name)) {
+            $output .= "if (isset(\$this->_foreach[$name])) unset(\$this->_foreach[$name]);\n";
+            $foreach_props = "\$this->_foreach[$name]";
+        }
+
+        $key_part = '';
+
+        foreach ($attrs as $attr_name => $attr_value) {
+            switch ($attr_name) {
+                case 'key':
+                    $key  = $this->_dequote($attrs['key']);
+                    $key_part = "\$this->_tpl_vars['$key'] => ";
+                    break;
+
+                case 'name':
+                    $output .= "{$foreach_props}['$attr_name'] = $attr_value;\n";
+                    break;
+            }
+        }
+
+        if (isset($name)) {
+            $output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
+            $output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
+            $output .= "if ({$foreach_props}['show']):\n";
+            $output .= "{$foreach_props}['iteration'] = 0;\n";
+            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
+            $output .= "        {$foreach_props}['iteration']++;\n";
+            $output .= "        {$foreach_props}['first'] = ({$foreach_props}['iteration'] == 1);\n";
+            $output .= "        {$foreach_props}['last']  = ({$foreach_props}['iteration'] == {$foreach_props}['total']);\n";
+        } else {
+            $output .= "if (count(\$_from = (array)$from)):\n";
+            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
+        }
+        $output .= '?>';
+
+        return $output;
+    }
+
+
+    /**
+     * Compile {capture} .. {/capture} tags
+     *
+     * @param boolean $start true if this is the {capture} tag
+     * @param string $tag_args
+     * @return string
+     */
+
+    function _compile_capture_tag($start, $tag_args = '')
+    {
+        $attrs = $this->_parse_attrs($tag_args);
+
+        if ($start) {
+            if (isset($attrs['name']))
+                $buffer = $attrs['name'];
+            else
+                $buffer = "'default'";
+
+            if (isset($attrs['assign']))
+                $assign = $attrs['assign'];
+            else
+                $assign = null;
+            $output = "<?php ob_start(); ?>";
+            $this->_capture_stack[] = array($buffer, $assign);
+        } else {
+            list($buffer, $assign) = array_pop($this->_capture_stack);
+            $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
+            if (isset($assign)) {
+                $output .= " \$this->assign($assign, ob_get_contents());";
+            }
+            $output .= "ob_end_clean(); ?>";
+        }
+
+        return $output;
+    }
+
+    /**
+     * Compile {if ...} tag
+     *
+     * @param string $tag_args
+     * @param boolean $elseif if true, uses elseif instead of if
+     * @return string
+     */
+    function _compile_if_tag($tag_args, $elseif = false)
+    {
+
+        /* Tokenize args for 'if' tag. */
+        preg_match_all('/(?>
+                ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
+                ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
+                \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
+                \b\w+\b                                                        | # valid word token
+                \S+                                                           # anything else
+                )/x', $tag_args, $match);
+
+        $tokens = $match[0];
+
+        // make sure we have balanced parenthesis
+        $token_count = array_count_values($tokens);
+        if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
+            $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        $is_arg_stack = array();
+
+        for ($i = 0; $i < count($tokens); $i++) {
+
+            $token = &$tokens[$i];
+
+            switch (strtolower($token)) {
+                case '!':
+                case '%':
+                case '!==':
+                case '==':
+                case '===':
+                case '>':
+                case '<':
+                case '!=':
+                case '<>':
+                case '<<':
+                case '>>':
+                case '<=':
+                case '>=':
+                case '&&':
+                case '||':
+                case '|':
+                case '^':
+                case '&':
+                case '~':
+                case ')':
+                case ',':
+                case '+':
+                case '-':
+                case '*':
+                case '/':
+                case '@':
+                    break;
+
+                case 'eq':
+                    $token = '==';
+                    break;
+
+                case 'ne':
+                case 'neq':
+                    $token = '!=';
+                    break;
+
+                case 'lt':
+                    $token = '<';
+                    break;
+
+                case 'le':
+                case 'lte':
+                    $token = '<=';
+                    break;
+
+                case 'gt':
+                    $token = '>';
+                    break;
+
+                case 'ge':
+                case 'gte':
+                    $token = '>=';
+                    break;
+
+                case 'and':
+                    $token = '&&';
+                    break;
+
+                case 'or':
+                    $token = '||';
+                    break;
+
+                case 'not':
+                    $token = '!';
+                    break;
+
+                case 'mod':
+                    $token = '%';
+                    break;
+
+                case '(':
+                    array_push($is_arg_stack, $i);
+                    break;
+
+                case 'is':
+                    /* If last token was a ')', we operate on the parenthesized
+                       expression. The start of the expression is on the stack.
+                       Otherwise, we operate on the last encountered token. */
+                    if ($tokens[$i-1] == ')')
+                        $is_arg_start = array_pop($is_arg_stack);
+                    else
+                        $is_arg_start = $i-1;
+                    /* Construct the argument for 'is' expression, so it knows
+                       what to operate on. */
+                    $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
+
+                    /* Pass all tokens from next one until the end to the
+                       'is' expression parsing function. The function will
+                       return modified tokens, where the first one is the result
+                       of the 'is' expression and the rest are the tokens it
+                       didn't touch. */
+                    $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
+
+                    /* Replace the old tokens with the new ones. */
+                    array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
+
+                    /* Adjust argument start so that it won't change from the
+                       current position for the next iteration. */
+                    $i = $is_arg_start;
+                    break;
+
+                default:
+                    if(preg_match('!^' . $this->_func_regexp . '$!', $token) ) {
+                            // function call
+                            if($this->security &&
+                               !in_array($token, $this->security_settings['IF_FUNCS'])) {
+                                $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
+                            }
+                    } elseif(preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$!', $token)) {
+                        // object or variable
+                        $token = $this->_parse_var_props($token);
+                    } elseif(is_numeric($token)) {
+                        // number, skip it
+                    } else {
+                        $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
+                    }
+                    break;
+            }
+        }
+
+        if ($elseif)
+            return '<?php elseif ('.implode(' ', $tokens).'): ?>';
+        else
+            return '<?php if ('.implode(' ', $tokens).'): ?>';
+    }
+
+
+    function _compile_arg_list($type, $name, $attrs, &$cache_code) {
+        $arg_list = array();
+
+        if (isset($type) && isset($name)
+            && isset($this->_plugins[$type])
+            && isset($this->_plugins[$type][$name])
+            && empty($this->_plugins[$type][$name][4])
+            && is_array($this->_plugins[$type][$name][5])
+            ) {
+            /* we have a list of parameters that should be cached */
+            $_cache_attrs = $this->_plugins[$type][$name][5];
+            $_count = $this->_cache_attrs_count++;
+            $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
+
+        } else {
+            /* no parameters are cached */
+            $_cache_attrs = null;
+        }
+
+        foreach ($attrs as $arg_name => $arg_value) {
+            if (is_bool($arg_value))
+                $arg_value = $arg_value ? 'true' : 'false';
+            if (is_null($arg_value))
+                $arg_value = 'null';
+            if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
+                $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
+            } else {
+                $arg_list[] = "'$arg_name' => $arg_value";
+            }
+        }
+        return $arg_list;
+    }
+
+    /**
+     * Parse is expression
+     *
+     * @param string $is_arg
+     * @param array $tokens
+     * @return array
+     */
+    function _parse_is_expr($is_arg, $tokens)
+    {
+        $expr_end = 0;
+        $negate_expr = false;
+
+        if (($first_token = array_shift($tokens)) == 'not') {
+            $negate_expr = true;
+            $expr_type = array_shift($tokens);
+        } else
+            $expr_type = $first_token;
+
+        switch ($expr_type) {
+            case 'even':
+                if (@$tokens[$expr_end] == 'by') {
+                    $expr_end++;
+                    $expr_arg = $tokens[$expr_end++];
+                    $expr = "!(($is_arg / $expr_arg) % " . $this->_parse_var_props($expr_arg) . ")";
+                } else
+                    $expr = "!($is_arg % 2)";
+                break;
+
+            case 'odd':
+                if (@$tokens[$expr_end] == 'by') {
+                    $expr_end++;
+                    $expr_arg = $tokens[$expr_end++];
+                    $expr = "(($is_arg / $expr_arg) % ". $this->_parse_var_props($expr_arg) . ")";
+                } else
+                    $expr = "($is_arg % 2)";
+                break;
+
+            case 'div':
+                if (@$tokens[$expr_end] == 'by') {
+                    $expr_end++;
+                    $expr_arg = $tokens[$expr_end++];
+                    $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
+                } else {
+                    $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
+                }
+                break;
+
+            default:
+                $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
+                break;
+        }
+
+        if ($negate_expr) {
+            $expr = "!($expr)";
+        }
+
+        array_splice($tokens, 0, $expr_end, $expr);
+
+        return $tokens;
+    }
+
+
+    /**
+     * Parse attribute string
+     *
+     * @param string $tag_args
+     * @return array
+     */
+    function _parse_attrs($tag_args)
+    {
+
+        /* Tokenize tag attributes. */
+        preg_match_all('/(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
+                         )+ |
+                         [=]
+                        /x', $tag_args, $match);
+        $tokens       = $match[0];
+
+        $attrs = array();
+        /* Parse state:
+            0 - expecting attribute name
+            1 - expecting '='
+            2 - expecting attribute value (not '=') */
+        $state = 0;
+
+        foreach ($tokens as $token) {
+            switch ($state) {
+                case 0:
+                    /* If the token is a valid identifier, we set attribute name
+                       and go to state 1. */
+                    if (preg_match('!^\w+$!', $token)) {
+                        $attr_name = $token;
+                        $state = 1;
+                    } else
+                        $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
+                    break;
+
+                case 1:
+                    /* If the token is '=', then we go to state 2. */
+                    if ($token == '=') {
+                        $state = 2;
+                    } else
+                        $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
+                    break;
+
+                case 2:
+                    /* If token is not '=', we set the attribute value and go to
+                       state 0. */
+                    if ($token != '=') {
+                        /* We booleanize the token if it's a non-quoted possible
+                           boolean value. */
+                        if (preg_match('!^(on|yes|true)$!', $token)) {
+                            $token = 'true';
+                        } else if (preg_match('!^(off|no|false)$!', $token)) {
+                            $token = 'false';
+                        } else if ($token == 'null') {
+                            $token = 'null';
+                        } else if (preg_match('!^-?([0-9]+|0[xX][0-9a-fA-F]+)$!', $token)) {
+                            /* treat integer literally */
+                        } else if (!preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$!', $token)) {
+                            /* treat as a string, double-quote it escaping quotes */
+                            $token = '"'.addslashes($token).'"';
+                        }
+
+                        $attrs[$attr_name] = $token;
+                        $state = 0;
+                    } else
+                        $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
+                    break;
+            }
+            $last_token = $token;
+        }
+
+        if($state != 0) {
+            if($state == 1) {
+                $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
+            } else {
+                $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
+            }
+        }
+
+        $this->_parse_vars_props($attrs);
+
+        return $attrs;
+    }
+
+    /**
+     * compile multiple variables and section properties tokens into
+     * PHP code
+     *
+     * @param array $tokens
+     */
+    function _parse_vars_props(&$tokens)
+    {
+        foreach($tokens as $key => $val) {
+            $tokens[$key] = $this->_parse_var_props($val);
+        }
+    }
+
+    /**
+     * compile single variable and section properties token into
+     * PHP code
+     *
+     * @param string $val
+     * @param string $tag_attrs
+     * @return string
+     */
+    function _parse_var_props($val)
+    {
+        $val = trim($val);
+
+        if(preg_match('!^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$!', $val, $match)) {
+                // $ variable or object
+                $return = $this->_parse_var($match[1]);
+                if($match[2] != '') {
+                    $this->_parse_modifiers($return, $match[2]);
+                }
+                return $return;
+            }
+        elseif(preg_match('!^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
+                // double quoted text
+                preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
+                $return = $this->_expand_quoted_text($match[1]);
+                if($match[2] != '') {
+                    $this->_parse_modifiers($return, $match[2]);
+                }
+                return $return;
+            }
+        elseif(preg_match('!^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
+                // single quoted text
+                preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
+                if($match[2] != '') {
+                    $this->_parse_modifiers($match[1], $match[2]);
+                    return $match[1];
+                }
+            }
+        elseif(preg_match('!^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
+                // config var
+                return $this->_parse_conf_var($val);
+            }
+        elseif(preg_match('!^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
+                // section var
+                return $this->_parse_section_prop($val);
+            }
+        elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
+            // literal string
+            return $this->_expand_quoted_text('"' . $val .'"');
+        }
+        return $val;
+    }
+
+    /**
+     * expand quoted text with embedded variables
+     *
+     * @param string $var_expr
+     * @return string
+     */
+    function _expand_quoted_text($var_expr)
+    {
+        // if contains unescaped $, expand it
+        if(preg_match_all('%(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)%', $var_expr, $_match)) {
+            $_match = $_match[0];
+            rsort($_match);
+            reset($_match);
+            foreach($_match as $_var) {
+                $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr);
+            }
+            $_return = preg_replace('%\.""|(?<!\\\\)""\.%', '', $var_expr);
+        } else {
+            $_return = $var_expr;
+        }
+        // replace double quoted literal string with single quotes
+        $_return = preg_replace('!^"([\s\w]+)"$!',"'\\1'",$_return);
+        return $_return;
+    }
+
+    /**
+     * parse variable expression into PHP code
+     *
+     * @param string $var_expr
+     * @param string $output
+     * @return string
+     */
+    function _parse_var($var_expr)
+    {
+        $_has_math = false;
+        $_math_vars = preg_split('!('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')!', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+        if(count($_math_vars) > 1) {
+            $_first_var = "";
+            $_complete_var = "";
+            // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
+            foreach($_math_vars as $_k => $_math_var) {
+                $_math_var = $_math_vars[$_k];
+
+                if(!empty($_math_var) || is_numeric($_math_var)) {
+                    // hit a math operator, so process the stuff which came before it
+                    if(preg_match('!^' . $this->_dvar_math_regexp . '$!', $_math_var)) {
+                        $_has_math = true;
+                        if(!empty($_complete_var) || is_numeric($_complete_var)) {
+                            $_output .= $this->_parse_var($_complete_var);
+                        }
+
+                        // just output the math operator to php
+                        $_output .= $_math_var;
+
+                        if(empty($_first_var))
+                            $_first_var = $_complete_var;
+
+                        $_complete_var = "";
+                    } else {
+                        // fetch multiple -> (like $foo->bar->baz ) which wouldn't get fetched else, because it would only get $foo->bar and treat the ->baz as "-" ">baz" then
+                        for($_i = $_k + 1; $_i <= count($_math_vars); $_i += 2) {
+                            // fetch -> because it gets splitted at - and move it back together
+                            if( /* prevent notice */ (isset($_math_vars[$_i]) && isset($_math_vars[$_i+1])) && ($_math_vars[$_i] === '-' && $_math_vars[$_i+1]{0} === '>')) {
+                                $_math_var .= $_math_vars[$_i].$_math_vars[$_i+1];
+                                $_math_vars[$_i] = $_math_vars[$_i+1] = '';
+                            } else {
+                                break;
+                            }
+                        }
+                        $_complete_var .= $_math_var;
+                    }
+                }
+            }
+            if($_has_math) {
+                if(!empty($_complete_var) || is_numeric($_complete_var))
+                    $_output .= $this->_parse_var($_complete_var, true);
+
+                // get the modifiers working (only the last var from math + modifier is left)
+                $var_expr = $_complete_var;
+            }
+        }
+
+        // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
+        if(is_numeric($var_expr{0}))
+            $_var_ref = $var_expr;
+        else
+            $_var_ref = substr($var_expr, 1);
+
+        if(!$_has_math) {
+            // get [foo] and .foo and ->foo and (...) pieces
+            preg_match_all('!(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+!', $_var_ref, $match);
+
+            $_indexes = $match[0];
+            $_var_name = array_shift($_indexes);
+
+            /* Handle $smarty.* variable references as a special case. */
+            if ($_var_name == 'smarty') {
+                /*
+                 * If the reference could be compiled, use the compiled output;
+                 * otherwise, fall back on the $smarty variable generated at
+                 * run-time.
+                 */
+                if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
+                    $_output = $smarty_ref;
+                } else {
+                    $_var_name = substr(array_shift($_indexes), 1);
+                    $_output = "\$this->_smarty_vars['$_var_name']";
+                }
+            } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
+                // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
+                if(count($_indexes) > 0)
+                {
+                    $_var_name .= implode("", $_indexes);
+                    $_indexes = array();
+                }
+                $_output = $_var_name;
+            } else {
+                $_output = "\$this->_tpl_vars['$_var_name']";
+            }
+
+            foreach ($_indexes as $_index) {
+                if ($_index{0} == '[') {
+                    $_index = substr($_index, 1, -1);
+                    if (is_numeric($_index)) {
+                        $_output .= "[$_index]";
+                    } elseif ($_index{0} == '$') {
+                        if (strpos($_index, '.') !== false) {
+                            $_output .= '[' . $this->_parse_var($_index) . ']';
+                        } else {
+                            $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
+                        }
+                    } else {
+                        $_var_parts = explode('.', $_index);
+                        $_var_section = $_var_parts[0];
+                        $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
+                        $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
+                    }
+                } else if ($_index{0} == '.') {
+                    if ($_index{1} == '$')
+                        $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
+                    else
+                        $_output .= "['" . substr($_index, 1) . "']";
+                } else if (substr($_index,0,2) == '->') {
+                    if(substr($_index,2,2) == '__') {
+                        $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+                    } elseif($this->security && substr($_index, 2, 1) == '_') {
+                        $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+                    } elseif ($_index{2} == '$') {
+                        if ($this->security) {
+                            $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+                        } else {
+                            $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
+                        }
+                    } else {
+                        $_output .= $_index;
+                    }
+                } elseif ($_index{0} == '(') {
+                    $_index = $this->_parse_parenth_args($_index);
+                    $_output .= $_index;
+                } else {
+                    $_output .= $_index;
+                }
+            }
+        }
+
+        return $_output;
+    }
+
+    /**
+     * parse arguments in function call parenthesis
+     *
+     * @param string $parenth_args
+     * @return string
+     */
+    function _parse_parenth_args($parenth_args)
+    {
+        preg_match_all('!' . $this->_param_regexp . '!',$parenth_args, $match);
+        $match = $match[0];
+        rsort($match);
+        reset($match);
+        $orig_vals = $match;
+        $this->_parse_vars_props($match);
+        return str_replace($orig_vals, $match, $parenth_args);
+    }
+
+    /**
+     * parse configuration variable expression into PHP code
+     *
+     * @param string $conf_var_expr
+     */
+    function _parse_conf_var($conf_var_expr)
+    {
+        $parts = explode('|', $conf_var_expr, 2);
+        $var_ref = $parts[0];
+        $modifiers = isset($parts[1]) ? $parts[1] : '';
+
+        $var_name = substr($var_ref, 1, -1);
+
+        $output = "\$this->_config[0]['vars']['$var_name']";
+
+        $this->_parse_modifiers($output, $modifiers);
+
+        return $output;
+    }
+
+    /**
+     * parse section property expression into PHP code
+     *
+     * @param string $section_prop_expr
+     * @return string
+     */
+    function _parse_section_prop($section_prop_expr)
+    {
+        $parts = explode('|', $section_prop_expr, 2);
+        $var_ref = $parts[0];
+        $modifiers = isset($parts[1]) ? $parts[1] : '';
+
+        preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
+        $section_name = $match[1];
+        $prop_name = $match[2];
+
+        $output = "\$this->_sections['$section_name']['$prop_name']";
+
+        $this->_parse_modifiers($output, $modifiers);
+
+        return $output;
+    }
+
+
+    /**
+     * parse modifier chain into PHP code
+     *
+     * sets $output to parsed modified chain
+     * @param string $output
+     * @param string $modifier_string
+     */
+    function _parse_modifiers(&$output, $modifier_string)
+    {
+        preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifier_string, $_match);
+        list(, $_modifiers, $modifier_arg_strings) = $_match;
+
+        for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
+            $_modifier_name = $_modifiers[$_i];
+
+            if($_modifier_name == 'smarty') {
+                // skip smarty modifier
+                continue;
+            }
+
+            preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $modifier_arg_strings[$_i], $_match);
+            $_modifier_args = $_match[1];
+
+            if ($_modifier_name{0} == '@') {
+                $_map_array = false;
+                $_modifier_name = substr($_modifier_name, 1);
+            } else {
+                $_map_array = true;
+            }
+
+            $this->_add_plugin('modifier', $_modifier_name);
+            if (empty($this->_plugins['modifier'][$_modifier_name])
+                && !$this->_get_plugin_filepath('modifier', $_modifier_name)
+                && function_exists($_modifier_name)) {
+                if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
+                    $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+                } else {
+                    $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name,  null, null, false);
+                }
+            }
+
+            $this->_parse_vars_props($_modifier_args);
+
+            if($_modifier_name == 'default') {
+                // supress notifications of default modifier vars and args
+                if($output{0} == '$') {
+                    $output = '@' . $output;
+                }
+                if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
+                    $_modifier_args[0] = '@' . $_modifier_args[0];
+                }
+            }
+            if (count($_modifier_args) > 0)
+                $_modifier_args = ', '.implode(', ', $_modifier_args);
+            else
+                $_modifier_args = '';
+
+            if ($_map_array) {
+                $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
+
+            } else {
+
+                $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
+
+            }
+        }
+    }
+
+
+    /**
+     * add plugin
+     *
+     * @param string $type
+     * @param string $name
+     * @param boolean? $delayed_loading
+     */
+    function _add_plugin($type, $name, $delayed_loading = null)
+    {
+        if (!isset($this->_plugin_info[$type])) {
+            $this->_plugin_info[$type] = array();
+        }
+        if (!isset($this->_plugin_info[$type][$name])) {
+            $this->_plugin_info[$type][$name] = array($this->_current_file,
+                                                      $this->_current_line_no,
+                                                      $delayed_loading);
+        }
+    }
+
+
+    /**
+     * Compiles references of type $smarty.foo
+     *
+     * @param string $indexes
+     * @return string
+     */
+    function _compile_smarty_ref(&$indexes)
+    {
+        /* Extract the reference name. */
+        $_ref = substr($indexes[0], 1);
+        foreach($indexes as $_index_no=>$_index) {
+            if ($_index{0} != '.' && $_index_no<2 || !preg_match('!^(\.|\[|->)!', $_index)) {
+                $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
+            }
+        }
+
+        switch ($_ref) {
+            case 'now':
+                $compiled_ref = 'time()';
+                $_max_index = 1;
+                break;
+
+            case 'foreach':
+            case 'section':
+                array_shift($indexes);
+                $_var = $this->_parse_var_props(substr($indexes[0], 1));
+                if ($_ref == 'foreach')
+                    $compiled_ref = "\$this->_foreach[$_var]";
+                else
+                    $compiled_ref = "\$this->_sections[$_var]";
+                break;
+
+            case 'get':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
+                break;
+
+            case 'post':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
+                break;
+
+            case 'cookies':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
+                break;
+
+            case 'env':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
+                break;
+
+            case 'server':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
+                break;
+
+            case 'session':
+                $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
+                break;
+
+            /*
+             * These cases are handled either at run-time or elsewhere in the
+             * compiler.
+             */
+            case 'request':
+                if ($this->request_use_auto_globals) {
+                    $compiled_ref = '$_REQUEST';
+                    break;
+                } else {
+                    $this->_init_smarty_vars = true;
+                }
+                return null;
+
+            case 'capture':
+                return null;
+
+            case 'template':
+                $compiled_ref = "'$this->_current_file'";
+                $_max_index = 1;
+                break;
+
+            case 'version':
+                $compiled_ref = "'$this->_version'";
+                $_max_index = 1;
+                break;
+
+            case 'const':
+                array_shift($indexes);
+                $_val = $this->_parse_var_props(substr($indexes[0],1));
+                $compiled_ref = '@constant(' . $_val . ')';
+                $_max_index = 1;
+                break;
+
+            case 'config':
+                $compiled_ref = "\$this->_config[0]['vars']";
+                $_max_index = 2;
+                break;
+
+            default:
+                $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
+                break;
+        }
+
+        if (isset($_max_index) && count($indexes) > $_max_index) {
+            $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        array_shift($indexes);
+        return $compiled_ref;
+    }
+
+    /**
+     * compiles call to plugin of type $type with name $name
+     * returns a string containing the function-name or method call
+     * without the paramter-list that would have follow to make the
+     * call valid php-syntax
+     *
+     * @param string $type
+     * @param string $name
+     * @return string
+     */
+    function _compile_plugin_call($type, $name) {
+        if (isset($this->_plugins[$type][$name])) {
+            /* plugin loaded */
+            if (is_array($this->_plugins[$type][$name][0])) {
+                return ((is_object($this->_plugins[$type][$name][0][0])) ?
+                        "\$this->_plugins['$type']['$name'][0][0]->"    /* method callback */
+                        : (string)($this->_plugins[$type][$name][0][0]).'::'    /* class callback */
+                       ). $this->_plugins[$type][$name][0][1];
+
+            } else {
+                /* function callback */
+                return $this->_plugins[$type][$name][0];
+
+            }
+        } else {
+            /* plugin not loaded -> auto-loadable-plugin */
+            return 'smarty_'.$type.'_'.$name;
+
+        }
+    }
+
+    /**
+     * load pre- and post-filters
+     */
+    function _load_filters()
+    {
+        if (count($this->_plugins['prefilter']) > 0) {
+            foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
+                if ($prefilter === false) {
+                    unset($this->_plugins['prefilter'][$filter_name]);
+                    $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
+                    smarty_core_load_plugins($_params, $this);
+                }
+            }
+        }
+        if (count($this->_plugins['postfilter']) > 0) {
+            foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
+                if ($postfilter === false) {
+                    unset($this->_plugins['postfilter'][$filter_name]);
+                    $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
+                    smarty_core_load_plugins($_params, $this);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Quote subpattern references
+     *
+     * @param string $string
+     * @return string
+     */
+    function _quote_replace($string)
+    {
+        return preg_replace('![\\$]\d!', '\\\\\\0', $string);
+    }
+
+    /**
+     * display Smarty syntax error
+     *
+     * @param string $error_msg
+     * @param integer $error_type
+     * @param string $file
+     * @param integer $line
+     */
+    function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
+    {
+        if(isset($file) && isset($line)) {
+            $info = ' ('.basename($file).", line $line)";
+        } else {
+            $info = null;
+        }
+        trigger_error('Smarty: [in ' . $this->_current_file . ' line ' .
+                      $this->_current_line_no . "]: syntax error: $error_msg$info", $error_type);
+    }
+
+
+    /**
+     * check if the compilation changes from cacheable to
+     * non-cacheable state with the beginning of the current
+     * plugin. return php-code to reflect the transition.
+     * @return string
+     */
+    function _push_cacheable_state($type, $name) {
+        $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
+        if ($_cacheable
+            || 0<$this->_cacheable_state++) return '';
+        if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
+        $_ret = 'if ($this->caching) { echo \'{nocache:'
+            . $this->_cache_serial . '#' . $this->_nocache_count
+            . '}\';}';
+        return $_ret;
+    }
+
+
+    /**
+     * check if the compilation changes from non-cacheable to
+     * cacheable state with the end of the current plugin return
+     * php-code to reflect the transition.
+     * @return string
+     */
+    function _pop_cacheable_state($type, $name) {
+        $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
+        if ($_cacheable
+            || --$this->_cacheable_state>0) return '';
+        return 'if ($this->caching) { echo \'{/nocache:'
+            . $this->_cache_serial . '#' . ($this->_nocache_count++)
+            . '}\';}';
+    }
+
+}
+
+/**
+ * compare to values by their string length
+ *
+ * @access private
+ * @param string $a
+ * @param string $b
+ * @return 0|-1|1
+ */
+function _smarty_sort_length($a, $b)
+{
+    if($a == $b)
+        return 0;
+
+    if(strlen($a) == strlen($b))
+        return ($a > $b) ? -1 : 1;
+
+    return (strlen($a) > strlen($b)) ? -1 : 1;
+}
+
+
+/* vim: set et: */
+
+?>
diff --git a/smarty/core/core.assemble_plugin_filepath.php b/smarty/core/core.assemble_plugin_filepath.php
new file mode 100644 (file)
index 0000000..ec44f8e
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * assemble filepath of requested plugin
+ *
+ * @param string $type
+ * @param string $name
+ * @return string|false
+ */
+function smarty_core_assemble_plugin_filepath($params, &$smarty)
+{
+
+    $_plugin_filename = $params['type'] . '.' . $params['name'] . '.php';
+    $_return = false;
+
+    foreach ((array)$smarty->plugins_dir as $_plugin_dir) {
+
+        $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename;
+
+        // see if path is relative
+        if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $_plugin_dir)) {
+            $_relative_paths[] = $_plugin_dir;
+            // relative path, see if it is in the SMARTY_DIR
+            if (@is_readable(SMARTY_DIR . $_plugin_filepath)) {
+                $_return = SMARTY_DIR . $_plugin_filepath;
+                break;
+            }
+        }
+        // try relative to cwd (or absolute)
+        if (@is_readable($_plugin_filepath)) {
+            $_return = $_plugin_filepath;
+            break;
+        }
+    }
+
+    if($_return === false) {
+        // still not found, try PHP include_path
+        if(isset($_relative_paths)) {
+            foreach ((array)$_relative_paths as $_plugin_dir) {
+
+                $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename;
+
+                $_params = array('file_path' => $_plugin_filepath);
+                require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_include_path.php');
+                if(smarty_core_get_include_path($_params, $smarty)) {
+                    return $_params['new_file_path'];
+                }
+            }
+        }
+    }
+
+    return $_return;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.assign_smarty_interface.php b/smarty/core/core.assign_smarty_interface.php
new file mode 100644 (file)
index 0000000..7e65a73
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty assign_smarty_interface core plugin
+ *
+ * Type:     core<br>
+ * Name:     assign_smarty_interface<br>
+ * Purpose:  assign the $smarty interface variable
+ * @param array Format: null
+ * @param Smarty
+ */
+function smarty_core_assign_smarty_interface($params, &$smarty)
+{
+        if (isset($smarty->_smarty_vars) && isset($smarty->_smarty_vars['request'])) {
+            return;
+        }
+
+        $_globals_map = array('g'  => 'HTTP_GET_VARS',
+                             'p'  => 'HTTP_POST_VARS',
+                             'c'  => 'HTTP_COOKIE_VARS',
+                             's'  => 'HTTP_SERVER_VARS',
+                             'e'  => 'HTTP_ENV_VARS');
+
+        $_smarty_vars_request  = array();
+
+        foreach (preg_split('!!', strtolower($smarty->request_vars_order)) as $_c) {
+            if (isset($_globals_map[$_c])) {
+                $_smarty_vars_request = array_merge($_smarty_vars_request, $GLOBALS[$_globals_map[$_c]]);
+            }
+        }
+        $_smarty_vars_request = @array_merge($_smarty_vars_request, $GLOBALS['HTTP_SESSION_VARS']);
+
+        $smarty->_smarty_vars['request'] = $_smarty_vars_request;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.create_dir_structure.php b/smarty/core/core.create_dir_structure.php
new file mode 100644 (file)
index 0000000..999cf59
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * create full directory structure
+ *
+ * @param string $dir
+ */
+
+// $dir
+
+function smarty_core_create_dir_structure($params, &$smarty)
+{
+    if (!file_exists($params['dir'])) {
+        $_open_basedir_ini = ini_get('open_basedir');
+
+        if (DIRECTORY_SEPARATOR=='/') {
+            /* unix-style paths */
+            $_dir = $params['dir'];
+            $_dir_parts = preg_split('!/+!', $_dir, -1, PREG_SPLIT_NO_EMPTY);
+            $_new_dir = ($_dir{0}=='/') ? '/' : getcwd().'/';
+            if($_use_open_basedir = !empty($_open_basedir_ini)) {
+                $_open_basedirs = explode(':', $_open_basedir_ini);
+            }
+
+        } else {
+            /* other-style paths */
+            $_dir = str_replace('\\','/', $params['dir']);
+            $_dir_parts = preg_split('!/+!', $_dir, -1, PREG_SPLIT_NO_EMPTY);
+            if (preg_match('!^((//)|([a-zA-Z]:/))!', $_dir, $_root_dir)) {
+                /* leading "//" for network volume, or "[letter]:/" for full path */
+                $_new_dir = $_root_dir[1];
+                /* remove drive-letter from _dir_parts */
+                if (isset($_root_dir[3])) array_shift($_dir_parts);
+
+            } else {
+                $_new_dir = str_replace('\\', '/', getcwd()).'/';
+
+            }
+
+            if($_use_open_basedir = !empty($_open_basedir_ini)) {
+                $_open_basedirs = explode(';', str_replace('\\', '/', $_open_basedir_ini));
+            }
+
+        }
+
+        /* all paths use "/" only from here */
+        foreach ($_dir_parts as $_dir_part) {
+            $_new_dir .= $_dir_part;
+
+            if ($_use_open_basedir) {
+                // do not attempt to test or make directories outside of open_basedir
+                $_make_new_dir = false;
+                foreach ($_open_basedirs as $_open_basedir) {
+                    if (substr($_new_dir, 0, strlen($_open_basedir)) == $_open_basedir) {
+                        $_make_new_dir = true;
+                        break;
+                    }
+                }
+            } else {
+                $_make_new_dir = true;
+            }
+
+            if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, $smarty->_dir_perms) && !is_dir($_new_dir)) {
+                $smarty->trigger_error("problem creating directory '" . $_new_dir . "'");
+                return false;
+            }
+            $_new_dir .= '/';
+        }
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.display_debug_console.php b/smarty/core/core.display_debug_console.php
new file mode 100644 (file)
index 0000000..c509ff7
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty debug_console function plugin
+ *
+ * Type:     core<br>
+ * Name:     display_debug_console<br>
+ * Purpose:  display the javascript debug console window
+ * @param array Format: null
+ * @param Smarty
+ */
+function smarty_core_display_debug_console($params, &$smarty)
+{
+    // we must force compile the debug template in case the environment
+    // changed between separate applications.
+
+    if(empty($smarty->debug_tpl)) {
+        // set path to debug template from SMARTY_DIR
+        $smarty->debug_tpl = SMARTY_DIR . 'debug.tpl';
+        if($smarty->security && is_file($smarty->debug_tpl)) {
+            $smarty->secure_dir[] = dirname(realpath($smarty->debug_tpl));
+        }
+    }
+
+    $_ldelim_orig = $smarty->left_delimiter;
+    $_rdelim_orig = $smarty->right_delimiter;
+
+    $smarty->left_delimiter = '{';
+    $smarty->right_delimiter = '}';
+
+    $_compile_id_orig = $smarty->_compile_id;
+    $smarty->_compile_id = null;
+
+    $_compile_path = $smarty->_get_compile_path($smarty->debug_tpl);
+    if ($smarty->_compile_resource($smarty->debug_tpl, $_compile_path))
+    {
+        ob_start();
+        $smarty->_include($_compile_path);
+        $_results = ob_get_contents();
+        ob_end_clean();
+    } else {
+        $_results = '';
+    }
+
+    $smarty->_compile_id = $_compile_id_orig;
+
+    $smarty->left_delimiter = $_ldelim_orig;
+    $smarty->right_delimiter = $_rdelim_orig;
+
+    return $_results;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.get_include_path.php b/smarty/core/core.get_include_path.php
new file mode 100644 (file)
index 0000000..eb7188c
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Get path to file from include_path
+ *
+ * @param string $file_path
+ * @param string $new_file_path
+ * @return boolean
+ * @staticvar array|null
+ */
+
+//  $file_path, &$new_file_path
+
+function smarty_core_get_include_path(&$params, &$smarty)
+{
+    static $_path_array = null;
+
+    if(!isset($_path_array)) {
+        $_ini_include_path = ini_get('include_path');
+
+        if(strstr($_ini_include_path,';')) {
+            // windows pathnames
+            $_path_array = explode(';',$_ini_include_path);
+        } else {
+            $_path_array = explode(':',$_ini_include_path);
+        }
+    }
+    foreach ($_path_array as $_include_path) {
+        if (file_exists($_include_path . DIRECTORY_SEPARATOR . $params['file_path'])) {
+               $params['new_file_path'] = $_include_path . DIRECTORY_SEPARATOR . $params['file_path'];
+            return true;
+        }
+    }
+    return false;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.get_microtime.php b/smarty/core/core.get_microtime.php
new file mode 100644 (file)
index 0000000..f1a28e0
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Get seconds and microseconds
+ * @return double
+ */
+function smarty_core_get_microtime($params, &$smarty)
+{
+    $mtime = microtime();
+    $mtime = explode(" ", $mtime);
+    $mtime = (double)($mtime[1]) + (double)($mtime[0]);
+    return ($mtime);
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.get_php_resource.php b/smarty/core/core.get_php_resource.php
new file mode 100644 (file)
index 0000000..8121acf
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Retrieves PHP script resource
+ *
+ * sets $php_resource to the returned resource
+ * @param string $resource
+ * @param string $resource_type
+ * @param  $php_resource
+ * @return boolean
+ */
+
+function smarty_core_get_php_resource(&$params, &$smarty)
+{
+
+    $params['resource_base_path'] = $smarty->trusted_dir;
+    $smarty->_parse_resource_name($params, $smarty);
+
+    /*
+     * Find out if the resource exists.
+     */
+
+    if ($params['resource_type'] == 'file') {
+        $_readable = false;
+        if(file_exists($params['resource_name']) && is_readable($params['resource_name'])) {
+            $_readable = true;
+        } else {
+            // test for file in include_path
+            $_params = array('file_path' => $params['resource_name']);
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_include_path.php');
+            if(smarty_core_get_include_path($_params, $smarty)) {
+                $_include_path = $_params['new_file_path'];
+                $_readable = true;
+            }
+        }
+    } else if ($params['resource_type'] != 'file') {
+        $_template_source = null;
+        $_readable = is_callable($smarty->_plugins['resource'][$params['resource_type']][0][0])
+            && call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][0],
+                                    array($params['resource_name'], &$_template_source, &$smarty));
+    }
+
+    /*
+     * Set the error function, depending on which class calls us.
+     */
+    if (method_exists($smarty, '_syntax_error')) {
+        $_error_funcc = '_syntax_error';
+    } else {
+        $_error_funcc = 'trigger_error';
+    }
+
+    if ($_readable) {
+        if ($smarty->security) {
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.is_trusted.php');
+            if (!smarty_core_is_trusted($params, $smarty)) {
+                $smarty->$_error_funcc('(secure mode) ' . $params['resource_type'] . ':' . $params['resource_name'] . ' is not trusted');
+                return false;
+            }
+        }
+    } else {
+        $smarty->$_error_funcc($params['resource_type'] . ':' . $params['resource_name'] . ' is not readable');
+        return false;
+    }
+
+    if ($params['resource_type'] == 'file') {
+        $params['php_resource'] = $params['resource_name'];
+    } else {
+        $params['php_resource'] = $_template_source;
+    }
+    return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.is_secure.php b/smarty/core/core.is_secure.php
new file mode 100644 (file)
index 0000000..877886b
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * determines if a resource is secure or not.
+ *
+ * @param string $resource_type
+ * @param string $resource_name
+ * @return boolean
+ */
+
+//  $resource_type, $resource_name
+
+function smarty_core_is_secure($params, &$smarty)
+{
+    static $check_template_dir = true;
+
+    if (!$smarty->security || $smarty->security_settings['INCLUDE_ANY']) {
+        return true;
+    }
+
+    $_smarty_secure = false;
+    if ($params['resource_type'] == 'file') {
+        if($check_template_dir) {
+            if (!in_array($smarty->template_dir, $smarty->secure_dir))
+                // add template_dir to secure_dir array
+                array_unshift($smarty->secure_dir, $smarty->template_dir);
+            $check_template_dir = false;
+        }
+        if (!empty($smarty->secure_dir)) {
+            $_rp = realpath($params['resource_name']);
+            foreach ((array)$smarty->secure_dir as $curr_dir) {
+                if ( !empty($curr_dir) && is_readable ($curr_dir)) {
+                    $_cd = realpath($curr_dir);
+                    if (strncmp($_rp, $_cd, strlen($_cd)) == 0
+                        && $_rp{strlen($_cd)} == DIRECTORY_SEPARATOR ) {
+                        $_smarty_secure = true;
+                        break;
+                    }
+                }
+            }
+        }
+    } else {
+        // resource is not on local file system
+        $_smarty_secure = call_user_func_array(
+            $smarty->_plugins['resource'][$params['resource_type']][0][2],
+            array($params['resource_name'], &$_smarty_secure, &$smarty));
+    }
+
+    return $_smarty_secure;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.is_trusted.php b/smarty/core/core.is_trusted.php
new file mode 100644 (file)
index 0000000..c90e3ef
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**#@+
+ * @access private
+ */
+/**
+ * determines if a resource is trusted or not
+ *
+ * @param string $resource_type
+ * @param string $resource_name
+ * @return boolean
+ */
+
+ // $resource_type, $resource_name
+
+function smarty_core_is_trusted($params, &$smarty)
+{
+    $_smarty_trusted = false;
+    if ($params['resource_type'] == 'file') {
+        if (!empty($smarty->trusted_dir)) {
+            $_rp = realpath($params['resource_name']);
+            foreach ((array)$smarty->trusted_dir as $curr_dir) {
+                if (!empty($curr_dir) && is_readable ($curr_dir)) {
+                    $_cd = realpath($curr_dir);
+                    if (strncmp($_rp, $_cd, strlen($_cd)) == 0
+                        && $_rp{strlen($_cd)} == DIRECTORY_SEPARATOR ) {
+                        $_smarty_trusted = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+    } else {
+        // resource is not on local file system
+        $_smarty_trusted = call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][3],
+                                                array($params['resource_name'], $smarty));
+    }
+
+    return $_smarty_trusted;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.load_plugins.php b/smarty/core/core.load_plugins.php
new file mode 100644 (file)
index 0000000..6db1dc5
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Load requested plugins
+ *
+ * @param array $plugins
+ */
+
+// $plugins
+
+function smarty_core_load_plugins($params, &$smarty)
+{
+
+    foreach ($params['plugins'] as $_plugin_info) {
+        list($_type, $_name, $_tpl_file, $_tpl_line, $_delayed_loading) = $_plugin_info;
+        $_plugin = &$smarty->_plugins[$_type][$_name];
+
+        /*
+         * We do not load plugin more than once for each instance of Smarty.
+         * The following code checks for that. The plugin can also be
+         * registered dynamically at runtime, in which case template file
+         * and line number will be unknown, so we fill them in.
+         *
+         * The final element of the info array is a flag that indicates
+         * whether the dynamically registered plugin function has been
+         * checked for existence yet or not.
+         */
+        if (isset($_plugin)) {
+            if (empty($_plugin[3])) {
+                if (!is_callable($_plugin[0])) {
+                    $smarty->_trigger_fatal_error("[plugin] $_type '$_name' is not implemented", $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+                } else {
+                    $_plugin[1] = $_tpl_file;
+                    $_plugin[2] = $_tpl_line;
+                    $_plugin[3] = true;
+                    if (!isset($_plugin[4])) $_plugin[4] = true; /* cacheable */
+                }
+            }
+            continue;
+        } else if ($_type == 'insert') {
+            /*
+             * For backwards compatibility, we check for insert functions in
+             * the symbol table before trying to load them as a plugin.
+             */
+            $_plugin_func = 'insert_' . $_name;
+            if (function_exists($_plugin_func)) {
+                $_plugin = array($_plugin_func, $_tpl_file, $_tpl_line, true, false);
+                continue;
+            }
+        }
+
+        $_plugin_file = $smarty->_get_plugin_filepath($_type, $_name);
+
+        if (! $_found = ($_plugin_file != false)) {
+            $_message = "could not load plugin file '$_type.$_name.php'\n";
+        }
+
+        /*
+         * If plugin file is found, it -must- provide the properly named
+         * plugin function. In case it doesn't, simply output the error and
+         * do not fall back on any other method.
+         */
+        if ($_found) {
+            include_once $_plugin_file;
+
+            $_plugin_func = 'smarty_' . $_type . '_' . $_name;
+            if (!function_exists($_plugin_func)) {
+                $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+                continue;
+            }
+        }
+        /*
+         * In case of insert plugins, their code may be loaded later via
+         * 'script' attribute.
+         */
+        else if ($_type == 'insert' && $_delayed_loading) {
+            $_plugin_func = 'smarty_' . $_type . '_' . $_name;
+            $_found = true;
+        }
+
+        /*
+         * Plugin specific processing and error checking.
+         */
+        if (!$_found) {
+            if ($_type == 'modifier') {
+                /*
+                 * In case modifier falls back on using PHP functions
+                 * directly, we only allow those specified in the security
+                 * context.
+                 */
+                if ($smarty->security && !in_array($_name, $smarty->security_settings['MODIFIER_FUNCS'])) {
+                    $_message = "(secure mode) modifier '$_name' is not allowed";
+                } else {
+                    if (!function_exists($_name)) {
+                        $_message = "modifier '$_name' is not implemented";
+                    } else {
+                        $_plugin_func = $_name;
+                        $_found = true;
+                    }
+                }
+            } else if ($_type == 'function') {
+                /*
+                 * This is a catch-all situation.
+                 */
+                $_message = "unknown tag - '$_name'";
+            }
+        }
+
+        if ($_found) {
+            $smarty->_plugins[$_type][$_name] = array($_plugin_func, $_tpl_file, $_tpl_line, true, true);
+        } else {
+            // output error
+            $smarty->_trigger_fatal_error('[plugin] ' . $_message, $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+        }
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.load_resource_plugin.php b/smarty/core/core.load_resource_plugin.php
new file mode 100644 (file)
index 0000000..a7d37d1
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * load a resource plugin
+ *
+ * @param string $type
+ */
+
+// $type
+
+function smarty_core_load_resource_plugin($params, &$smarty)
+{
+    /*
+     * Resource plugins are not quite like the other ones, so they are
+     * handled differently. The first element of plugin info is the array of
+     * functions provided by the plugin, the second one indicates whether
+     * all of them exist or not.
+     */
+
+    $_plugin = &$smarty->_plugins['resource'][$params['type']];
+    if (isset($_plugin)) {
+        if (!$_plugin[1] && count($_plugin[0])) {
+            $_plugin[1] = true;
+            foreach ($_plugin[0] as $_plugin_func) {
+                if (!is_callable($_plugin_func)) {
+                    $_plugin[1] = false;
+                    break;
+                }
+            }
+        }
+
+        if (!$_plugin[1]) {
+            $smarty->_trigger_fatal_error("[plugin] resource '" . $params['type'] . "' is not implemented", null, null, __FILE__, __LINE__);
+        }
+
+        return;
+    }
+
+    $_plugin_file = $smarty->_get_plugin_filepath('resource', $params['type']);
+    $_found = ($_plugin_file != false);
+
+    if ($_found) {            /*
+         * If the plugin file is found, it -must- provide the properly named
+         * plugin functions.
+         */
+        include_once($_plugin_file);
+
+        /*
+         * Locate functions that we require the plugin to provide.
+         */
+        $_resource_ops = array('source', 'timestamp', 'secure', 'trusted');
+        $_resource_funcs = array();
+        foreach ($_resource_ops as $_op) {
+            $_plugin_func = 'smarty_resource_' . $params['type'] . '_' . $_op;
+            if (!function_exists($_plugin_func)) {
+                $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", null, null, __FILE__, __LINE__);
+                return;
+            } else {
+                $_resource_funcs[] = $_plugin_func;
+            }
+        }
+
+        $smarty->_plugins['resource'][$params['type']] = array($_resource_funcs, true);
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.process_cached_inserts.php b/smarty/core/core.process_cached_inserts.php
new file mode 100644 (file)
index 0000000..0e368fd
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Replace cached inserts with the actual results
+ *
+ * @param string $results
+ * @return string
+ */
+function smarty_core_process_cached_inserts($params, &$smarty)
+{
+    preg_match_all('!'.$smarty->_smarty_md5.'{insert_cache (.*)}'.$smarty->_smarty_md5.'!Uis',
+                   $params['results'], $match);
+    list($cached_inserts, $insert_args) = $match;
+
+    for ($i = 0, $for_max = count($cached_inserts); $i < $for_max; $i++) {
+        if ($smarty->debugging) {
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $debug_start_time = smarty_core_get_microtime($_params, $smarty);
+        }
+
+        $args = unserialize($insert_args[$i]);
+        $name = $args['name'];
+
+        if (isset($args['script'])) {
+            $_params = array('resource_name' => $smarty->_dequote($args['script']));
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_php_resource.php');
+            if(!smarty_core_get_php_resource($_params, $smarty)) {
+                return false;
+            }
+            $resource_type = $_params['resource_type'];
+            $php_resource = $_params['php_resource'];
+
+
+            if ($resource_type == 'file') {
+                $smarty->_include($php_resource, true);
+            } else {
+                $smarty->_eval($php_resource);
+            }
+        }
+
+        $function_name = $smarty->_plugins['insert'][$name][0];
+        if (empty($args['assign'])) {
+            $replace = $function_name($args, $smarty);
+        } else {
+            $smarty->assign($args['assign'], $function_name($args, $smarty));
+            $replace = '';
+        }
+
+        $params['results'] = str_replace($cached_inserts[$i], $replace, $params['results']);
+        if ($smarty->debugging) {
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $smarty->_smarty_debug_info[] = array('type'      => 'insert',
+                                                'filename'  => 'insert_'.$name,
+                                                'depth'     => $smarty->_inclusion_depth,
+                                                'exec_time' => smarty_core_get_microtime($_params, $smarty) - $debug_start_time);
+        }
+    }
+
+    return $params['results'];
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.process_compiled_include.php b/smarty/core/core.process_compiled_include.php
new file mode 100644 (file)
index 0000000..3e1d4c1
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Replace nocache-tags by results of the corresponding non-cacheable
+ * functions and return it
+ *
+ * @param string $compiled_tpl
+ * @param string $cached_source
+ * @return string
+ */
+
+function smarty_core_process_compiled_include($params, &$smarty)
+{
+    $_cache_including = $smarty->_cache_including;
+    $smarty->_cache_including = true;
+
+    $_return = $params['results'];
+    foreach ($smarty->_cache_serials as $_include_file_path=>$_cache_serial) {
+        $_return = preg_replace_callback('!(\{nocache\:('.$_cache_serial.')#(\d+)\})!s',
+                                         array(&$smarty, '_process_compiled_include_callback'),
+                                         $_return);
+    }
+    $smarty->_cache_including = $_cache_including;
+    return $_return;
+}
+
+?>
diff --git a/smarty/core/core.read_cache_file.php b/smarty/core/core.read_cache_file.php
new file mode 100644 (file)
index 0000000..2ab4281
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * read a cache file, determine if it needs to be
+ * regenerated or not
+ *
+ * @param string $tpl_file
+ * @param string $cache_id
+ * @param string $compile_id
+ * @param string $results
+ * @return boolean
+ */
+
+//  $tpl_file, $cache_id, $compile_id, &$results
+
+function smarty_core_read_cache_file(&$params, &$smarty)
+{
+    static  $content_cache = array();
+
+    if ($smarty->force_compile) {
+        // force compile enabled, always regenerate
+        return false;
+    }
+
+    if (isset($content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']])) {
+        list($params['results'], $smarty->_cache_info) = $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']];
+        return true;
+    }
+
+    if (!empty($smarty->cache_handler_func)) {
+        // use cache_handler function
+        call_user_func_array($smarty->cache_handler_func,
+                             array('read', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
+    } else {
+        // use local cache file
+        $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+        $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+        $params['results'] = $smarty->_read_file($_cache_file);
+    }
+
+    if (empty($params['results'])) {
+        // nothing to parse (error?), regenerate cache
+        return false;
+    }
+
+    $cache_split = explode("\n", $params['results'], 2);
+    $cache_header = $cache_split[0];
+
+    $_cache_info = unserialize($cache_header);
+
+    if ($smarty->caching == 2 && isset ($_cache_info['expires'])){
+        // caching by expiration time
+        if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) {
+            // cache expired, regenerate
+            return false;
+        }
+    } else {
+        // caching by lifetime
+        if ($smarty->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $smarty->cache_lifetime)) {
+            // cache expired, regenerate
+            return false;
+        }
+    }
+
+    if ($smarty->compile_check) {
+        $_params = array('get_source' => false, 'quiet'=>true);
+        foreach (array_keys($_cache_info['template']) as $_template_dep) {
+            $_params['resource_name'] = $_template_dep;
+            if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+                // template file has changed, regenerate cache
+                return false;
+            }
+        }
+
+        if (isset($_cache_info['config'])) {
+            $_params = array('resource_base_path' => $smarty->config_dir, 'get_source' => false, 'quiet'=>true);
+            foreach (array_keys($_cache_info['config']) as $_config_dep) {
+                $_params['resource_name'] = $_config_dep;
+                if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+                    // config file has changed, regenerate cache
+                    return false;
+                }
+            }
+        }
+    }
+
+    foreach ($_cache_info['cache_serials'] as $_include_file_path=>$_cache_serial) {
+        if (empty($smarty->_cache_serials[$_include_file_path])) {
+            $smarty->_include($_include_file_path, true);
+        }
+
+        if ($smarty->_cache_serials[$_include_file_path] != $_cache_serial) {
+            /* regenerate */
+            return false;
+        }
+    }
+    $params['results'] = $cache_split[1];
+    $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']] = array($params['results'], $_cache_info);
+
+    $smarty->_cache_info = $_cache_info;
+    return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.rm_auto.php b/smarty/core/core.rm_auto.php
new file mode 100644 (file)
index 0000000..b7cdaf8
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * delete an automagically created file by name and id
+ *
+ * @param string $auto_base
+ * @param string $auto_source
+ * @param string $auto_id
+ * @param integer $exp_time
+ * @return boolean
+ */
+
+// $auto_base, $auto_source = null, $auto_id = null, $exp_time = null
+
+function smarty_core_rm_auto($params, &$smarty)
+{
+    if (!@is_dir($params['auto_base']))
+      return false;
+
+    if(!isset($params['auto_id']) && !isset($params['auto_source'])) {
+        $_params = array(
+            'dirname' => $params['auto_base'],
+            'level' => 0,
+            'exp_time' => $params['exp_time']
+        );
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rmdir.php');
+        $_res = smarty_core_rmdir($_params, $smarty);
+    } else {
+        $_tname = $smarty->_get_auto_filename($params['auto_base'], $params['auto_source'], $params['auto_id']);
+
+        if(isset($params['auto_source'])) {
+            if (isset($params['extensions'])) {
+                $_res = false;
+                foreach ((array)$params['extensions'] as $_extension)
+                    $_res |= $smarty->_unlink($_tname.$_extension, $params['exp_time']);
+            } else {
+                $_res = $smarty->_unlink($_tname, $params['exp_time']);
+            }
+        } elseif ($smarty->use_sub_dirs) {
+            $_params = array(
+                'dirname' => $_tname,
+                'level' => 1,
+                'exp_time' => $params['exp_time']
+            );
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rmdir.php');
+            $_res = smarty_core_rmdir($_params, $smarty);
+        } else {
+            // remove matching file names
+            $_handle = opendir($params['auto_base']);
+            $_res = true;
+            while (false !== ($_filename = readdir($_handle))) {
+                if($_filename == '.' || $_filename == '..') {
+                    continue;
+                } elseif (substr($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, 0, strlen($_tname)) == $_tname) {
+                    $_res &= (bool)$smarty->_unlink($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, $params['exp_time']);
+                }
+            }
+        }
+    }
+
+    return $_res;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.rmdir.php b/smarty/core/core.rmdir.php
new file mode 100644 (file)
index 0000000..38df822
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * delete a dir recursively (level=0 -> keep root)
+ * WARNING: no tests, it will try to remove what you tell it!
+ *
+ * @param string $dirname
+ * @param integer $level
+ * @param integer $exp_time
+ * @return boolean
+ */
+
+//  $dirname, $level = 1, $exp_time = null
+
+function smarty_core_rmdir($params, &$smarty)
+{
+   if(!isset($params['level'])) { $params['level'] = 1; }
+   if(!isset($params['exp_time'])) { $params['exp_time'] = null; }
+
+   if($_handle = @opendir($params['dirname'])) {
+
+        while (false !== ($_entry = readdir($_handle))) {
+            if ($_entry != '.' && $_entry != '..') {
+                if (@is_dir($params['dirname'] . DIRECTORY_SEPARATOR . $_entry)) {
+                    $_params = array(
+                        'dirname' => $params['dirname'] . DIRECTORY_SEPARATOR . $_entry,
+                        'level' => $params['level'] + 1,
+                        'exp_time' => $params['exp_time']
+                    );
+                    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.rmdir.php');
+                    smarty_core_rmdir($_params, $smarty);
+                }
+                else {
+                    $smarty->_unlink($params['dirname'] . DIRECTORY_SEPARATOR . $_entry, $params['exp_time']);
+                }
+            }
+        }
+        closedir($_handle);
+   }
+
+   if ($params['level']) {
+       return @rmdir($params['dirname']);
+   }
+   return (bool)$_handle;
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.run_insert_handler.php b/smarty/core/core.run_insert_handler.php
new file mode 100644 (file)
index 0000000..aa391ab
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Handle insert tags
+ *
+ * @param array $args
+ * @return string
+ */
+function smarty_core_run_insert_handler($params, &$smarty)
+{
+
+    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+    if ($smarty->debugging) {
+        $_params = array();
+        $_debug_start_time = smarty_core_get_microtime($_params, $smarty);
+    }
+
+    if ($smarty->caching) {
+        $_arg_string = serialize($params['args']);
+        $_name = $params['args']['name'];
+        if (!isset($smarty->_cache_info['insert_tags'][$_name])) {
+            $smarty->_cache_info['insert_tags'][$_name] = array('insert',
+                                                             $_name,
+                                                             $smarty->_plugins['insert'][$_name][1],
+                                                             $smarty->_plugins['insert'][$_name][2],
+                                                             !empty($params['args']['script']) ? true : false);
+        }
+        return $smarty->_smarty_md5."{insert_cache $_arg_string}".$smarty->_smarty_md5;
+    } else {
+        if (isset($params['args']['script'])) {
+            $_params = array('resource_name' => $smarty->_dequote($params['args']['script']));
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_php_resource.php');
+            if(!smarty_core_get_php_resource($_params, $smarty)) {
+                return false;
+            }
+
+            if ($_params['resource_type'] == 'file') {
+                $smarty->_include($_params['php_resource'], true);
+            } else {
+                $smarty->_eval($_params['php_resource']);
+            }
+            unset($params['args']['script']);
+        }
+
+        $_funcname = $smarty->_plugins['insert'][$params['args']['name']][0];
+        $_content = $_funcname($params['args'], $smarty);
+        if ($smarty->debugging) {
+            $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $smarty->_smarty_debug_info[] = array('type'      => 'insert',
+                                                'filename'  => 'insert_'.$params['args']['name'],
+                                                'depth'     => $smarty->_inclusion_depth,
+                                                'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time);
+        }
+
+        if (!empty($params['args']["assign"])) {
+            $smarty->assign($params['args']["assign"], $_content);
+        } else {
+            return $_content;
+        }
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.smarty_include_php.php b/smarty/core/core.smarty_include_php.php
new file mode 100644 (file)
index 0000000..4b31648
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * called for included php files within templates
+ *
+ * @param string $smarty_file
+ * @param string $smarty_assign variable to assign the included template's
+ *               output into
+ * @param boolean $smarty_once uses include_once if this is true
+ * @param array $smarty_include_vars associative array of vars from
+ *              {include file="blah" var=$var}
+ */
+
+//  $file, $assign, $once, $_smarty_include_vars
+
+function smarty_core_smarty_include_php($params, &$smarty)
+{
+    $_params = array('resource_name' => $params['smarty_file']);
+    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_php_resource.php');
+    smarty_core_get_php_resource($_params, $smarty);
+    $_smarty_resource_type = $_params['resource_type'];
+    $_smarty_php_resource = $_params['php_resource'];
+
+    if (!empty($params['smarty_assign'])) {
+        ob_start();
+        if ($_smarty_resource_type == 'file') {
+            $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']);
+        } else {
+            $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']);
+        }
+        $smarty->assign($params['smarty_assign'], ob_get_contents());
+        ob_end_clean();
+    } else {
+        if ($_smarty_resource_type == 'file') {
+            $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']);
+        } else {
+            $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']);
+        }
+    }
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.write_cache_file.php b/smarty/core/core.write_cache_file.php
new file mode 100644 (file)
index 0000000..7429ad9
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Prepend the cache information to the cache file
+ * and write it
+ *
+ * @param string $tpl_file
+ * @param string $cache_id
+ * @param string $compile_id
+ * @param string $results
+ * @return true|null
+ */
+
+ // $tpl_file, $cache_id, $compile_id, $results
+
+function smarty_core_write_cache_file($params, &$smarty)
+{
+
+    // put timestamp in cache header
+    $smarty->_cache_info['timestamp'] = time();
+    if ($smarty->cache_lifetime > -1){
+        // expiration set
+        $smarty->_cache_info['expires'] = $smarty->_cache_info['timestamp'] + $smarty->cache_lifetime;
+    } else {
+        // cache will never expire
+        $smarty->_cache_info['expires'] = -1;
+    }
+
+    // collapse {nocache...}-tags
+    $params['results'] = preg_replace('!((\{nocache\:([0-9a-f]{32})#(\d+)\})'
+                                      .'.*'
+                                      .'{/nocache\:\\3#\\4\})!Us'
+                                      ,'\\2'
+                                      ,$params['results']);
+    $smarty->_cache_info['cache_serials'] = $smarty->_cache_serials;
+
+    // prepend the cache header info into cache file
+    $params['results'] = serialize($smarty->_cache_info)."\n".$params['results'];
+
+    if (!empty($smarty->cache_handler_func)) {
+        // use cache_handler function
+        call_user_func_array($smarty->cache_handler_func,
+                             array('write', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
+    } else {
+        // use local cache file
+
+        if(!@is_writable($smarty->cache_dir)) {
+            // cache_dir not writable, see if it exists
+            if(!@is_dir($smarty->cache_dir)) {
+                $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
+                return false;
+            }
+            $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR);
+            return false;
+        }
+
+        $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+        $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+        $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true);
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_file.php');
+        smarty_core_write_file($_params, $smarty);
+        return true;
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.write_compiled_include.php b/smarty/core/core.write_compiled_include.php
new file mode 100644 (file)
index 0000000..9c6a491
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Extract non-cacheable parts out of compiled template and write it
+ *
+ * @param string $compile_path
+ * @param string $template_compiled
+ * @param integer $template_timestamp
+ * @return boolean
+ */
+
+function smarty_core_write_compiled_include($params, &$smarty)
+{
+    $_tag_start = 'if \(\$this->caching\) \{ echo \'\{nocache\:('.$params['cache_serial'].')#(\d+)\}\';\}';
+    $_tag_end   = 'if \(\$this->caching\) \{ echo \'\{/nocache\:(\\2)#(\\3)\}\';\}';
+
+    preg_match_all('!('.$_tag_start.'(.*)'.$_tag_end.')!Us',
+                   $params['compiled_content'], $_match_source, PREG_SET_ORDER);
+
+    // no nocache-parts found: done
+    if (count($_match_source)==0) return;
+
+    // convert the matched php-code to functions
+    $_include_compiled = "<?php /* funky header here */\n\n";
+
+    $_compile_path = $params['include_file_path'];
+
+    $smarty->_cache_serials[$_compile_path] = $params['cache_serial'];
+    $_include_compiled .= "\$this->_cache_serials['".$_compile_path."'] = '".$params['cache_serial']."';\n\n?>";
+
+    $_include_compiled .= $params['plugins_code'];
+    $_include_compiled .= "<?php";
+    for ($_i = 0, $_for_max = count($_match_source); $_i < $_for_max; $_i++) {
+        $_match =& $_match_source[$_i];
+        $_include_compiled .= "
+function _smarty_tplfunc_$_match[2]_$_match[3](&\$this)
+{
+$_match[4]
+}
+
+";
+    }
+    $_include_compiled .= "\n\n?>\n";
+
+    $_params = array('filename' => $_compile_path,
+                     'contents' => $_include_compiled, 'create_dirs' => true);
+
+    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_file.php');
+    smarty_core_write_file($_params, $smarty);
+    return true;
+}
+
+
+?>
diff --git a/smarty/core/core.write_compiled_resource.php b/smarty/core/core.write_compiled_resource.php
new file mode 100644 (file)
index 0000000..09b50d3
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * write the compiled resource
+ *
+ * @param string $compile_path
+ * @param string $compiled_content
+ * @param integer $resource_timestamp
+ * @return true
+ */
+function smarty_core_write_compiled_resource($params, &$smarty)
+{
+    if(!@is_writable($smarty->compile_dir)) {
+        // compile_dir not writable, see if it exists
+        if(!@is_dir($smarty->compile_dir)) {
+            $smarty->trigger_error('the $compile_dir \'' . $smarty->compile_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
+            return false;
+        }
+        $smarty->trigger_error('unable to write to $compile_dir \'' . realpath($smarty->compile_dir) . '\'. Be sure $compile_dir is writable by the web server user.', E_USER_ERROR);
+        return false;
+    }
+
+    $_params = array('filename' => $params['compile_path'], 'contents' => $params['compiled_content'], 'create_dirs' => true);
+    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_file.php');
+    smarty_core_write_file($_params, $smarty);
+    touch($params['compile_path'], $params['resource_timestamp']);
+    return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/core/core.write_file.php b/smarty/core/core.write_file.php
new file mode 100644 (file)
index 0000000..c92454d
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * write out a file to disk
+ *
+ * @param string $filename
+ * @param string $contents
+ * @param boolean $create_dirs
+ * @return boolean
+ */
+function smarty_core_write_file($params, &$smarty)
+{
+    $_dirname = dirname($params['filename']);
+
+    if ($params['create_dirs']) {
+        $_params = array('dir' => $_dirname);
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.create_dir_structure.php');
+        smarty_core_create_dir_structure($_params, $smarty);
+    }
+
+    // write to tmp file, then rename it to avoid
+    // file locking race condition
+    $_tmp_file = $_dirname . DIRECTORY_SEPARATOR . uniqid('');
+
+    if (!($fd = @fopen($_tmp_file, 'w'))) {
+        $smarty->trigger_error("problem writing temporary file '$_tmp_file'");
+        return false;
+    }
+
+    fwrite($fd, $params['contents']);
+    fclose($fd);
+    if(file_exists($params['filename'])) {
+        @unlink($params['filename']);
+    }
+    @rename($_tmp_file, $params['filename']);
+    @chmod($params['filename'], $smarty->_file_perms);
+
+    return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/debug.tpl b/smarty/debug.tpl
new file mode 100644 (file)
index 0000000..01265fb
--- /dev/null
@@ -0,0 +1,64 @@
+{* Smarty *}
+
+{* debug.tpl, last updated version 2.0.1 *}
+
+{assign_debug_info}
+
+{if isset($_smarty_debug_output) and $_smarty_debug_output eq "html"}
+       <table border=0 width=100%>
+       <tr bgcolor=#cccccc><th colspan=2>Smarty Debug Console</th></tr>
+       <tr bgcolor=#cccccc><td colspan=2><b>included templates & config files (load time in seconds):</b></td></tr>
+       {section name=templates loop=$_debug_tpls}
+               <tr bgcolor={if %templates.index% is even}#eeeeee{else}#fafafa{/if}><td colspan=2><tt>{section name=indent loop=$_debug_tpls[templates].depth}&nbsp;&nbsp;&nbsp;{/section}<font color={if $_debug_tpls[templates].type eq "template"}brown{elseif $_debug_tpls[templates].type eq "insert"}black{else}green{/if}>{$_debug_tpls[templates].filename|escape:html}</font>{if isset($_debug_tpls[templates].exec_time)} <font size=-1><i>({$_debug_tpls[templates].exec_time|string_format:"%.5f"}){if %templates.index% eq 0} (total){/if}</i></font>{/if}</tt></td></tr>
+       {sectionelse}
+               <tr bgcolor=#eeeeee><td colspan=2><tt><i>no templates included</i></tt></td></tr>       
+       {/section}
+       <tr bgcolor=#cccccc><td colspan=2><b>assigned template variables:</b></td></tr>
+       {section name=vars loop=$_debug_keys}
+               <tr bgcolor={if %vars.index% is even}#eeeeee{else}#fafafa{/if}><td valign=top><tt><font color=blue>{ldelim}${$_debug_keys[vars]}{rdelim}</font></tt></td><td nowrap><tt><font color=green>{$_debug_vals[vars]|@debug_print_var}</font></tt></td></tr>
+       {sectionelse}
+               <tr bgcolor=#eeeeee><td colspan=2><tt><i>no template variables assigned</i></tt></td></tr>      
+       {/section}
+       <tr bgcolor=#cccccc><td colspan=2><b>assigned config file variables (outer template scope):</b></td></tr>
+       {section name=config_vars loop=$_debug_config_keys}
+               <tr bgcolor={if %config_vars.index% is even}#eeeeee{else}#fafafa{/if}><td valign=top><tt><font color=maroon>{ldelim}#{$_debug_config_keys[config_vars]}#{rdelim}</font></tt></td><td><tt><font color=green>{$_debug_config_vals[config_vars]|@debug_print_var}</font></tt></td></tr>
+       {sectionelse}
+               <tr bgcolor=#eeeeee><td colspan=2><tt><i>no config vars assigned</i></tt></td></tr>     
+       {/section}
+       </table>
+</BODY></HTML>
+{else}
+<SCRIPT language=javascript>
+       if( self.name == '' ) {ldelim}
+          var title = 'Console';
+       {rdelim}
+       else {ldelim}
+          var title = 'Console_' + self.name;
+       {rdelim}
+       _smarty_console = window.open("",title.value,"width=680,height=600,resizable,scrollbars=yes");
+       _smarty_console.document.write("<HTML><TITLE>Smarty Debug Console_"+self.name+"</TITLE><BODY bgcolor=#ffffff>");
+       _smarty_console.document.write("<table border=0 width=100%>");
+       _smarty_console.document.write("<tr bgcolor=#cccccc><th colspan=2>Smarty Debug Console</th></tr>");
+       _smarty_console.document.write("<tr bgcolor=#cccccc><td colspan=2><b>included templates & config files (load time in seconds):</b></td></tr>");
+       {section name=templates loop=$_debug_tpls}
+               _smarty_console.document.write("<tr bgcolor={if %templates.index% is even}#eeeeee{else}#fafafa{/if}><td colspan=2><tt>{section name=indent loop=$_debug_tpls[templates].depth}&nbsp;&nbsp;&nbsp;{/section}<font color={if $_debug_tpls[templates].type eq "template"}brown{elseif $_debug_tpls[templates].type eq "insert"}black{else}green{/if}>{$_debug_tpls[templates].filename|escape:html|escape:javascript}</font>{if isset($_debug_tpls[templates].exec_time)} <font size=-1><i>({$_debug_tpls[templates].exec_time|string_format:"%.5f"}){if %templates.index% eq 0} (total){/if}</i></font>{/if}</tt></td></tr>");
+       {sectionelse}
+               _smarty_console.document.write("<tr bgcolor=#eeeeee><td colspan=2><tt><i>no templates included</i></tt></td></tr>");    
+       {/section}
+       _smarty_console.document.write("<tr bgcolor=#cccccc><td colspan=2><b>assigned template variables:</b></td></tr>");
+       {section name=vars loop=$_debug_keys}
+               _smarty_console.document.write("<tr bgcolor={if %vars.index% is even}#eeeeee{else}#fafafa{/if}><td valign=top><tt><font color=blue>{ldelim}${$_debug_keys[vars]}{rdelim}</font></tt></td><td nowrap><tt><font color=green>{$_debug_vals[vars]|@debug_print_var|escape:javascript}</font></tt></td></tr>");
+       {sectionelse}
+               _smarty_console.document.write("<tr bgcolor=#eeeeee><td colspan=2><tt><i>no template variables assigned</i></tt></td></tr>");   
+       {/section}
+       _smarty_console.document.write("<tr bgcolor=#cccccc><td colspan=2><b>assigned config file variables (outer template scope):</b></td></tr>");
+       {section name=config_vars loop=$_debug_config_keys}
+               _smarty_console.document.write("<tr bgcolor={if %config_vars.index% is even}#eeeeee{else}#fafafa{/if}><td valign=top><tt><font color=maroon>{ldelim}#{$_debug_config_keys[config_vars]}#{rdelim}</font></tt></td><td><tt><font color=green>{$_debug_config_vals[config_vars]|@debug_print_var|escape:javascript}</font></tt></td></tr>");
+       {sectionelse}
+               _smarty_console.document.write("<tr bgcolor=#eeeeee><td colspan=2><tt><i>no config vars assigned</i></tt></td></tr>");  
+       {/section}
+       _smarty_console.document.write("</table>");
+       _smarty_console.document.write("</BODY></HTML>");
+       _smarty_console.document.close();
+</SCRIPT>
+{/if}
diff --git a/smarty/plugins/block.textformat.php b/smarty/plugins/block.textformat.php
new file mode 100644 (file)
index 0000000..7ddccc7
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {textformat}{/textformat} block plugin
+ *
+ * Type:     block function<br>
+ * Name:     textformat<br>
+ * Purpose:  format text a certain way with preset styles
+ *           or custom wrap/indent settings<br>
+ * @link http://smarty.php.net/manual/en/language.function.textformat.php {textformat}
+ *       (Smarty online manual)
+ * @param array
+ * <pre>
+ * Params:   style: string (email)
+ *           indent: integer (0)
+ *           wrap: integer (80)
+ *           wrap_char string ("\n")
+ *           indent_char: string (" ")
+ *           wrap_boundary: boolean (true)
+ * </pre>
+ * @param string contents of the block
+ * @param Smarty clever simulation of a method
+ * @return string string $content re-formatted
+ */
+function smarty_block_textformat($params, $content, &$smarty)
+{
+       $style = null;
+       $indent = 0;
+       $indent_first = 0;
+       $indent_char = ' ';
+       $wrap = 80;
+       $wrap_char = "\n";
+       $wrap_cut = false;
+       $assign = null;
+       
+       if($content == null) {
+               return true;
+       }
+
+    extract($params);
+
+       if($style == 'email') {
+               $wrap = 72;
+       }       
+       
+       // split into paragraphs        
+       $paragraphs = preg_split('![\r\n][\r\n]!',$content);
+       $output = '';
+
+       foreach($paragraphs as $paragraph) {
+               if($paragraph == '') {
+                       continue;
+               }
+               // convert mult. spaces & special chars to single space
+               $paragraph = preg_replace(array('!\s+!','!(^\s+)|(\s+$)!'),array(' ',''),$paragraph);
+               // indent first line
+               if($indent_first > 0) {
+                       $paragraph = str_repeat($indent_char,$indent_first) . $paragraph;
+               }
+               // wordwrap sentences
+               $paragraph = wordwrap($paragraph, $wrap - $indent, $wrap_char, $wrap_cut);
+               // indent lines
+               if($indent > 0) {
+                       $paragraph = preg_replace('!^!m',str_repeat($indent_char,$indent),$paragraph);
+               }
+               $output .= $paragraph . $wrap_char . $wrap_char;
+       }
+                               
+       if($assign != null) {
+               $smarty->assign($assign,$output);
+       } else {
+               return $output;
+       }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.assign.php b/smarty/plugins/function.assign.php
new file mode 100644 (file)
index 0000000..ad23f04
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {assign} function plugin
+ *
+ * Type:     function<br>
+ * Name:     assign<br>
+ * Purpose:  assign a value to a template variable
+ * @link http://smarty.php.net/manual/en/language.custom.functions.php#LANGUAGE.FUNCTION.ASSIGN {assign}
+ *       (Smarty online manual)
+ * @param array Format: array('var' => variable name, 'value' => value to assign)
+ * @param Smarty
+ */
+function smarty_function_assign($params, &$smarty)
+{
+    extract($params);
+
+    if (empty($var)) {
+        $smarty->trigger_error("assign: missing 'var' parameter");
+        return;
+    }
+
+    if (!in_array('value', array_keys($params))) {
+        $smarty->trigger_error("assign: missing 'value' parameter");
+        return;
+    }
+
+    $smarty->assign($var, $value);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.assign_debug_info.php b/smarty/plugins/function.assign_debug_info.php
new file mode 100644 (file)
index 0000000..c281ce8
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {assign_debug_info} function plugin
+ *
+ * Type:     function<br>
+ * Name:     assign_debug_info<br>
+ * Purpose:  assign debug info to the template<br>
+ * @param array unused in this plugin, this plugin uses {@link Smarty::$_config},
+ *              {@link Smarty::$_tpl_vars} and {@link Smarty::$_smarty_debug_info}
+ * @param Smarty
+ */
+function smarty_function_assign_debug_info($params, &$smarty)
+{
+    $assigned_vars = $smarty->_tpl_vars;
+    ksort($assigned_vars);
+    if (@is_array($smarty->_config[0])) {
+        $config_vars = $smarty->_config[0];
+        ksort($config_vars);
+        $smarty->assign("_debug_config_keys", array_keys($config_vars));
+        $smarty->assign("_debug_config_vals", array_values($config_vars));
+    }   
+    
+    $included_templates = $smarty->_smarty_debug_info;
+    
+    $smarty->assign("_debug_keys", array_keys($assigned_vars));
+    $smarty->assign("_debug_vals", array_values($assigned_vars));
+    
+    $smarty->assign("_debug_tpls", $included_templates);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.config_load.php b/smarty/plugins/function.config_load.php
new file mode 100644 (file)
index 0000000..12b7462
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {config_load} function plugin
+ *
+ * Type:     function<br>
+ * Name:     config_load<br>
+ * Purpose:  load config file vars
+ * @link http://smarty.php.net/manual/en/language.function.config.load.php {config_load}
+ *       (Smarty online manual)
+ * @param array Format:
+ * <pre>
+ * array('file' => required config file name,
+ *       'section' => optional config file section to load
+ *       'scope' => local/parent/global
+ *       'global' => overrides scope, setting to parent if true)
+ * </pre>
+ * @param Smarty
+ */
+function smarty_function_config_load($params, &$smarty)
+{
+        if ($smarty->debugging) {
+                       $_params = array();
+            require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $_debug_start_time = smarty_core_get_microtime($_params, $smarty);
+        }
+
+               $_file = isset($params['file']) ? $smarty->_dequote($params['file']) : null;
+               $_section = isset($params['section']) ? $smarty->_dequote($params['section']) : null;
+               $_scope = isset($params['scope']) ? $smarty->_dequote($params['scope']) : 'global';
+               $_global = isset($params['global']) ? $smarty->_dequote($params['global']) : false;
+
+        if (!isset($_file) || strlen($_file) == 0) {
+            $smarty->_syntax_error("missing 'file' attribute in config_load tag", E_USER_ERROR, __FILE__, __LINE__);
+        }
+
+        if (isset($_scope)) {
+            if ($_scope != 'local' &&
+                $_scope != 'parent' &&
+                $_scope != 'global') {
+                $smarty->_syntax_error("invalid 'scope' attribute value", E_USER_ERROR, __FILE__, __LINE__);
+            }
+        } else {
+            if ($_global) {
+                $_scope = 'parent';
+                       } else {
+                $_scope = 'local';
+                       }
+        }              
+                       
+        if(@is_dir($smarty->config_dir)) {
+            $_config_dir = $smarty->config_dir;            
+        } else {
+            // config_dir not found, try include_path
+                       $_params = array('file_path' => $smarty->config_dir);
+                       require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_include_path.php');
+            smarty_core_get_include_path($_params, $smarty);
+                       $_config_dir = $_params['new_file_path'];
+        }
+
+               $_file_path = $_config_dir . DIRECTORY_SEPARATOR . $_file;
+        if (isset($_section)) 
+            $_compile_file = $smarty->_get_compile_path($_file_path.'|'.$_section);
+        else
+            $_compile_file = $smarty->_get_compile_path($_file_path);
+               
+               if($smarty->force_compile
+                               || !file_exists($_compile_file)
+                               || ($smarty->compile_check
+                                       && !$smarty->_is_compiled($_file_path, $_compile_file))) {
+                       // compile config file
+               if(!is_object($smarty->_conf_obj)) {
+               require_once SMARTY_DIR . $smarty->config_class . '.class.php';
+               $smarty->_conf_obj = new $smarty->config_class($_config_dir);
+               $smarty->_conf_obj->overwrite = $smarty->config_overwrite;
+               $smarty->_conf_obj->booleanize = $smarty->config_booleanize;
+               $smarty->_conf_obj->read_hidden = $smarty->config_read_hidden;
+               $smarty->_conf_obj->fix_newlines = $smarty->config_fix_newlines;
+               $smarty->_conf_obj->set_path = $_config_dir;
+               }
+               $_config_vars = array_merge($smarty->_conf_obj->get($_file),
+                       $smarty->_conf_obj->get($_file, $_section));
+                       if(function_exists('var_export')) {
+                               $_output = '<?php $_config_vars = ' . var_export($_config_vars, true) . '; ?>';
+                       } else {
+                               $_output = '<?php $_config_vars = unserialize(\'' . strtr(serialize($_config_vars),array('\''=>'\\\'', '\\'=>'\\\\')) . '\'); ?>';
+                       }
+                       $_params = (array('compile_path' => $_compile_file, 'compiled_content' => $_output, 'resource_timestamp' => filemtime($_file_path)));
+                       require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.write_compiled_resource.php');
+                       smarty_core_write_compiled_resource($_params, $smarty);
+               } else {
+            include($_compile_file);
+               }
+
+        if ($smarty->caching) {
+            $smarty->_cache_info['config'][$_file] = true;
+        }
+
+        $smarty->_config[0]['vars'] = @array_merge($smarty->_config[0]['vars'], $_config_vars);
+        $smarty->_config[0]['files'][$_file] = true;
+        
+        if ($_scope == 'parent') {
+                $smarty->_config[1]['vars'] = @array_merge($smarty->_config[1]['vars'], $_config_vars);
+                $smarty->_config[1]['files'][$_file] = true;
+        } else if ($_scope == 'global') {
+            for ($i = 1, $for_max = count($smarty->_config); $i < $for_max; $i++) {
+                    $smarty->_config[$i]['vars'] = @array_merge($smarty->_config[$i]['vars'], $_config_vars);
+                    $smarty->_config[$i]['files'][$_file] = true;
+            }
+        }
+               
+        if ($smarty->debugging) {
+                       $_params = array();
+                       require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.get_microtime.php');
+            $smarty->_smarty_debug_info[] = array('type'      => 'config',
+                                                'filename'  => $_file.' ['.$_section.'] '.$_scope,
+                                                'depth'     => $smarty->_inclusion_depth,
+                                                'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time);
+        }
+       
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.counter.php b/smarty/plugins/function.counter.php
new file mode 100644 (file)
index 0000000..2536c14
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {counter} function plugin
+ *
+ * Type:     function<br>
+ * Name:     counter<br>
+ * Purpose:  print out a counter value
+ * @link http://smarty.php.net/manual/en/language.function.counter.php {counter}
+ *       (Smarty online manual)
+ * @param array parameters
+ * @param Smarty
+ * @return string|null
+ */
+function smarty_function_counter($params, &$smarty)
+{
+    static $counters = array();
+
+    extract($params);
+
+    if (!isset($name)) {
+               if(isset($id)) {
+                       $name = $id;
+               } else {                
+               $name = "default";
+               }
+       }
+
+    if (!isset($counters[$name])) {
+        $counters[$name] = array(
+            'start'=>1,
+            'skip'=>1,
+            'direction'=>'up',
+            'count'=>1
+            );
+    }
+    $counter =& $counters[$name];
+
+    if (isset($start)) {
+        $counter['start'] = $counter['count'] = $start;
+    }
+
+    if (!empty($assign)) {
+        $counter['assign'] = $assign;
+    }
+
+    if (isset($counter['assign'])) {
+        $smarty->assign($counter['assign'], $counter['count']);
+    }
+    
+    if (isset($print)) {
+        $print = (bool)$print;
+    } else {
+        $print = empty($counter['assign']);
+    }
+
+    if ($print) {
+        $retval = $counter['count'];
+       } else {
+               $retval = null;
+       }
+
+    if (isset($skip)) {
+        $counter['skip'] = $skip;
+    }
+    
+    if (isset($direction)) {
+        $counter['direction'] = $direction;
+    }
+
+    if ($counter['direction'] == "down")
+        $counter['count'] -= $counter['skip'];
+    else
+        $counter['count'] += $counter['skip'];
+       
+       return $retval;
+       
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.cycle.php b/smarty/plugins/function.cycle.php
new file mode 100644 (file)
index 0000000..d5909a6
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {cycle} function plugin
+ *
+ * Type:     function<br>
+ * Name:     cycle<br>
+ * Date:     May 3, 2002<br>
+ * Purpose:  cycle through given values<br>
+ * Input:
+ *         - name = name of cycle (optional)
+ *         - values = comma separated list of values to cycle,
+ *                    or an array of values to cycle
+ *                    (this can be left out for subsequent calls)
+ *         - reset = boolean - resets given var to true
+ *         - print = boolean - print var or not. default is true
+ *         - advance = boolean - whether or not to advance the cycle
+ *         - delimiter = the value delimiter, default is ","
+ *         - assign = boolean, assigns to template var instead of
+ *                    printed.
+ * 
+ * Examples:<br>
+ * <pre>
+ * {cycle values="#eeeeee,#d0d0d0d"}
+ * {cycle name=row values="one,two,three" reset=true}
+ * {cycle name=row}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.cycle.php {cycle}
+ *       (Smarty online manual)
+ * @author Monte Ohrt <monte@ispi.net>
+ * @author credit to Mark Priatel <mpriatel@rogers.com>
+ * @author credit to Gerard <gerard@interfold.com>
+ * @author credit to Jason Sweat <jsweat_php@yahoo.com>
+ * @version  1.3
+ * @param array
+ * @param Smarty
+ * @return string|null
+ */
+function smarty_function_cycle($params, &$smarty)
+{
+    static $cycle_vars;
+    
+    extract($params);
+
+    if (empty($name)) {
+        $name = 'default';
+    }
+
+    if (!isset($print)) {
+        $print = true;
+    }
+
+    if (!isset($advance)) {
+        $advance = true;        
+    }    
+
+    if (!isset($reset)) {
+        $reset = false;        
+    }        
+            
+    if (!in_array('values', array_keys($params))) {
+        if(!isset($cycle_vars[$name]['values'])) {
+            $smarty->trigger_error("cycle: missing 'values' parameter");
+            return;
+        }
+    } else {
+        if(isset($cycle_vars[$name]['values'])
+            && $cycle_vars[$name]['values'] != $values ) {
+            $cycle_vars[$name]['index'] = 0;
+        }
+        $cycle_vars[$name]['values'] = $values;
+    }
+
+    if (isset($delimiter)) {
+        $cycle_vars[$name]['delimiter'] = $delimiter;
+    } elseif (!isset($cycle_vars[$name]['delimiter'])) {
+        $cycle_vars[$name]['delimiter'] = ',';        
+    }
+    
+    if(!is_array($cycle_vars[$name]['values'])) {
+        $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']);
+    } else {
+        $cycle_array = $cycle_vars[$name]['values'];    
+    }
+    
+    if(!isset($cycle_vars[$name]['index']) || $reset ) {
+        $cycle_vars[$name]['index'] = 0;
+    }
+    
+    if (isset($assign)) {
+        $print = false;
+        $smarty->assign($assign, $cycle_array[$cycle_vars[$name]['index']]);
+    }
+        
+    if($print) {
+        $retval = $cycle_array[$cycle_vars[$name]['index']];
+    } else {
+        $retval = null;
+    }
+
+    if($advance) {
+        if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) {
+            $cycle_vars[$name]['index'] = 0;            
+        } else {
+            $cycle_vars[$name]['index']++;
+        }
+    }
+    
+    return $retval;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.debug.php b/smarty/plugins/function.debug.php
new file mode 100644 (file)
index 0000000..2452d62
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {debug} function plugin
+ *
+ * Type:     function<br>
+ * Name:     debug<br>
+ * Date:     July 1, 2002<br>
+ * Purpose:  popup debug window
+ * @link http://smarty.php.net/manual/en/language.function.debug.php {debug}
+ *       (Smarty online manual)
+ * @author   Monte Ohrt <monte@ispi.net>
+ * @version  1.0
+ * @param array
+ * @param Smarty
+ * @return string output from {@link Smarty::_generate_debug_output()}
+ */
+function smarty_function_debug($params, &$smarty)
+{
+    if($params['output']) {
+        $smarty->assign('_smarty_debug_output',$params['output']);
+    }
+    require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.display_debug_console.php');
+    return smarty_core_display_debug_console(null, $smarty);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.eval.php b/smarty/plugins/function.eval.php
new file mode 100644 (file)
index 0000000..3a4b8b2
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {eval} function plugin
+ *
+ * Type:     function<br>
+ * Name:     eval<br>
+ * Purpose:  evaluate a template variable as a template<br>
+ * @link http://smarty.php.net/manual/en/language.function.eval.php {eval}
+ *       (Smarty online manual)
+ * @param array
+ * @param Smarty
+ */
+function smarty_function_eval($params, &$smarty)
+{
+
+    if (!isset($params['var'])) {
+        $smarty->trigger_error("eval: missing 'var' parameter");
+        return;
+    }
+
+    if($params['var'] == '') {
+        return;
+    }
+
+    $smarty->_compile_source('evaluated template', $params['var'], $_var_compiled);
+
+    ob_start();
+    $smarty->_eval('?>' . $_var_compiled);
+    $_contents = ob_get_contents();
+    ob_end_clean();
+
+    if (!empty($params['assign'])) {
+        $smarty->assign($params['assign'], $_contents);
+    } else {
+        return $_contents;
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.fetch.php b/smarty/plugins/function.fetch.php
new file mode 100644 (file)
index 0000000..b013b8c
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {fetch} plugin
+ *
+ * Type:     function<br>
+ * Name:     fetch<br>
+ * Purpose:  fetch file, web or ftp data and display results
+ * @link http://smarty.php.net/manual/en/language.function.fetch.php {fetch}
+ *       (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string|null if the assign parameter is passed, Smarty assigns the
+ *                     result to a template variable
+ */
+function smarty_function_fetch($params, &$smarty)
+{
+    if (empty($params['file'])) {
+        $smarty->_trigger_fatal_error("[plugin] parameter 'file' cannot be empty");
+        return;
+    }
+
+    if ($smarty->security && !preg_match('!^(http|ftp)://!i', $params['file'])) {
+               $_params = array('resource_type' => 'file', 'resource_name' => $params['file']);
+               require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.is_secure.php');
+               if(!smarty_core_is_secure($_params, $smarty)) {
+            $smarty->_trigger_fatal_error('[plugin] (secure mode) fetch \'' . $params['file'] . '\' is not allowed');
+            return;                    
+               }
+               
+               // fetch the file
+               if($fp = @fopen($params['file'],'r')) {
+                       while(!feof($fp)) {
+                               $content .= fgets ($fp,4096);
+                       }
+                       fclose($fp);
+               } else {
+            $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] . '\'');
+            return;                    
+               }
+    } else {
+               // not a local file
+               if(preg_match('!^http://!i',$params['file'])) {
+                       // http fetch
+                       if($uri_parts = parse_url($params['file'])) {
+                               // set defaults
+                               $host = $server_name = $uri_parts['host'];
+                               $timeout = 30;
+                               $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
+                               $agent = "Smarty Template Engine ".$smarty->_version;
+                               $referer = "";
+                               $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/';
+                               $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : '';
+                               $_is_proxy = false;
+                               if(empty($uri_parts['port'])) {
+                                       $port = 80;
+                               } else {
+                                       $port = $uri_parts['port'];
+                               }
+                               if(empty($uri_parts['user'])) {
+                                       $user = '';
+                               }                               
+                               // loop through parameters, setup headers
+                               foreach($params as $param_key => $param_value) {                        
+                                       switch($param_key) {
+                                               case "file":
+                                               case "assign":
+                                               case "assign_headers":
+                                                       break;
+                                               case "user":
+                                                       if(!empty($param_value)) {
+                                                               $user = $param_value;
+                                                       }
+                                                       break;
+                                               case "pass":
+                                                       if(!empty($param_value)) {
+                                                               $pass = $param_value;
+                                                       }
+                                                       break;
+                                               case "accept":
+                                                       if(!empty($param_value)) {
+                                                               $accept = $param_value;
+                                                       }
+                                                       break;
+                                               case "header":
+                                                       if(!empty($param_value)) {
+                                                               if(!preg_match('![\w\d-]+: .+!',$param_value)) {
+                                                       $smarty->_trigger_fatal_error("[plugin] invalid header format '".$param_value."'");
+                                                       return;                                                                 
+                                                               } else {
+                                                                       $extra_headers[] = $param_value;
+                                                               }
+                                                       }
+                                                       break;
+                                               case "proxy_host":
+                                                       if(!empty($param_value)) {
+                                                               $proxy_host = $param_value;
+                                                       }
+                                                       break;
+                                               case "proxy_port":
+                                                       if(!preg_match('!\D!', $param_value)) {
+                                                               $proxy_port = (int) $param_value;
+                                                       } else {
+                                               $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'");
+                                               return;                                                                 
+                                                       }
+                                                       break;
+                                               case "agent":
+                                                       if(!empty($param_value)) {
+                                                               $agent = $param_value;
+                                                       }
+                                                       break;
+                                               case "referer":
+                                                       if(!empty($param_value)) {
+                                                               $referer = $param_value;
+                                                       }
+                                                       break;
+                                               case "timeout":
+                                                       if(!preg_match('!\D!', $param_value)) {
+                                                               $timeout = (int) $param_value;
+                                                       } else {
+                                               $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'");
+                                               return;                                                                 
+                                                       }
+                                                       break;
+                                               default:
+                                       $smarty->_trigger_fatal_error("[plugin] unrecognized attribute '".$param_key."'");
+                                       return;
+                                       }                       
+                               }
+                               if(!empty($proxy_host) && !empty($proxy_port)) {
+                                       $_is_proxy = true;
+                                       $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout);
+                               } else {
+                                       $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout);
+                               }
+
+                               if(!$fp) {
+                       $smarty->_trigger_fatal_error("[plugin] unable to fetch: $errstr ($errno)");
+                       return;                         
+                               } else {
+                                       if($_is_proxy) {
+                                               fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n");                                         
+                                       } else {
+                                               fputs($fp, "GET $uri HTTP/1.0\r\n");
+                                       }
+                                       if(!empty($host)) {
+                                               fputs($fp, "Host: $host\r\n");
+                                       }
+                                       if(!empty($accept)) {
+                                               fputs($fp, "Accept: $accept\r\n");
+                                       }
+                                       if(!empty($agent)) {
+                                               fputs($fp, "User-Agent: $agent\r\n");
+                                       }
+                                       if(!empty($referer)) {
+                                               fputs($fp, "Referer: $referer\r\n");
+                                       }
+                                       if(isset($extra_headers) && is_array($extra_headers)) {
+                                               foreach($extra_headers as $curr_header) {
+                                                       fputs($fp, $curr_header."\r\n");
+                                               }
+                                       }
+                                       if(!empty($user) && !empty($pass)) {
+                                               fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n");                                                
+                                       }
+
+                                       $content = '';                                  
+                                       fputs($fp, "\r\n");
+                                       while(!feof($fp)) {
+                                               $content .= fgets($fp,4096);
+                                       }
+                                       fclose($fp);                                    
+                                       $csplit = split("\r\n\r\n",$content,2);
+
+                                       $content = $csplit[1];
+                                       
+                                       if(!empty($params['assign_headers'])) {
+                                               $smarty->assign($params['assign_headers'],split("\r\n",$csplit[0]));
+                                       }
+                               }
+                       } else {
+                       $smarty->_trigger_fatal_error("[plugin] unable to parse URL, check syntax");
+                       return;
+                       }
+               } else {
+                       // ftp fetch
+                       if($fp = @fopen($params['file'],'r')) {
+                               while(!feof($fp)) {
+                                       $content .= fgets ($fp,4096);
+                               }
+                               fclose($fp);
+                       } else {
+               $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] .'\'');
+               return;                 
+                       }
+               }
+               
+       }
+
+
+    if (!empty($params['assign'])) {
+        $smarty->assign($params['assign'],$content);
+    } else {
+        return $content;
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.html_checkboxes.php b/smarty/plugins/function.html_checkboxes.php
new file mode 100644 (file)
index 0000000..c146fc1
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_checkboxes} function plugin
+ *
+ * File:       function.html_checkboxes.php<br>
+ * Type:       function<br>
+ * Name:       html_checkboxes<br>
+ * Date:       24.Feb.2003<br>
+ * Purpose:    Prints out a list of checkbox input types<br>
+ * Input:<br>
+ *           - name       (optional) - string default "checkbox"
+ *           - values     (required) - array
+ *           - options    (optional) - associative array
+ *           - checked    (optional) - array default not set
+ *           - separator  (optional) - ie <br> or &nbsp;
+ *           - output     (optional) - without this one the buttons don't have names
+ * Examples:
+ * <pre>
+ * {html_checkboxes values=$ids output=$names}
+ * {html_checkboxes values=$ids name='box' separator='<br>' output=$names}
+ * {html_checkboxes values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.html.checkboxes.php {html_checkboxes}
+ *      (Smarty online manual)
+ * @author     Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author credits to Monte Ohrt <monte@ispi.net>
+ * @version    1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_checkboxes($params, &$smarty)
+{
+   require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+
+   $name = 'checkbox';
+   $values = null;
+   $options = null;
+   $selected = null;
+   $separator = '';
+   $labels = true;
+   $output = null;
+   $extra = '';
+   
+   foreach($params as $_key => $_val) {
+      switch($_key) {
+      case 'name':
+      case 'separator':
+         $$_key = $_val;
+         break;
+
+      case 'labels':
+         $$_key = (bool)$_val;
+         break;
+
+      case 'options':
+         $$_key = (array)$_val;
+         break;
+
+      case 'values':
+      case 'output':
+         $$_key = array_values((array)$_val);
+        break;
+
+      case 'checked':
+      case 'selected':
+         $selected = array_values((array)$_val);
+         break;
+
+      case 'checkboxes':
+         $smarty->trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING);
+         $options = (array)$_val;
+         break;
+
+      default:
+               if(!is_array($_val)) {
+                       $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+               } else {
+                       $smarty->trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+               }
+         break;
+      }
+   }
+
+   if (!isset($options) && !isset($values))
+      return ''; /* raise error here? */
+
+   settype($selected, 'array');
+   $_html_result = '';
+
+   if (is_array($options)) {
+
+      foreach ($options as $_key=>$_val)
+         $_html_result .= smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+
+
+   } else {
+      foreach ($values as $_i=>$_key) {
+         $_val = isset($output[$_i]) ? $output[$_i] : '';
+         $_html_result .= smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+      }
+
+   }
+
+   return $_html_result;
+
+}
+
+function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels) {
+   $_output = '';
+   if ($labels) $_output .= '<label>';
+   $_output .= '<input type="checkbox" name="'
+      . smarty_function_escape_special_chars($name) . '[]" value="'
+      . smarty_function_escape_special_chars($value) . '"';
+
+   if (in_array($value, $selected)) {
+      $_output .= ' checked="checked"';
+   }
+   $_output .= $extra . ' />' . $output;
+   if ($labels) $_output .= '</label>';
+   $_output .=  $separator . "\n";
+
+   return $_output;
+}
+
+?>
diff --git a/smarty/plugins/function.html_image.php b/smarty/plugins/function.html_image.php
new file mode 100644 (file)
index 0000000..2fcdb4e
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_image} function plugin
+ *
+ * Type:     function<br>
+ * Name:     html_image<br>
+ * Date:     Feb 24, 2003<br>
+ * Purpose:  format HTML tags for the image<br>
+ * Input:<br>
+ *         - file = file (and path) of image (required)
+ *         - border = border width (optional, default 0)
+ *         - height = image height (optional, default actual height)
+ *         - image =image width (optional, default actual width)
+ *         - basedir = base directory for absolute paths, default
+ *                     is environment variable DOCUMENT_ROOT
+ *
+ * Examples: {html_image file="images/masthead.gif"}
+ * Output:   <img src="images/masthead.gif" border=0 width=400 height=23>
+ * @link http://smarty.php.net/manual/en/language.function.html.image.php {html_image}
+ *      (Smarty online manual)
+ * @author   Monte Ohrt <monte@ispi.net>
+ * @author credits to Duda <duda@big.hu> - wrote first image function
+ *           in repository, helped with lots of functionality
+ * @version  1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_image($params, &$smarty)
+{
+    require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+    
+    $alt = '';
+    $file = '';
+    $border = 0;
+    $height = '';
+    $width = '';
+    $extra = '';
+    $prefix = '';
+    $suffix = '';
+    $basedir = isset($GLOBALS['HTTP_SERVER_VARS']['DOCUMENT_ROOT'])
+        ? $GLOBALS['HTTP_SERVER_VARS']['DOCUMENT_ROOT'] : '';
+    if(strstr($GLOBALS['HTTP_SERVER_VARS']['HTTP_USER_AGENT'], 'Mac')) {
+        $dpi_default = 72;
+    } else {
+        $dpi_default = 96;
+    }
+
+    foreach($params as $_key => $_val) {
+        switch($_key) {
+            case 'file':
+            case 'border':
+            case 'height':
+            case 'width':
+            case 'dpi':
+            case 'basedir':
+                $$_key = $_val;
+                break;
+
+            case 'alt':
+                if(!is_array($_val)) {
+                    $$_key = smarty_function_escape_special_chars($_val);
+                } else {
+                    $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+
+            case 'link':
+            case 'href':
+                $prefix = '<a href="' . $_val . '">';
+                $suffix = '</a>';
+                break;
+
+            default:
+                if(!is_array($_val)) {
+                    $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+                } else {
+                    $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (empty($file)) {
+        $smarty->trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE);
+        return;
+    }
+
+    if (substr($file,0,1) == '/') {
+        $_image_path = $basedir . $file;
+    } else {
+        $_image_path = $file;
+    }
+
+    if(!isset($params['width']) || !isset($params['height'])) {
+        if(!$_image_data = @getimagesize($_image_path)) {
+            if(!file_exists($_image_path)) {
+                $smarty->trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE);
+                return;
+            } else if(!is_readable($_image_path)) {
+                $smarty->trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE);
+                return;
+            } else {
+                $smarty->trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE);
+                return;
+            }
+        }
+        $_params = array('resource_type' => 'file', 'resource_name' => $_image_path);
+        require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.is_secure.php');
+        if(!$smarty->security && !smarty_core_is_secure($_params, $smarty)) {
+            $smarty->trigger_error("html_image: (secure) '$_image_path' not in secure directory", E_USER_NOTICE);
+            return;
+        }
+
+        if(!isset($params['width'])) {
+            $width = $_image_data[0];
+        }
+        if(!isset($params['height'])) {
+            $height = $_image_data[1];
+        }
+
+    }
+
+    if(isset($params['dpi'])) {
+        $_resize = $dpi_default/$params['dpi'];
+        $width = round($width * $_resize);
+        $height = round($height * $_resize);
+    }
+
+    return $prefix . '<img src="'.$file.'" alt="'.$alt.'" border="'.$border.'" width="'.$width.'" height="'.$height.'"'.$extra.' />' . $suffix;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.html_options.php b/smarty/plugins/function.html_options.php
new file mode 100644 (file)
index 0000000..1b10653
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_options} function plugin
+ *
+ * Type:     function<br>
+ * Name:     html_options<br>
+ * Input:<br>
+ *           - name       (optional) - string default "select"
+ *           - values     (required if no options supplied) - array
+ *           - options    (required if no values supplied) - associative array
+ *           - selected   (optional) - string default not set
+ *           - output     (required if not options supplied) - array
+ * Purpose:  Prints the list of <option> tags generated from
+ *           the passed parameters
+ * @link http://smarty.php.net/manual/en/language.function.html.options.php {html_image}
+ *      (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_options($params, &$smarty)
+{
+   require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+  
+   $name = null;
+   $values = null;
+   $options = null;
+   $selected = array();
+   $output = null;
+
+   $extra = '';
+  
+       foreach($params as $_key => $_val) {    
+               switch($_key) {
+                       case 'name':
+                       $$_key = (string)$_val;
+                       break;
+
+               case 'options':
+                       $$_key = (array)$_val;
+                       break;
+
+               case 'selected':
+               case 'values':
+               case 'output':
+                       $$_key = array_values((array)$_val);      
+                       break;
+
+               default:
+                       if(!is_array($_val)) {
+                               $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+                       } else {
+                               $smarty->trigger_error("html_options: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                       }
+                       break;                                  
+               }
+       }
+
+   if (!isset($options) && !isset($values))
+      return ''; /* raise error here? */
+
+   $_html_result = '';
+
+   if (is_array($options)) {
+
+      foreach ($options as $_key=>$_val)
+        $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected);      
+    
+   } else {  
+
+      foreach ((array)$values as $_i=>$_key) {
+        $_val = isset($output[$_i]) ? $output[$_i] : '';
+        $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected);      
+      }
+
+   }
+
+   if(!empty($name)) {
+      $_html_result = '<select name="' . $name . '"' . $extra . '>' . "\n" . $_html_result . '</select>' . "\n";
+   }
+
+   return $_html_result;
+
+}
+
+function smarty_function_html_options_optoutput($key, $value, $selected) {
+   if(!is_array($value)) {
+      $_html_result = '<option label="' . smarty_function_escape_special_chars($value) . '" value="' . 
+        smarty_function_escape_special_chars($key) . '"';
+      if (in_array($key, $selected))
+        $_html_result .= ' selected="selected"';
+      $_html_result .= '>' . smarty_function_escape_special_chars($value) . '</option>' . "\n";
+   } else {
+      $_html_result = smarty_function_html_options_optgroup($key, $value, $selected);
+   }
+   return $_html_result;       
+}
+
+function smarty_function_html_options_optgroup($key, $values, $selected) {
+   $optgroup_html = '<optgroup label="' . smarty_function_escape_special_chars($key) . '">' . "\n";
+   foreach ($values as $key => $value) {
+      $optgroup_html .= smarty_function_html_options_optoutput($key, $value, $selected);
+   }
+   $optgroup_html .= "</optgroup>\n";
+   return $optgroup_html;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.html_radios.php b/smarty/plugins/function.html_radios.php
new file mode 100644 (file)
index 0000000..b80e9bc
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_radios} function plugin
+ *
+ * File:       function.html_radios.php<br>
+ * Type:       function<br>
+ * Name:       html_radios<br>
+ * Date:       24.Feb.2003<br>
+ * Purpose:    Prints out a list of radio input types<br>
+ * Input:<br>
+ *           - name       (optional) - string default "radio"
+ *           - values     (required) - array
+ *           - options    (optional) - associative array
+ *           - checked    (optional) - array default not set
+ *           - separator  (optional) - ie <br> or &nbsp;
+ *           - output     (optional) - without this one the buttons don't have names
+ * Examples:
+ * <pre>
+ * {html_radios values=$ids output=$names}
+ * {html_radios values=$ids name='box' separator='<br>' output=$names}
+ * {html_radios values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios}
+ *      (Smarty online manual)
+ * @author     Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author credits to Monte Ohrt <monte@ispi.net>
+ * @version    1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_radios($params, &$smarty)
+{
+   require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+   
+   $name = 'radio';
+   $values = null;
+   $options = null;
+   $selected = null;
+   $separator = '';
+   $labels = true;
+   $output = null;
+   $extra = '';
+
+   foreach($params as $_key => $_val) {
+               switch($_key) {
+               case 'name':
+               case 'separator':
+                   $$_key = (string)$_val;
+                   break;
+
+               case 'checked':
+               case 'selected':
+                       if(is_array($_val)) {
+                               $smarty->trigger_error('html_radios: the "' . $_key . '" attribute cannot be an array', E_USER_WARNING);
+                       } else {
+                               $selected = (string)$_val;
+                       }
+                       break;
+
+        case 'labels':
+            $$_key = (bool)$_val;
+            break;
+
+               case 'options':
+                       $$_key = (array)$_val;
+                       break;
+
+               case 'values':
+               case 'output':
+                       $$_key = array_values((array)$_val);
+                       break;
+
+               case 'radios':
+                       $smarty->trigger_error('html_radios: the use of the "radios" attribute is deprecated, use "options" instead', E_USER_WARNING);
+                       $options = (array)$_val;
+                       break;
+
+
+               default:
+                       if(!is_array($_val)) {
+                               $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+                       } else {
+                               $smarty->trigger_error("html_radios: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                       }
+                       break;
+               }
+   }
+
+   if (!isset($options) && !isset($values))
+      return ''; /* raise error here? */
+
+   $_html_result = '';
+
+   if (isset($options) && is_array($options)) {
+
+      foreach ((array)$options as $_key=>$_val)
+        $_html_result .= smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+
+   } else {
+
+      foreach ((array)$values as $_i=>$_key) {
+        $_val = isset($output[$_i]) ? $output[$_i] : '';
+        $_html_result .= smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+      }
+
+   }
+
+   return $_html_result;
+
+}
+
+function smarty_function_html_radios_output($name, $value, $output, $selected, $extra, $separator, $labels) {
+   $_output = '';
+   if ($labels) $_output .= '<label>';
+   $_output .= '<input type="radio" name="'
+      . smarty_function_escape_special_chars($name) . '" value="'
+      . smarty_function_escape_special_chars($value) . '"';
+
+   if ($value==$selected) {
+      $_output .= ' checked="checked"';
+   }
+   $_output .= $extra . ' />' . $output;
+   if ($labels) $_output .= '</label>';
+   $_output .=  $separator . "\n";
+
+   return $_output;
+}
+
+?>
diff --git a/smarty/plugins/function.html_select_date.php b/smarty/plugins/function.html_select_date.php
new file mode 100644 (file)
index 0000000..dd90e92
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {html_select_date} plugin
+ *
+ * Type:     function<br>
+ * Name:     html_select_date<br>
+ * Purpose:  Prints the dropdowns for date selection.
+ *
+ * ChangeLog:<br>
+ *           - 1.0 initial release
+ *           - 1.1 added support for +/- N syntax for begin
+ *                and end year values. (Monte)
+ *           - 1.2 added support for yyyy-mm-dd syntax for
+ *                time value. (Jan Rosier)
+ *           - 1.3 added support for choosing format for 
+ *                month values (Gary Loescher)
+ *           - 1.3.1 added support for choosing format for
+ *                day values (Marcus Bointon)
+ * @link http://smarty.php.net/manual/en/language.function.html.select.date.php {html_select_date}
+ *      (Smarty online manual)
+ * @version 1.3
+ * @author   Andrei Zmievski
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_html_select_date($params, &$smarty)
+{
+    require_once $smarty->_get_plugin_filepath('shared','make_timestamp');
+    require_once $smarty->_get_plugin_filepath('function','html_options');
+    /* Default values. */
+    $prefix          = "Date_";
+    $start_year      = strftime("%Y");
+    $end_year        = $start_year;
+    $display_days    = true;
+    $display_months  = true;
+    $display_years   = true;
+    $month_format    = "%B";
+    /* Write months as numbers by default  GL */
+    $month_value_format = "%m";
+    $day_format      = "%02d";
+    /* Write day values using this format MB */
+    $day_value_format = "%d";
+    $year_as_text    = false;
+    /* Display years in reverse order? Ie. 2000,1999,.... */
+    $reverse_years   = false;
+    /* Should the select boxes be part of an array when returned from PHP?
+       e.g. setting it to "birthday", would create "birthday[Day]",
+       "birthday[Month]" & "birthday[Year]". Can be combined with prefix */
+    $field_array     = null;
+    /* <select size>'s of the different <select> tags.
+       If not set, uses default dropdown. */
+    $day_size        = null;
+    $month_size      = null;
+    $year_size       = null;
+    /* Unparsed attributes common to *ALL* the <select>/<input> tags.
+       An example might be in the template: all_extra ='class ="foo"'. */
+    $all_extra       = null;
+    /* Separate attributes for the tags. */
+    $day_extra       = null;
+    $month_extra     = null;
+    $year_extra      = null;
+    /* Order in which to display the fields.
+       "D" -> day, "M" -> month, "Y" -> year. */
+    $field_order      = 'MDY';
+    /* String printed between the different fields. */
+    $field_separator = "\n";
+       $time = time();
+
+
+    extract($params);
+
+       // If $time is not in format yyyy-mm-dd
+       if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $time)) {
+               // then $time is empty or unix timestamp or mysql timestamp
+               // using smarty_make_timestamp to get an unix timestamp and
+               // strftime to make yyyy-mm-dd
+               $time = strftime('%Y-%m-%d', smarty_make_timestamp($time));
+       }
+       // Now split this in pieces, which later can be used to set the select
+       $time = explode("-", $time);
+  
+       // make syntax "+N" or "-N" work with start_year and end_year
+       if (preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match)) {
+               if ($match[1] == '+') {
+                       $end_year = strftime('%Y') + $match[2];
+               } else {
+                       $end_year = strftime('%Y') - $match[2];
+               }
+       }
+       if (preg_match('!^(\+|\-)\s*(\d+)$!', $start_year, $match)) {
+               if ($match[1] == '+') {
+                       $start_year = strftime('%Y') + $match[2];
+               } else {
+                       $start_year = strftime('%Y') - $match[2];
+               }
+       }
+  
+    $field_order = strtoupper($field_order);
+
+    $html_result = $month_result = $day_result = $year_result = "";
+
+    if ($display_months) {
+        $month_names = array();
+        $month_values = array();
+
+        for ($i = 1; $i <= 12; $i++) {
+            $month_names[] = strftime($month_format, mktime(0, 0, 0, $i, 1, 2000));
+            $month_values[] = strftime($month_value_format, mktime(0, 0, 0, $i, 1, 2000));
+        }
+
+        $month_result .= '<select name=';
+        if (null !== $field_array){
+            $month_result .= '"' . $field_array . '[' . $prefix . 'Month]"';
+        } else {
+            $month_result .= '"' . $prefix . 'Month"';
+        }
+        if (null !== $month_size){
+            $month_result .= ' size="' . $month_size . '"';
+        }
+        if (null !== $month_extra){
+            $month_result .= ' ' . $month_extra;
+        }
+        if (null !== $all_extra){
+            $month_result .= ' ' . $all_extra;
+        }
+        $month_result .= '>'."\n";
+        
+        $month_result .= smarty_function_html_options(array('output'     => $month_names,
+                                                            'values'     => $month_values,
+                                                            'selected'   => $month_values[$time[1]-1],
+                                                            'print_result' => false),
+                                                      $smarty);
+        
+        $month_result .= '</select>';
+    }
+
+    if ($display_days) {
+        $days = array();
+        for ($i = 1; $i <= 31; $i++) {
+            $days[] = sprintf($day_format, $i);
+            $day_values[] = sprintf($day_value_format, $i);
+        }
+
+        $day_result .= '<select name=';
+        if (null !== $field_array){
+            $day_result .= '"' . $field_array . '[' . $prefix . 'Day]"';
+        } else {
+            $day_result .= '"' . $prefix . 'Day"';
+        }
+        if (null !== $day_size){
+            $day_result .= ' size="' . $day_size . '"';
+        }
+        if (null !== $all_extra){
+            $day_result .= ' ' . $all_extra;
+        }
+        if (null !== $day_extra){
+            $day_result .= ' ' . $day_extra;
+        }
+        $day_result .= '>'."\n";
+        $day_result .= smarty_function_html_options(array('output'     => $days,
+                                                          'values'     => $day_values,
+                                                          'selected'   => $time[2],
+                                                          'print_result' => false),
+                                                    $smarty);
+        $day_result .= '</select>';
+    }
+
+    if ($display_years) {
+        if (null !== $field_array){
+            $year_name = $field_array . '[' . $prefix . 'Year]';
+        } else {
+            $year_name = $prefix . 'Year';
+        }
+        if ($year_as_text) {
+            $year_result .= '<input type="text" name="' . $year_name . '" value="' . $time[0] . '" size="4" maxlength="4"';
+            if (null !== $all_extra){
+                $year_result .= ' ' . $all_extra;
+            }
+            if (null !== $year_extra){
+                $year_result .= ' ' . $year_extra;
+            }
+            $year_result .= '>';
+        } else {
+            $years = range((int)$start_year, (int)$end_year);
+            if ($reverse_years) {
+                rsort($years, SORT_NUMERIC);
+            }
+
+            $year_result .= '<select name="' . $year_name . '"';
+            if (null !== $year_size){
+                $year_result .= ' size="' . $year_size . '"';
+            }
+            if (null !== $all_extra){
+                $year_result .= ' ' . $all_extra;
+            }
+            if (null !== $year_extra){
+                $year_result .= ' ' . $year_extra;
+            }
+            $year_result .= '>'."\n";
+            $year_result .= smarty_function_html_options(array('output' => $years,
+                                                               'values' => $years,
+                                                               'selected'   => $time[0],
+                                                               'print_result' => false),
+                                                         $smarty);
+            $year_result .= '</select>';
+        }
+    }
+
+    // Loop thru the field_order field
+    for ($i = 0; $i <= 2; $i++){
+      $c = substr($field_order, $i, 1);
+      switch ($c){
+        case 'D':
+            $html_result .= $day_result;
+            break;
+
+        case 'M':
+            $html_result .= $month_result;
+            break;
+
+        case 'Y':
+            $html_result .= $year_result;
+            break;
+      }
+      // Add the field seperator
+         if($i != 2) {
+       $html_result .= $field_separator;
+         }
+    }
+
+    return $html_result;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.html_select_time.php b/smarty/plugins/function.html_select_time.php
new file mode 100644 (file)
index 0000000..969c13e
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_select_time} function plugin
+ *
+ * Type:     function<br>
+ * Name:     html_select_time<br>
+ * Purpose:  Prints the dropdowns for time selection
+ * @link http://smarty.php.net/manual/en/language.function.html.select.time.php {html_select_time}
+ *          (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_make_timestamp()
+ */
+function smarty_function_html_select_time($params, &$smarty)
+{
+    require_once $smarty->_get_plugin_filepath('shared','make_timestamp');
+    require_once $smarty->_get_plugin_filepath('function','html_options');
+    /* Default values. */
+    $prefix             = "Time_";
+    $time               = time();
+    $display_hours      = true;
+    $display_minutes    = true;
+    $display_seconds    = true;
+    $display_meridian   = true;
+    $use_24_hours       = true;
+    $minute_interval    = 1;
+    $second_interval    = 1;
+    /* Should the select boxes be part of an array when returned from PHP?
+       e.g. setting it to "birthday", would create "birthday[Hour]",
+       "birthday[Minute]", "birthday[Seconds]" & "birthday[Meridian]".
+       Can be combined with prefix. */
+    $field_array        = null;
+    $all_extra          = null;
+    $hour_extra         = null;
+    $minute_extra       = null;
+    $second_extra       = null;
+    $meridian_extra     = null;
+
+    extract($params);
+
+    $time = smarty_make_timestamp($time);
+
+    $html_result = '';
+
+    if ($display_hours) {
+        $hours       = $use_24_hours ? range(0, 23) : range(1, 12);
+        $hour_fmt = $use_24_hours ? '%H' : '%I';
+        for ($i = 0, $for_max = count($hours); $i < $for_max; $i++)
+            $hours[$i] = sprintf('%02d', $hours[$i]);
+        $html_result .= '<select name=';
+        if (null !== $field_array) {
+            $html_result .= '"' . $field_array . '[' . $prefix . 'Hour]"';
+        } else {
+            $html_result .= '"' . $prefix . 'Hour"';
+        }
+        if (null !== $hour_extra){
+            $html_result .= ' ' . $hour_extra;
+        }
+        if (null !== $all_extra){
+            $html_result .= ' ' . $all_extra;
+        }
+        $html_result .= '>'."\n";
+        $html_result .= smarty_function_html_options(array('output'          => $hours,
+                                                           'values'          => $hours,
+                                                           'selected'      => strftime($hour_fmt, $time),
+                                                           'print_result' => false),
+                                                     $smarty);
+        $html_result .= "</select>\n";
+    }
+
+    if ($display_minutes) {
+        $all_minutes = range(0, 59);
+        for ($i = 0, $for_max = count($all_minutes); $i < $for_max; $i+= $minute_interval)
+            $minutes[] = sprintf('%02d', $all_minutes[$i]);
+        $selected = intval(floor(strftime('%M', $time) / $minute_interval) * $minute_interval);
+        $html_result .= '<select name=';
+        if (null !== $field_array) {
+            $html_result .= '"' . $field_array . '[' . $prefix . 'Minute]"';
+        } else {
+            $html_result .= '"' . $prefix . 'Minute"';
+        }
+        if (null !== $minute_extra){
+            $html_result .= ' ' . $minute_extra;
+        }
+        if (null !== $all_extra){
+            $html_result .= ' ' . $all_extra;
+        }
+        $html_result .= '>'."\n";
+        
+        $html_result .= smarty_function_html_options(array('output'          => $minutes,
+                                                           'values'          => $minutes,
+                                                           'selected'      => $selected,
+                                                           'print_result' => false),
+                                                     $smarty);
+        $html_result .= "</select>\n";
+    }
+
+    if ($display_seconds) {
+        $all_seconds = range(0, 59);
+        for ($i = 0, $for_max = count($all_seconds); $i < $for_max; $i+= $second_interval)
+            $seconds[] = sprintf('%02d', $all_seconds[$i]);
+        $selected = intval(floor(strftime('%S', $time) / $second_interval) * $second_interval);
+        $html_result .= '<select name=';
+        if (null !== $field_array) {
+            $html_result .= '"' . $field_array . '[' . $prefix . 'Second]"';
+        } else {
+            $html_result .= '"' . $prefix . 'Second"';
+        }
+        
+        if (null !== $second_extra){
+            $html_result .= ' ' . $second_extra;
+        }
+        if (null !== $all_extra){
+            $html_result .= ' ' . $all_extra;
+        }
+        $html_result .= '>'."\n";
+        
+        $html_result .= smarty_function_html_options(array('output'          => $seconds,
+                                                           'values'          => $seconds,
+                                                           'selected'      => $selected,
+                                                           'print_result' => false),
+                                                     $smarty);
+        $html_result .= "</select>\n";
+    }
+
+    if ($display_meridian && !$use_24_hours) {
+        $html_result .= '<select name=';
+        if (null !== $field_array) {
+            $html_result .= '"' . $field_array . '[' . $prefix . 'Meridian]"';
+        } else {
+            $html_result .= '"' . $prefix . 'Meridian"';
+        }
+        
+        if (null !== $meridian_extra){
+            $html_result .= ' ' . $meridian_extra;
+        }
+        if (null !== $all_extra){
+            $html_result .= ' ' . $all_extra;
+        }
+        $html_result .= '>'."\n";
+        
+        $html_result .= smarty_function_html_options(array('output'          => array('AM', 'PM'),
+                                                           'values'          => array('am', 'pm'),
+                                                           'selected'      => strtolower(strftime('%p', $time)),
+                                                           'print_result' => false),
+                                                     $smarty);
+        $html_result .= "</select>\n";
+    }
+
+    return $html_result;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.html_table.php b/smarty/plugins/function.html_table.php
new file mode 100644 (file)
index 0000000..33be01a
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_table} function plugin
+ *
+ * Type:     function<br>
+ * Name:     html_table<br>
+ * Date:     Feb 17, 2003<br>
+ * Purpose:  make an html table from an array of data<br>
+ * Input:<br>
+ *         - loop = array to loop through
+ *         - cols = number of columns
+ *         - rows = number of rows
+ *         - table_attr = table attributes
+ *         - tr_attr = table row attributes (arrays are cycled)
+ *         - td_attr = table cell attributes (arrays are cycled)
+ *         - trailpad = value to pad trailing cells with
+ *         - vdir = vertical direction (default: "down", means top-to-bottom)
+ *         - hdir = horizontal direction (default: "right", means left-to-right)
+ *         - inner = inner loop (default "cols": print $loop line by line,
+ *                   $loop will be printed column by column otherwise)
+ *
+ *
+ * Examples:
+ * <pre>
+ * {table loop=$data}
+ * {table loop=$data cols=4 tr_attr='"bgcolor=red"'}
+ * {table loop=$data cols=4 tr_attr=$colors}
+ * </pre>
+ * @author   Monte Ohrt <monte@ispi.net>
+ * @version  1.0
+ * @link http://smarty.php.net/manual/en/language.function.html.table.php {html_table}
+ *          (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_html_table($params, &$smarty)
+{
+    $table_attr = 'border="1"';
+    $tr_attr = '';
+    $td_attr = '';
+    $cols = 3;
+    $rows = 3;
+    $trailpad = '&nbsp;';
+    $vdir = 'down';
+    $hdir = 'right';
+    $inner = 'cols';
+
+    extract($params);
+
+    if (!isset($loop)) {
+        $smarty->trigger_error("html_table: missing 'loop' parameter");
+        return;
+    }
+
+    $loop_count = count($loop);
+    if (empty($params['rows'])) {
+        /* no rows specified */
+        $rows = ceil($loop_count/$cols);
+    } elseif (empty($params['cols'])) {
+        if (!empty($params['rows'])) {
+            /* no cols specified, but rows */
+            $cols = ceil($loop_count/$rows);
+        }
+    }
+
+    $output = "<table $table_attr>\n";
+
+    for ($r=0; $r<$rows; $r++) {
+        $output .= "<tr" . smarty_function_html_table_cycle('tr', $tr_attr, $r) . ">\n";
+        $rx =  ($vdir == 'down') ? $r*$cols : ($rows-1-$r)*$cols;
+
+        for ($c=0; $c<$cols; $c++) {
+            $x =  ($hdir == 'right') ? $rx+$c : $rx+$cols-1-$c;
+            if ($inner!='cols') {
+                /* shuffle x to loop over rows*/
+                $x = floor($x/$cols) + ($x%$cols)*$rows;
+            }
+
+            if ($x<$loop_count) {
+                $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">" . $loop[$x] . "</td>\n";
+            } else {
+                $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">$trailpad</td>\n";
+            }
+        }
+        $output .= "</tr>\n";
+    }            
+    $output .= "</table>\n";
+    
+    return $output;
+}
+
+function smarty_function_html_table_cycle($name, $var, $no) {
+    if(!is_array($var)) {
+        $ret = $var;
+    } else {
+        $ret = $var[$no % count($var)];
+    }
+    
+    return ($ret) ? ' '.$ret : '';
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.mailto.php b/smarty/plugins/function.mailto.php
new file mode 100644 (file)
index 0000000..f6e3de0
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {mailto} function plugin
+ *
+ * Type:     function<br>
+ * Name:     mailto<br>
+ * Date:     May 21, 2002
+ * Purpose:  automate mailto address link creation, and optionally
+ *           encode them.<br>
+ * Input:<br>
+ *         - address = e-mail address
+ *         - text = (optional) text to display, default is address
+ *         - encode = (optional) can be one of:
+ *                * none : no encoding (default)
+ *                * javascript : encode with javascript
+ *                * hex : encode with hexidecimal (no javascript)
+ *         - cc = (optional) address(es) to carbon copy
+ *         - bcc = (optional) address(es) to blind carbon copy
+ *         - subject = (optional) e-mail subject
+ *         - newsgroups = (optional) newsgroup(s) to post to
+ *         - followupto = (optional) address(es) to follow up to
+ *         - extra = (optional) extra tags for the href link
+ * 
+ * Examples:
+ * <pre>
+ * {mailto address="me@domain.com"}
+ * {mailto address="me@domain.com" encode="javascript"}
+ * {mailto address="me@domain.com" encode="hex"}
+ * {mailto address="me@domain.com" subject="Hello to you!"}
+ * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"}
+ * {mailto address="me@domain.com" extra='class="mailto"'}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.mailto.php {mailto}
+ *          (Smarty online manual)
+ * @version  1.2
+ * @author      Monte Ohrt <monte@ispi.net>
+ * @author credits to Jason Sweat (added cc, bcc and subject functionality)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_mailto($params, &$smarty)
+{
+    $extra = '';
+    extract($params);
+
+    if (empty($address)) {
+        $smarty->trigger_error("mailto: missing 'address' parameter");
+        return;
+    }
+       
+    if (empty($text)) {
+               $text = $address;
+    }
+       
+       // netscape and mozilla do not decode %40 (@) in BCC field (bug?)
+       // so, don't encode it.
+       
+       $mail_parms = array();
+       if (!empty($cc)) {
+               $mail_parms[] = 'cc='.str_replace('%40','@',rawurlencode($cc));
+       }
+       
+       if (!empty($bcc)) {
+               $mail_parms[] = 'bcc='.str_replace('%40','@',rawurlencode($bcc));
+       }
+       
+       if (!empty($subject)) {
+               $mail_parms[] = 'subject='.rawurlencode($subject);
+       }
+
+       if (!empty($newsgroups)) {
+               $mail_parms[] = 'newsgroups='.rawurlencode($newsgroups);
+       }
+
+       if (!empty($followupto)) {
+               $mail_parms[] = 'followupto='.str_replace('%40','@',rawurlencode($followupto));
+       }
+       
+    $mail_parm_vals = '';
+       for ($i=0; $i<count($mail_parms); $i++) {
+               $mail_parm_vals .= (0==$i) ? '?' : '&';
+               $mail_parm_vals .= $mail_parms[$i];
+       }
+       $address .= $mail_parm_vals;
+       
+       if (empty($encode)) {
+               $encode = 'none';
+    } elseif (!in_array($encode,array('javascript','hex','none')) ) {
+        $smarty->trigger_error("mailto: 'encode' parameter must be none, javascript or hex");
+        return;                
+       }
+       
+       if ($encode == 'javascript' ) {
+               $string = 'document.write(\'<a href="mailto:'.$address.'" '.$extra.'>'.$text.'</a>\');';
+               
+               for ($x=0; $x < strlen($string); $x++) {
+                       $js_encode .= '%' . bin2hex($string[$x]);
+               }
+       
+               return '<script type="text/javascript" language="javascript">eval(unescape(\''.$js_encode.'\'))</script>';
+               
+       } elseif ($encode == 'hex') {
+
+               preg_match('!^(.*)(\?.*)$!',$address,$match);
+               if(!empty($match[2])) {
+               $smarty->trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.");
+               return;                                         
+               }  
+               for ($x=0; $x < strlen($address); $x++) {
+                       if(preg_match('!\w!',$address[$x])) {
+                               $address_encode .= '%' . bin2hex($address[$x]);
+                       } else {
+                               $address_encode .= $address[$x];                                
+                       }
+               }
+               for ($x=0; $x < strlen($text); $x++) {
+                       $text_encode .= '&#x' . bin2hex($text[$x]).';';
+               }
+               
+               return '<a href="mailto:'.$address_encode.'" '.$extra.'>'.$text_encode.'</a>';
+               
+       } else {
+               // no encoding          
+               return '<a href="mailto:'.$address.'" '.$extra.'>'.$text.'</a>';
+
+       }
+       
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.math.php b/smarty/plugins/function.math.php
new file mode 100644 (file)
index 0000000..c080d4d
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {math} function plugin
+ *
+ * Type:     function<br>
+ * Name:     math<br>
+ * Purpose:  handle math computations in template<br>
+ * @link http://smarty.php.net/manual/en/language.function.math.php {math}
+ *          (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_math($params, &$smarty)
+{
+    // be sure equation parameter is present
+    if (empty($params['equation'])) {
+        $smarty->trigger_error("math: missing equation parameter");
+        return;
+    }
+
+    $equation = $params['equation'];
+
+    // make sure parenthesis are balanced
+    if (substr_count($equation,"(") != substr_count($equation,")")) {
+        $smarty->trigger_error("math: unbalanced parenthesis");
+        return;
+    }
+
+    // match all vars in equation, make sure all are passed
+    preg_match_all("!\!(0x)([a-zA-Z][a-zA-Z0-9_]*)!",$equation, $match);
+    $allowed_funcs = array('int','abs','ceil','cos','exp','floor','log','log10',
+                           'max','min','pi','pow','rand','round','sin','sqrt','srand','tan');
+    foreach($match[2] as $curr_var) {
+        if (!in_array($curr_var,array_keys($params)) && !in_array($curr_var, $allowed_funcs)) {
+            $smarty->trigger_error("math: parameter $curr_var not passed as argument");
+            return;
+        }
+    }
+
+    foreach($params as $key => $val) {
+        if ($key != "equation" && $key != "format" && $key != "assign") {
+            // make sure value is not empty
+            if (strlen($val)==0) {
+                $smarty->trigger_error("math: parameter $key is empty");
+                return;
+            }
+            if (!is_numeric($val)) {
+                $smarty->trigger_error("math: parameter $key: is not numeric");
+                return;
+            }
+            $equation = preg_replace("/\b$key\b/",$val, $equation);
+        }
+    }
+
+    eval("\$smarty_math_result = ".$equation.";");
+
+    if (empty($params['format'])) {
+        if (empty($params['assign'])) {
+            return $smarty_math_result;
+        } else {
+            $smarty->assign($params['assign'],$smarty_math_result);
+        }
+    } else {
+        if (empty($params['assign'])){
+            printf($params['format'],$smarty_math_result);
+        } else {
+            $smarty->assign($params['assign'],sprintf($params['format'],$smarty_math_result));
+        }
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.popup.php b/smarty/plugins/function.popup.php
new file mode 100644 (file)
index 0000000..d1030a7
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {popup} function plugin
+ *
+ * Type:     function<br>
+ * Name:     popup<br>
+ * Purpose:  make text pop up in windows via overlib
+ * @link http://smarty.php.net/manual/en/language.function.popup.php {popup}
+ *          (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_popup($params, &$smarty)
+{
+    extract($params);
+
+    if (empty($text) && !isset($inarray) && empty($function)) {
+        $smarty->trigger_error("overlib: attribute 'text' or 'inarray' or 'function' required");
+        return false;
+    }
+
+    if (empty($trigger)) { $trigger = "onmouseover"; }
+
+    $retval = $trigger . '="return overlib(\''.preg_replace(array("!'!","![\r\n]!"),array("\'",'\r'),$text).'\'';
+    if ($sticky) { $retval .= ",STICKY"; }
+    if (!empty($caption)) { $retval .= ",CAPTION,'".str_replace("'","\'",$caption)."'"; }
+    if (!empty($fgcolor)) { $retval .= ",FGCOLOR,'$fgcolor'"; }
+    if (!empty($bgcolor)) { $retval .= ",BGCOLOR,'$bgcolor'"; }
+    if (!empty($textcolor)) { $retval .= ",TEXTCOLOR,'$textcolor'"; }
+    if (!empty($capcolor)) { $retval .= ",CAPCOLOR,'$capcolor'"; }
+    if (!empty($closecolor)) { $retval .= ",CLOSECOLOR,'$closecolor'"; }
+    if (!empty($textfont)) { $retval .= ",TEXTFONT,'$textfont'"; }
+    if (!empty($captionfont)) { $retval .= ",CAPTIONFONT,'$captionfont'"; }
+    if (!empty($closefont)) { $retval .= ",CLOSEFONT,'$closefont'"; }
+    if (!empty($textsize)) { $retval .= ",TEXTSIZE,$textsize"; }
+    if (!empty($captionsize)) { $retval .= ",CAPTIONSIZE,$captionsize"; }
+    if (!empty($closesize)) { $retval .= ",CLOSESIZE,$closesize"; }
+    if (!empty($width)) { $retval .= ",WIDTH,$width"; }
+    if (!empty($height)) { $retval .= ",HEIGHT,$height"; }
+    if (!empty($left)) { $retval .= ",LEFT"; }
+    if (!empty($right)) { $retval .= ",RIGHT"; }
+    if (!empty($center)) { $retval .= ",CENTER"; }
+    if (!empty($above)) { $retval .= ",ABOVE"; }
+    if (!empty($below)) { $retval .= ",BELOW"; }
+    if (isset($border)) { $retval .= ",BORDER,$border"; }
+    if (isset($offsetx)) { $retval .= ",OFFSETX,$offsetx"; }
+    if (isset($offsety)) { $retval .= ",OFFSETY,$offsety"; }
+    if (!empty($fgbackground)) { $retval .= ",FGBACKGROUND,'$fgbackground'"; }
+    if (!empty($bgbackground)) { $retval .= ",BGBACKGROUND,'$bgbackground'"; }
+    if (!empty($closetext)) { $retval .= ",CLOSETEXT,'".str_replace("'","\'",$closetext)."'"; }
+    if (!empty($noclose)) { $retval .= ",NOCLOSE"; }
+    if (!empty($status)) { $retval .= ",STATUS,'".str_replace("'","\'",$status)."'"; }
+    if (!empty($autostatus)) { $retval .= ",AUTOSTATUS"; }
+    if (!empty($autostatuscap)) { $retval .= ",AUTOSTATUSCAP"; }
+    if (isset($inarray)) { $retval .= ",INARRAY,'$inarray'"; }
+    if (isset($caparray)) { $retval .= ",CAPARRAY,'$caparray'"; }
+    if (!empty($capicon)) { $retval .= ",CAPICON,'$capicon'"; }
+    if (!empty($snapx)) { $retval .= ",SNAPX,$snapx"; }
+    if (!empty($snapy)) { $retval .= ",SNAPY,$snapy"; }
+    if (isset($fixx)) { $retval .= ",FIXX,$fixx"; }
+    if (isset($fixy)) { $retval .= ",FIXY,$fixy"; }
+    if (!empty($background)) { $retval .= ",BACKGROUND,'$background'"; }
+    if (!empty($padx)) { $retval .= ",PADX,$padx"; }
+    if (!empty($pady)) { $retval .= ",PADY,$pady"; }
+    if (!empty($fullhtml)) { $retval .= ",FULLHTML"; }
+    if (!empty($frame)) { $retval .= ",FRAME,'$frame'"; }
+    if (isset($timeout)) { $retval .= ",TIMEOUT,$timeout"; }
+    if (!empty($function)) { $retval .= ",FUNCTION,'$function'"; }
+    if (isset($delay)) { $retval .= ",DELAY,$delay"; }
+    if (!empty($hauto)) { $retval .= ",HAUTO"; }
+    if (!empty($vauto)) { $retval .= ",VAUTO"; }
+    $retval .= ');" onmouseout="nd();"';
+       
+       return $retval;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/function.popup_init.php b/smarty/plugins/function.popup_init.php
new file mode 100644 (file)
index 0000000..d9b42bd
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {popup_init} function plugin
+ *
+ * Type:     function<br>
+ * Name:     popup_init<br>
+ * Purpose:  initialize overlib
+ * @link http://smarty.php.net/manual/en/language.function.popup.init.php {popup_init}
+ *          (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_popup_init($params, &$smarty)
+{
+       $zindex = 1000;
+       
+    if (!empty($params['zindex'])) {
+               $zindex = $params['zindex'];
+       }
+       
+    if (!empty($params['src'])) {
+       return '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:'.$zindex.';"></div>' . "\n"
+         . '<script type="text/javascript" language="JavaScript" src="'.$params['src'].'"></script>' . "\n";
+    } else {
+        $smarty->trigger_error("popup_init: missing src parameter");
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.capitalize.php b/smarty/plugins/modifier.capitalize.php
new file mode 100644 (file)
index 0000000..41d63ed
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty capitalize modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     capitalize<br>
+ * Purpose:  capitalize words in the string
+ * @link http://smarty.php.net/manual/en/language.modifiers.php#LANGUAGE.MODIFIER.CAPITALIZE
+ *      capitalize (Smarty online manual)
+ * @param string
+ * @return string
+ */
+function smarty_modifier_capitalize($string)
+{
+       return ucwords($string);
+}
+
+?>
diff --git a/smarty/plugins/modifier.cat.php b/smarty/plugins/modifier.cat.php
new file mode 100644 (file)
index 0000000..8dc7324
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty cat modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     cat<br>
+ * Date:     Feb 24, 2003
+ * Purpose:  catenate a value to a variable
+ * Input:    string to catenate
+ * Example:  {$var|cat:"foo"}
+ * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat
+ *          (Smarty online manual)
+ * @author      Monte Ohrt <monte@ispi.net>
+ * @version 1.0
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_cat($string, $cat)
+{
+       return $string . $cat;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.count_characters.php b/smarty/plugins/modifier.count_characters.php
new file mode 100644 (file)
index 0000000..49ce655
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_characters modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     count_characteres<br>
+ * Purpose:  count the number of characters in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.characters.php
+ *          count_characters (Smarty online manual)
+ * @param string
+ * @param boolean include whitespace in the character count
+ * @return integer
+ */
+function smarty_modifier_count_characters($string, $include_spaces = false)
+{
+    if ($include_spaces)
+       return(strlen($string));
+
+    return preg_match_all("/[^\s]/",$string, $match);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.count_paragraphs.php b/smarty/plugins/modifier.count_paragraphs.php
new file mode 100644 (file)
index 0000000..6a9833c
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_paragraphs modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     count_paragraphs<br>
+ * Purpose:  count the number of paragraphs in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php
+ *          count_paragraphs (Smarty online manual)
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_paragraphs($string)
+{
+    // count \r or \n characters
+    return count(preg_split('/[\r\n]+/', $string));
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.count_sentences.php b/smarty/plugins/modifier.count_sentences.php
new file mode 100644 (file)
index 0000000..0c210f0
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_sentences modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     count_sentences
+ * Purpose:  count the number of sentences in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php
+ *          count_sentences (Smarty online manual)
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_sentences($string)
+{
+    // find periods with a word before but not after.
+    return preg_match_all('/[^\s]\.(?!\w)/', $string, $match);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.count_words.php b/smarty/plugins/modifier.count_words.php
new file mode 100644 (file)
index 0000000..42c8a74
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_words modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     count_words<br>
+ * Purpose:  count the number of words in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.words.php
+ *          count_words (Smarty online manual)
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_words($string)
+{
+    // split text by ' ',\r,\n,\f,\t
+    $split_array = preg_split('/\s+/',$string);
+    // count matches that contain alphanumerics
+    $word_count = preg_grep('/[a-zA-Z0-9\\x80-\\xff]/', $split_array);
+
+    return count($word_count);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.date_format.php b/smarty/plugins/modifier.date_format.php
new file mode 100644 (file)
index 0000000..dbe26a5
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Include the {@link shared.make_timestamp.php} plugin
+ */
+require_once $smarty->_get_plugin_filepath('shared','make_timestamp');
+/**
+ * Smarty date_format modifier plugin
+ * 
+ * Type:     modifier<br>
+ * Name:     date_format<br>
+ * Purpose:  format datestamps via strftime<br>
+ * Input:<br>
+ *         - string: input date string
+ *         - format: strftime format for output
+ *         - default_date: default date if $string is empty
+ * @link http://smarty.php.net/manual/en/language.modifier.date.format.php
+ *          date_format (Smarty online manual)
+ * @param string
+ * @param string
+ * @param string
+ * @return string|void
+ * @uses smarty_make_timestamp()
+ */
+function smarty_modifier_date_format($string, $format="%b %e, %Y", $default_date=null)
+{
+       if($string != '') {
+       return strftime($format, smarty_make_timestamp($string));
+       } elseif (isset($default_date) && $default_date != '') {                
+       return strftime($format, smarty_make_timestamp($default_date));
+       } else {
+               return;
+       }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.debug_print_var.php b/smarty/plugins/modifier.debug_print_var.php
new file mode 100644 (file)
index 0000000..3528311
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty debug_print_var modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     debug_print_var<br>
+ * Purpose:  formats variable contents for display in the console
+ * @link http://smarty.php.net/manual/en/language.modifier.debug.print.var.php
+ *          debug_print_var (Smarty online manual)
+ * @param array|object
+ * @param integer
+ * @param integer
+ * @return string
+ */
+function smarty_modifier_debug_print_var($var, $depth = 0, $length = 40)
+{
+       $_replace = array("\n"=>'<i>&#92;n</i>', "\r"=>'<i>&#92;r</i>', "\t"=>'<i>&#92;t</i>');
+    if (is_array($var)) {
+        $results = "<b>Array (".count($var).")</b>";
+        foreach ($var as $curr_key => $curr_val) {
+            $return = smarty_modifier_debug_print_var($curr_val, $depth+1, $length);
+            $results .= "<br>".str_repeat('&nbsp;', $depth*2)."<b>".strtr($curr_key, $_replace)."</b> =&gt; $return";
+        }
+        return $results;
+    } else if (is_object($var)) {
+        $object_vars = get_object_vars($var);
+        $results = "<b>".get_class($var)." Object (".count($object_vars).")</b>";
+        foreach ($object_vars as $curr_key => $curr_val) {
+            $return = smarty_modifier_debug_print_var($curr_val, $depth+1, $length);
+            $results .= "<br>".str_repeat('&nbsp;', $depth*2)."<b>$curr_key</b> =&gt; $return";
+        }
+        return $results;
+    } else {
+        if (empty($var) && $var != "0") {
+            return '<i>empty</i>';
+        }
+        if (strlen($var) > $length ) {
+            $results = substr($var, 0, $length-3).'...';
+        } else {
+            $results = $var;
+        }
+        $results = htmlspecialchars($results);
+        $results = strtr($results, $_replace);
+        return $results;
+    }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.default.php b/smarty/plugins/modifier.default.php
new file mode 100644 (file)
index 0000000..8268e39
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty default modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     default<br>
+ * Purpose:  designate default value for empty variables
+ * @link http://smarty.php.net/manual/en/language.modifier.default.php
+ *          default (Smarty online manual)
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_default($string, $default = '')
+{
+    if (!isset($string) || $string === '')
+        return $default;
+    else
+        return $string;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.escape.php b/smarty/plugins/modifier.escape.php
new file mode 100644 (file)
index 0000000..dc7bae2
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty escape modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     escape<br>
+ * Purpose:  Escape the string according to escapement type
+ * @link http://smarty.php.net/manual/en/language.modifier.escape.php
+ *          escape (Smarty online manual)
+ * @param string
+ * @param html|htmlall|url|quotes|hex|hexentity|javascript
+ * @return string
+ */
+function smarty_modifier_escape($string, $esc_type = 'html')
+{
+    switch ($esc_type) {
+        case 'html':
+            return htmlspecialchars($string, ENT_QUOTES);
+
+        case 'htmlall':
+            return htmlentities($string, ENT_QUOTES);
+
+        case 'url':
+            return urlencode($string);
+
+        case 'quotes':
+            // escape unescaped single quotes
+            return preg_replace("%(?<!\\\\)'%", "\\'", $string);
+
+        case 'qp':
+            return smarty_modifier_escape_qp_enc($string);
+
+               case 'hex':
+                       // escape every character into hex
+                       $return = '';
+                       for ($x=0; $x < strlen($string); $x++) {
+                               $return .= '%' . bin2hex($string[$x]);
+                       }
+                       return $return;
+            
+               case 'hexentity':
+                       $return = '';
+                       for ($x=0; $x < strlen($string); $x++) {
+                               $return .= '&#x' . bin2hex($string[$x]) . ';';
+                       }
+                       return $return;
+
+        case 'javascript':
+            // escape quotes and backslashes and newlines
+            return strtr($string, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n'));
+
+        default:
+            return $string;
+    }
+}
+
+function smarty_modifier_escape_qp_enc( $input = "") {
+   $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+   $escape  = "=";
+   $linlen  = strlen($input);
+   $newline = '';
+   for($i = 0; $i < $linlen; $i++) {
+     $c = substr( $input, $i, 1 );
+     $dec = ord( $c );
+     if( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ){
+       $h2 = floor($dec/16);
+       $h1 = floor($dec%16);
+       $c = $escape.$hex["$h2"].$hex["$h1"];
+     }
+     $newline .= $c;
+   }
+   return $newline;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.indent.php b/smarty/plugins/modifier.indent.php
new file mode 100644 (file)
index 0000000..552c3e1
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty indent modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     indent<br>
+ * Purpose:  indent lines of text
+ * @link http://smarty.php.net/manual/en/language.modifier.indent.php
+ *          indent (Smarty online manual)
+ * @param string
+ * @param integer
+ * @param string
+ * @return string
+ */
+function smarty_modifier_indent($string,$chars=4,$char=" ")
+{
+       return preg_replace('!^!m',str_repeat($char,$chars),$string);
+}
+
+?>
diff --git a/smarty/plugins/modifier.lower.php b/smarty/plugins/modifier.lower.php
new file mode 100644 (file)
index 0000000..ee37423
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty lower modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     lower<br>
+ * Purpose:  convert string to lowercase
+ * @link http://smarty.php.net/manual/en/language.modifier.lower.php
+ *          lower (Smarty online manual)
+ * @param string
+ * @return string
+ */
+function smarty_modifier_lower($string)
+{
+       return strtolower($string);
+}
+
+?>
diff --git a/smarty/plugins/modifier.nl2br.php b/smarty/plugins/modifier.nl2br.php
new file mode 100644 (file)
index 0000000..5a9b744
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty plugin
+ *
+ * Type:     modifier<br>
+ * Name:     nl2br<br>
+ * Date:     Feb 26, 2003
+ * Purpose:  convert \r\n, \r or \n to <<br>>
+ * Input:<br>
+ *         - contents = contents to replace
+ *         - preceed_test = if true, includes preceeding break tags
+ *           in replacement
+ * Example:  {$text|nl2br}
+ * @link http://smarty.php.net/manual/en/language.modifier.nl2br.php
+ *          nl2br (Smarty online manual)
+ * @version  1.0
+ * @author      Monte Ohrt <monte@ispi.net>
+ * @param string
+ * @return string
+ */
+function smarty_modifier_nl2br($string)
+{
+    return nl2br($string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.regex_replace.php b/smarty/plugins/modifier.regex_replace.php
new file mode 100644 (file)
index 0000000..b9cc865
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty regex_replace modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     regex_replace<br>
+ * Purpose:  regular epxression search/replace
+ * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php
+ *          regex_replace (Smarty online manual)
+ * @param string
+ * @param string|array
+ * @param string|array
+ * @return string
+ */
+function smarty_modifier_regex_replace($string, $search, $replace)
+{
+    return preg_replace($search, $replace, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.replace.php b/smarty/plugins/modifier.replace.php
new file mode 100644 (file)
index 0000000..2a43515
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty replace modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     replace<br>
+ * Purpose:  simple search/replace
+ * @link http://smarty.php.net/manual/en/language.modifier.replace.php
+ *          replace (Smarty online manual)
+ * @param string
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_replace($string, $search, $replace)
+{
+    return str_replace($search, $replace, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.spacify.php b/smarty/plugins/modifier.spacify.php
new file mode 100644 (file)
index 0000000..dad057f
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty spacify modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     spacify<br>
+ * Purpose:  add spaces between characters in a string
+ * @link http://smarty.php.net/manual/en/language.modifier.spacify.php
+ *          spacify (Smarty online manual)
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_spacify($string, $spacify_char = ' ')
+{
+    return implode($spacify_char,
+                   preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY));
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.string_format.php b/smarty/plugins/modifier.string_format.php
new file mode 100644 (file)
index 0000000..efd6215
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty string_format modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     string_format<br>
+ * Purpose:  format strings via sprintf
+ * @link http://smarty.php.net/manual/en/language.modifier.string.format.php
+ *          string_format (Smarty online manual)
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_string_format($string, $format)
+{
+    return sprintf($format, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.strip.php b/smarty/plugins/modifier.strip.php
new file mode 100644 (file)
index 0000000..0db2f8a
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty strip modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     strip<br>
+ * Purpose:  Replace all repeated spaces, newlines, tabs
+ *           with a single space or supplied replacement string.<br>
+ * Example:  {$var|strip} {$var|strip:"&nbsp;"}
+ * Date:     September 25th, 2002
+ * @link http://smarty.php.net/manual/en/language.modifier.strip.php
+ *          strip (Smarty online manual)
+ * @author   Monte Ohrt <monte@ispi.net>
+ * @version  1.0
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_strip($text, $replace = ' ')
+{
+       return preg_replace('!\s+!', $replace, $text);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.strip_tags.php b/smarty/plugins/modifier.strip_tags.php
new file mode 100644 (file)
index 0000000..45f1ec1
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty strip_tags modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     strip_tags<br>
+ * Purpose:  strip html tags from text
+ * @link http://smarty.php.net/manual/en/language.modifier.strip.tags.php
+ *          strip_tags (Smarty online manual)
+ * @param string
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_strip_tags($string, $replace_with_space = true)
+{
+    if ($replace_with_space)
+        return preg_replace('!<[^>]*?>!', ' ', $string);
+    else
+        return strip_tags($string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.truncate.php b/smarty/plugins/modifier.truncate.php
new file mode 100644 (file)
index 0000000..c82b14a
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty truncate modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     truncate<br>
+ * Purpose:  Truncate a string to a certain length if necessary,
+ *           optionally splitting in the middle of a word, and 
+ *           appending the $etc string.
+ * @link http://smarty.php.net/manual/en/language.modifier.truncate.php
+ *          truncate (Smarty online manual)
+ * @param string
+ * @param integer
+ * @param string
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_truncate($string, $length = 80, $etc = '...',
+                                  $break_words = false)
+{
+    if ($length == 0)
+        return '';
+
+    if (strlen($string) > $length) {
+        $length -= strlen($etc);       
+        if (!$break_words)
+           $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length+1));
+      
+        return substr($string, 0, $length).$etc;
+    } else
+        return $string;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/modifier.upper.php b/smarty/plugins/modifier.upper.php
new file mode 100644 (file)
index 0000000..9d9ef35
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty upper modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     upper<br>
+ * Purpose:  convert string to uppercase
+ * @link http://smarty.php.net/manual/en/language.modifier.upper.php
+ *          upper (Smarty online manual)
+ * @param string
+ * @return string
+ */
+function smarty_modifier_upper($string)
+{
+       return strtoupper($string);
+}
+
+?>
diff --git a/smarty/plugins/modifier.wordwrap.php b/smarty/plugins/modifier.wordwrap.php
new file mode 100644 (file)
index 0000000..55b4a1d
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty wordwrap modifier plugin
+ *
+ * Type:     modifier<br>
+ * Name:     wordwrap<br>
+ * Purpose:  wrap a string of text at a given length
+ * @link http://smarty.php.net/manual/en/language.modifier.wordwrap.php
+ *          wordwrap (Smarty online manual)
+ * @param string
+ * @param integer
+ * @param string
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_wordwrap($string,$length=80,$break="\n",$cut=false)
+{
+       return wordwrap($string,$length,$break,$cut);
+}
+
+?>
diff --git a/smarty/plugins/outputfilter.trimwhitespace.php b/smarty/plugins/outputfilter.trimwhitespace.php
new file mode 100644 (file)
index 0000000..e82acc1
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty trimwhitespace outputfilter plugin
+ *
+ * File:     outputfilter.trimwhitespace.php<br>
+ * Type:     outputfilter<br>
+ * Name:     trimwhitespace<br>
+ * Date:     Jan 25, 2003<br>
+ * Purpose:  trim leading white space and blank lines from
+ *           template source after it gets interpreted, cleaning
+ *           up code and saving bandwidth. Does not affect
+ *           <<PRE>></PRE> and <SCRIPT></SCRIPT> blocks.<br>
+ * Install:  Drop into the plugin directory, call 
+ *           <code>$smarty->load_filter('output','trimwhitespace');</code>
+ *           from application.
+ * @author   Monte Ohrt <monte@ispi.net>
+ * @author Contributions from Lars Noschinski <lars@usenet.noschinski.de>
+ * @version  1.3
+ * @param string
+ * @param Smarty
+ */
+ function smarty_outputfilter_trimwhitespace($source, &$smarty)
+ {
+    // Pull out the script blocks
+    preg_match_all("!<script[^>]+>.*?</script>!is", $source, $match);
+    $_script_blocks = $match[0];
+    $source = preg_replace("!<script[^>]+>.*?</script>!is",
+    '@@@SMARTY:TRIM:SCRIPT@@@', $source);
+
+    // Pull out the pre blocks
+    preg_match_all("!<pre>.*?</pre>!is", $source, $match);
+    $_pre_blocks = $match[0];
+    $source = preg_replace("!<pre>.*?</pre>!is",
+    '@@@SMARTY:TRIM:PRE@@@', $source);
+
+    // Pull out the textarea blocks
+    preg_match_all("!<textarea[^>]+>.*?</textarea>!is", $source, $match);
+    $_textarea_blocks = $match[0];
+    $source = preg_replace("!<textarea[^>]+>.*?</textarea>!is",
+    '@@@SMARTY:TRIM:TEXTAREA@@@', $source);
+
+       // remove all leading spaces, tabs and carriage returns NOT
+       // preceeded by a php close tag.
+       $source = trim(preg_replace('/((?<!\?>)\n)[\s]+/m', '\1', $source));
+
+       // replace script blocks
+       smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:SCRIPT@@@",$_script_blocks, $source);
+
+       // replace pre blocks
+       smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:PRE@@@",$_pre_blocks, $source);
+
+    // replace textarea blocks
+       smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:TEXTAREA@@@",$_textarea_blocks, $source);
+
+       return $source; 
+ }
+
+function smarty_outputfilter_trimwhitespace_replace($search_str, $replace, &$subject) {
+   $_len = strlen($search_str);
+   $_pos = 0;
+   for ($_i=0, $_count=count($replace); $_i<$_count; $_i++)
+      if (($_pos=strpos($subject, $search_str, $_pos))!==false)
+        $subject = substr_replace($subject, $replace[$_i], $_pos, $_len);
+      else
+        break;
+   
+}
+
+?>
diff --git a/smarty/plugins/shared.escape_special_chars.php b/smarty/plugins/shared.escape_special_chars.php
new file mode 100644 (file)
index 0000000..090ee9c
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Smarty shared plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * escape_special_chars common function
+ *
+ * Function: smarty_function_escape_special_chars<br>
+ * Purpose:  used by other smarty functions to escape
+ *           special chars except for already escaped ones
+ * @param string
+ * @return string
+ */
+function smarty_function_escape_special_chars($string)
+{
+       if(!is_array($string))  {
+               $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+               $string = htmlspecialchars($string);
+               $string = str_replace(array('%%%SMARTY_START%%%','%%%SMARTY_END%%%'), array('&',';'), $string);
+       }
+       return $string; 
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/smarty/plugins/shared.make_timestamp.php b/smarty/plugins/shared.make_timestamp.php
new file mode 100644 (file)
index 0000000..acdd777
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty shared plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Function: smarty_make_timestamp<br>
+ * Purpose:  used by other smarty functions to make a timestamp
+ *           from a string.
+ * @param string
+ * @return string
+ */
+function smarty_make_timestamp($string)
+{
+    if(empty($string)) {
+        $string = "now";
+    }
+    $time = strtotime($string);
+    if (is_numeric($time) && $time != -1)
+        return $time;
+
+    // is mysql timestamp format of YYYYMMDDHHMMSS?
+    if (preg_match('/^\d{14}$/', $string)) {
+        $time = mktime(substr($string,8,2),substr($string,10,2),substr($string,12,2),
+               substr($string,4,2),substr($string,6,2),substr($string,0,4));
+
+        return $time;
+    }
+
+    // couldn't recognize it, try to return a time
+    $time = (int) $string;
+    if ($time > 0)
+        return $time;
+    else
+        return time();
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/template.php b/template.php
new file mode 100644 (file)
index 0000000..7448a24
--- /dev/null
@@ -0,0 +1,163 @@
+<?
+
+/**
+ * assigns some standard variables to smarty templates
+ */
+function tpl_std(){
+  global $smarty;
+  global $lang;
+  global $conf;
+
+  $smarty->assign('user',$_SESSION[ldapab][username]);
+  $smarty->assign('binddn',$_SESSION[ldapab][binddn]);
+  if(!empty($_SESSION[ldapab][lastlocation])){
+    $smarty->assign('home',$_SESSION[ldapab][lastlocation]);
+  }else{
+     $smarty->assign('home','index.php');
+  }
+  $smarty->assign('conf',$conf);
+  $smarty->assign('lang',$lang);
+  $smarty->assign('dfexample',$dfexample);
+}
+
+/**
+ * assigns all the interesting data from an ldap result to
+ * the smarty template
+ */
+function tpl_entry($in){
+  global $smarty;
+  global $conf;
+  $entries = namedentries();
+
+
+  //handle named entries
+  foreach(array_keys($entries) as $key){
+    if($in[$key]){
+      if(is_array($in[$key])){
+        $out[$entries[$key]] = $in[$key][0];
+      }else{
+        $out[$entries[$key]] = $in[$key];
+      }
+    }
+  }
+
+  //set the type
+  $out['dn']        = normalize_dn($out['dn']);
+  $conf[publicbook] = normalize_dn($conf[publicbook]);
+  if($out['dn']){
+    if(strstr($out['dn'],$conf[publicbook])){
+      $out[type] = 'public';
+    }else{
+      $out[type] = 'private';
+    }
+  }
+
+  //mail entries are handled special
+  $out['mail'] = $in['mail'];
+  if ($conf[extended]){
+    //handle marker special in extended mode
+    $out['marker'] = $in['marker'];
+  }
+
+  //decode array
+  utf8_decode_array($out);
+
+/*print '<pre>';
+print_r($out);
+print '</pre>';*/
+
+  $smarty->assign('entry',$out);
+}
+
+/**
+ * assigns the last LDAP error to the template
+ */
+function tpl_ldaperror($message=""){
+  global $LDAP_CON;
+  global $__LDAPERROR__;
+  global $smarty;
+  $errno = ldap_errno($LDAP_CON);
+  if($errno){
+    $__LDAPERROR__ .= ldap_err2str($errno);
+    if(!empty($message)){
+      $__LDAPERROR__ .= "($message)";
+    }
+    $__LDAPERROR__ .= '\n';
+  }
+  $smarty->assign("LDAPERRORS",$__LDAPERROR__);
+}
+
+/**
+ * assigns all markers to the template
+ */
+function tpl_markers(){
+  global $conf;
+  global $LDAP_CON;
+  global $smarty;
+
+  if(!$conf[extended]) return;
+
+  $markers = array();
+
+  $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=inetOrgPerson",array("marker"));
+  $result1 = ldap_get_binentries($LDAP_CON, $sr);
+  //check users private addressbook
+  if(!empty($_SESSION[ldapab][binddn])){
+    $sr = @ldap_list($LDAP_CON,
+                    $conf[privatebook].','.$_SESSION[ldapab][binddn],
+                    "ObjectClass=inetOrgPerson",array("marker"));
+    $result2 = ldap_get_binentries($LDAP_CON, $sr);
+  }
+  $result = array_merge($result1,$result2);
+
+  if(count($result)){
+    foreach ($result as $entry){
+      if(count($entry['marker'])){
+        foreach($entry['marker'] as $marker){
+          array_push($markers, $marker);
+        }
+      }
+    }
+  }
+  $markers = array_unique($markers);
+  sort($markers,SORT_STRING);
+  utf8_decode_array($markers);
+  $smarty->assign('markers',$markers);
+}
+
+/**
+ * Assigns all distinct organization names to the template
+ */
+function tpl_orgs(){
+  global $conf;
+  global $LDAP_CON;
+  global $smarty;
+
+  $orgs = array();
+
+  $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=inetOrgPerson",array("o"));
+  $result1 = ldap_get_binentries($LDAP_CON, $sr);
+  //check users private addressbook
+  if(!empty($_SESSION[ldapab][binddn])){
+    $sr = @ldap_list($LDAP_CON,
+                    $conf[privatebook].','.$_SESSION[ldapab][binddn],
+                    "ObjectClass=inetOrgPerson",array("o"));
+    $result2 = ldap_get_binentries($LDAP_CON, $sr);
+  }
+  $result = array_merge($result1,$result2);
+
+  if(count($result)){
+    foreach ($result as $entry){
+      if(!empty($entry[o][0])){
+        array_push($orgs, $entry[o][0]);
+      }
+    }
+  }
+  $orgs = array_unique($orgs);
+  sort($orgs,SORT_STRING);
+  utf8_decode_array($orgs);
+  $smarty->assign('orgs',$orgs);
+}
+
+?>
diff --git a/templates/blank.gif b/templates/blank.gif
new file mode 100644 (file)
index 0000000..9935f82
Binary files /dev/null and b/templates/blank.gif differ
diff --git a/templates/entry_edit.tpl b/templates/entry_edit.tpl
new file mode 100644 (file)
index 0000000..de2283d
--- /dev/null
@@ -0,0 +1,179 @@
+<h1>
+  {if $entry.type != ''}
+  <img src="pix/{$entry.type}.png" border="0" width="22" height="22" align="middle" title="{$entry.type}">
+  {/if}
+  {$entry.givenname} {$entry.name}
+</h1>
+
+<form action="entry.php" method="post" enctype="multipart/form-data">
+<input type="hidden" name="MAX_FILE_SIZE" value="2000000" />
+<input type="hidden" name="dn" value="{$entry.dn|escape}" />
+<input type="hidden" name="save" value="1" />
+
+{include file="ldaperror.tpl"}
+
+<table width="100%">
+  <tr>
+    <td valign="top" width="50%" align="center">
+      <table>
+        <tr>
+          <td colspan="2"><b>{$lang.business}</b></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.name}:</td>
+          <td><input type="text" class="input" name="entry[name]" value="{$entry.name|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.givenname}:</td>
+          <td><input type="text" class="input" name="entry[givenname]" value="{$entry.givenname|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.title}:</td>
+          <td><input type="text" class="input" name="entry[title]" value="{$entry.title|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.organization}:</td>
+          <td>
+            <input type="text" class="input" name="entry[organization]" value="{$entry.organization|escape}">
+          </td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap"></td>
+          <td>
+            <select onChange="document.forms[0].elements['entry[organization]'].value=this.options[this.selectedIndex].value" class="input">
+              <option value="">--- {$lang.select} ---</option>
+              {html_options values=$orgs output=$orgs}
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.office}:</td>
+          <td><input type="text" class="input" name="entry[office]" value="{$entry.office|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.street}:</td>
+          <td><input type="text" class="input" name="entry[street]" value="{$entry.street|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.zip}:</td>
+          <td><input type="text" class="input" name="entry[zip]" value="{$entry.zip|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.location}:</td>
+          <td><input type="text" class="input" name="entry[location]" value="{$entry.location|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.phone}:</td>
+          <td><input type="text" class="input" name="entry[phone]" value="{$entry.phone|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.fax}:</td>
+          <td><input type="text" class="input" name="entry[fax]" value="{$entry.fax|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.pager}:</td>
+          <td><input type="text" class="input" name="entry[pager]" value="{$entry.pager|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.manager}:</td>
+          <td>
+            <select name="entry[manager]" class="input">
+              <option value="">--- {$lang.select} ---</option>
+              {html_options options=$managers selected=$entry.manager}
+            </select>
+          </td>
+        </tr>
+
+      {if $conf.extended}
+        {include file="extended_edit.tpl"}
+      {/if}
+      </table>
+    </td>
+    
+    <td valign="top" width="50%" align="center">
+      
+      
+      <table>
+        <tr>
+          <td colspan="2"><b>{$lang.private}</b></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.homestreet}:</td>
+          <td><textarea name="entry[homestreet]" class="input" rows="2" cols="30">{$entry.homestreet|escape}</textarea></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.homephone}:</td>
+          <td><input type="text" class="input" name="entry[homephone]" value="{$entry.homephone|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.mobile}:</td>
+          <td><input type="text" class="input" name="entry[mobile]" value="{$entry.mobile|escape}"></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.url}:</td>
+          <td><input type="text" class="input" name="entry[url]" value="{$entry.url|escape}"></td>
+        </tr>
+        
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{$lang.photo}:<br /><span class="hint">({$lang.msg_jpegonly})</span></td>
+          <td>{if $entry.photo}
+                <input type="checkbox" class="radio" name="delphoto" id="delphoto" value="1">
+                <label for="delphoto">{$lang.delphoto}</label>
+                <br />
+              {/if}
+            <input type="file" class="input" name="photoupload">
+          </td>
+        </tr>
+        
+
+        <tr>
+          <td colspan="2"><b>{$lang.mail}</b></td>
+        </tr>
+
+        {foreach from=$entry.mail item=mail}      
+          <tr>
+            <td align="right" valign="top" nowrap="nowrap">{counter}:</td>
+            <td><input type="text" class="input" name="entry[mail][]" value="{$mail|escape}"></td>
+          </tr>
+        {/foreach}
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{counter}:</td>
+          <td><input type="text" class="input" name="entry[mail][]" value=""></td>
+        </tr>
+        <tr>
+          <td align="right" valign="top" nowrap="nowrap">{counter}:</td>
+          <td><input type="text" class="input" name="entry[mail][]" value=""></td>
+        </tr>
+
+        <tr>
+          <td colspan="2"><b>{$lang.note}</b></td>
+        </tr>
+
+        <tr>
+          <td colspan="2" align="right"><textarea class="input" rows="6" cols="30" name="entry[note]" class="note">{$entry.note|escape}</textarea></td>
+        </tr>
+
+      </table>
+    </td>
+  </tr>
+  {if $entry.dn == ''}
+  <tr>
+    <td colspan="2" align="center">
+      {$lang.msg_addto}<br>
+      <table><tr><td>
+      <input type="radio" name="type" value="public" id="typepublic" class="radio" checked="checked">
+      <label for="typepublic"><img src="pix/public.png" border="0" width="22" height="22" align="middle">{$lang.publicbook}</label><br>
+      <input type="radio" name="type" value="private" id="typeprivate" class="radio">
+      <label for="typeprivate"><img src="pix/private.png" border="0" width="22" height="22" align="middle">{$lang.privatebook}</label>
+      </td></tr></table>
+    </td>
+  </tr>
+  {/if}
+  <tr>
+    <td colspan="2" align="center"><br><input type="submit" class="input" value="{$lang.submit}"></td>
+  </tr>
+</table>
+
+</form>
+
+<br>
diff --git a/templates/entry_show.tpl b/templates/entry_show.tpl
new file mode 100644 (file)
index 0000000..b87a69e
--- /dev/null
@@ -0,0 +1,120 @@
+<h1>
+  <img src="pix/{$entry.type}.png" border="0" width="22" height="22" align="middle" title="{$entry.type}">
+  {$entry.givenname} {$entry.name}
+</h1>
+{if $entry.photo != ''}
+  <img src="img.php?dn={$entry.dn|escape:url}" align="right" class="photo">
+{/if}
+
+{include file="ldaperror.tpl"}
+
+
+<table width="100%">
+  <tr>
+    <td valign="top" width="50%">
+      <b>{$lang.business}</b>
+      <dl><dd>
+        <table>
+          <tr>
+            <td colspan="2">
+              {$entry.title} {$entry.givenname} {$entry.name}<br>
+              {if $entry.organization}<a href="index.php?org={$entry.organization|escape:url}">{$entry.organization}</a><br>{/if}
+              {if $entry.office}{$entry.office}<br>{/if}
+              {if $entry.street}{$entry.street}<br>{/if}
+              {if $entry.location}{$entry.zip} {$entry.location}<br>{/if}
+              <br>
+            </td>
+          </tr>
+          {if $entry.phone}
+          <tr>
+            <td align="right">{$lang.phone}:</td>
+            <td>{$entry.phone}</td>
+          </tr>
+          {/if}
+          {if $entry.fax}
+          <tr>
+            <td align="right">{$lang.fax}:</td>
+            <td>{$entry.fax}</td>
+          </tr>
+          {/if}
+          {if $entry.pager}
+          <tr>
+            <td align="right">{$lang.pager}:</td>
+            <td>{$entry.pager}</td>
+          </tr>
+          {/if}
+          {if $entry.mail}
+          <tr>
+            <td valign="top" align="right">{$lang.mail}:</td>
+            <td>
+              {foreach from=$entry.mail item=mail}
+                <a href="mailto:{$mail}">{$mail}</a><br>
+              {/foreach}
+            </td>
+          </tr>
+          {/if}
+        </table>
+      </dd></dl>
+      {if $conf.extended}
+        {include file="extended_show.tpl"}
+      {/if}
+    </td>
+    <td valign="top" width="50%">
+      <b>{$lang.private}</b>
+      <dl><dd>
+        <table>
+          <tr>
+            <td colspan="2">
+              {if $entry.homestreet}
+                {$entry.givenname} {$entry.name}<br>
+                {$entry.homestreet|nl2br}<br><br>
+              {/if}
+            </td>
+          </tr>
+          {if $entry.homephone}
+          <tr>
+            <td align="right">{$lang.homephone}:</td>
+            <td>{$entry.homephone}</td>
+          </tr>
+          {/if}
+          {if $entry.mobile}
+          <tr>
+            <td align="right">{$lang.mobile}:</td>
+            <td>{$entry.mobile}</td>
+          </tr>
+          {/if}
+          {if $entry.url}
+          <tr>
+            <td align="right">{$lang.url}:</td>
+            <td><a href="{$entry.url}" target="_blank">{$entry.url}</a></td>
+          </tr>
+          {/if}
+        </table>
+      </dd></dl>
+
+      {if $entry.note}      
+      <b>{$lang.note}</b>
+      <dl><dd>
+        {$entry.note|nl2br}
+      </dd></dl>
+      {/if}
+
+
+      {if $managername}
+      <b>{$lang.manager}</b>
+      <dl><dd>
+        <table>
+          <tr>
+            <td colspan="2">
+              {$managername}
+            </td>
+          </tr>
+        </table>
+      </dd></dl>
+      {/if}
+              
+    </td>
+  </tr>
+</table>
+<br><br><br>
+
diff --git a/templates/entry_vcf.tpl b/templates/entry_vcf.tpl
new file mode 100644 (file)
index 0000000..ed3776c
--- /dev/null
@@ -0,0 +1,19 @@
+BEGIN:VCARD
+VERSION:2.1
+N:{$entry.name};{$entry.givenname};;{$entry.title}
+FN:{$entry.givenname} {$entry.name}
+ORG;ENCODING=QUOTED-PRINTABLE:{$entry.organisation|escape:qp};{$entry.office|escape:qp}
+NOTE;ENCODING=QUOTED-PRINTABLE:{$entry.note|escape:qp}
+TEL;WORK;VOICE;ENCODING=QUOTED-PRINTABLE:{$entry.phone|escape:qp}
+TEL;HOME;VOICE;ENCODING=QUOTED-PRINTABLE:{$entry.homephone|escape:qp}
+TEL;CELL;VOICE;ENCODING=QUOTED-PRINTABLE:{$entry.mobile|escape:qp}
+TEL;WORK;FAX;ENCODING=QUOTED-PRINTABLE:{$entry.fax|escape:qp}
+TEL;WORK;PAGER;ENCODING=QUOTED-PRINTABLE:{$entry.pager|escape:qp}
+{foreach from=$entry.mail item=mail}
+EMAIL;INTERNET:{$mail}
+{/foreach}
+ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;{$entry.street|escape:qp};{$entry.location|escape:qp};;{$entry.plz|escape:qp};
+ADR;HOME;ENCODING=QUOTED-PRINTABLE:;;{$entry.homestreet|escape:qp}
+URL;WORK:{$entry.url}
+BDAY:{$entry.anniversary}
+END:VCARD
diff --git a/templates/error.tpl b/templates/error.tpl
new file mode 100644 (file)
index 0000000..6f8d460
--- /dev/null
@@ -0,0 +1,10 @@
+
+<h1>An error occured</h1>
+
+{include file="ldaperror.tpl"}
+
+<table class="tableborder">
+<tr>
+  <td>{$error}</td>
+</tr>
+</table>
diff --git a/templates/extended_edit.tpl b/templates/extended_edit.tpl
new file mode 100644 (file)
index 0000000..8139fde
--- /dev/null
@@ -0,0 +1,20 @@
+
+  <tr>
+    <td colspan="2"><b>{$lang.extended}</b></td>
+  </tr>
+  <tr>
+    <td align="right" valign="top" nowrap="nowrap">{$lang.anniversary}:<br><span class="hint">({$lang.msg_dateformat})</span></td>
+    <td><input type="text" class="input" name="entry[anniversary]" value="{$entry.anniversary|escape}" maxlength="10"></td>
+  </tr>
+
+  <tr>
+    <td align="right" valign="top" nowrap="nowrap">{$lang.marker}:</td>
+    <td>
+      <select name="entry[marker][]" size="5" class="input" multiple="multiple">
+        {html_options values=$markers output=$markers selected=$entry.marker}
+      </select>
+      <input type="text" class="inputbr" name="entry[marker][]"><br>
+      <input type="text" class="inputbr" name="entry[marker][]"><br>
+    </td>
+
+  </tr>
diff --git a/templates/extended_show.tpl b/templates/extended_show.tpl
new file mode 100644 (file)
index 0000000..985dbcf
--- /dev/null
@@ -0,0 +1,22 @@
+<b>{$lang.extended}</b>
+<dl><dd>
+  <table>
+    <tr>
+    {if $entry.anniversary}
+    <tr>
+      <td align="right">{$lang.anniversary}:</td>
+      <td>{$entry.anniversary|date_format:$conf.dateformat}</td>
+    </tr>
+    {/if}
+    {if $entry.marker}
+    <tr>
+      <td valign="top" align="right">{$lang.marker}:</td>
+      <td>
+        {foreach from=$entry.marker item=marker}
+          {$marker}<br>
+        {/foreach}
+      </td>
+    </tr>
+    {/if}
+  </table>
+</dd></dl>
diff --git a/templates/footer.tpl b/templates/footer.tpl
new file mode 100644 (file)
index 0000000..ccadac9
--- /dev/null
@@ -0,0 +1,42 @@
+  </td>
+</tr>
+
+<tr>
+  <td class="buttonrow" align="right">
+    <table width="100%"><tr>
+      <td align="left">
+        <a href="orgs.php"><img src="pix/orgs.png" border="0" width="22" height="22" align="middle">{$lang.orgs}</a>
+      </td>
+      <td align="right">
+      {if $dn}
+        <a href="entry.php?dn={$dn|escape:url}&mode=vcf"><img src="pix/vcard.png" border="0" width="22" height="22" align="middle">{$lang.vcfexport}</a>
+      {/if}
+      {if $user}
+          <a href="import.php"><img src="pix/import.png" border="0" width="22" height="22" align="middle">{$lang.vcfimport}</a> 
+        {if $dn}
+          {if $smarty.request.mode == 'edit'}
+            <a href="entry.php?dn={$dn|escape:url}"><img src="pix/show.png" border="0" width="22" height="22" align="middle">{$lang.show}</a>
+          {elseif $smarty.request.mode != 'copy'}
+            <a href="entry.php?dn={$dn|escape:url}&mode=edit"><img src="pix/edit.png" border="0" width="22" height="22" align="middle">{$lang.edit}</a>
+            <a href="entry.php?dn={$dn|escape:url}&mode=copy"><img src="pix/copy.png" border="0" width="22" height="22" align="middle">{$lang.copy}</a>
+            <a href="entry.php?dn={$dn|escape:url}&del=1" onClick="return confirm('{$lang.msg_reallydel}');"><img src="pix/delete.png" border="0" width="22" height="22" align="middle">{$lang.delete}</a>
+          {/if}
+        {/if}
+          <a href="entry.php?mode=edit"><img src="pix/new.png" border="0" width="22" height="22" align="middle">{$lang.new}</a>
+      {/if}
+      </td>
+    </tr></table>
+  </td>
+</tr>
+<tr>
+  <td class="footrow">
+    {if $user == ''}
+      <a href="login.php"><img src="pix/login.png" border="0" width="22" height="22" align="middle"> {$lang.login}</a>&nbsp;&nbsp;&nbsp;
+      {$lang.notloggedin}
+    {else}
+      <a href="login.php?username="><img src="pix/logout.png" border="0" width="22" height="22" align="middle"> {$lang.logout}</a>&nbsp;&nbsp;&nbsp;
+      {$lang.loggedinas} <b>{$user}</b>
+    {/if}
+  </td>
+</tr>
+</table>
diff --git a/templates/header.tpl b/templates/header.tpl
new file mode 100644 (file)
index 0000000..9467e8a
--- /dev/null
@@ -0,0 +1,23 @@
+<html>
+<head>
+  <title>LDAPab - {$lang.ldapab}</title>
+  <link rel="stylesheet" type="text/css" href="templates/style.css" />
+</head>
+<body>
+
+<table width="100%">
+<tr>
+  <td class="headrow">
+    <table width="100%"><tr>
+      <td align="left" valign="bottom">
+        <a href="{$home}" class="logo">LDAPab</a>
+        <span class="logosmall">- {$lang.ldapab}</span>
+      </td>
+      <td align="right" valign="bottom">
+        <a href="help.php"><img src="pix/help.png" border="0" width="22" height="22" align="middle">&nbsp;{$lang.help}</a>
+      </td>
+    </tr></table>
+  </td>
+</tr>
+<tr>
+  <td>
diff --git a/templates/help.tpl b/templates/help.tpl
new file mode 100644 (file)
index 0000000..efa29af
--- /dev/null
@@ -0,0 +1,43 @@
+
+<h2>Adding LDAP-Access to your Mailclient</h2>
+
+<h3>Public Addressbook</h3>
+
+<p>Use these settings to connect to the public addressbook:</p>
+<dl>
+  <dt>LDAP-Server</dt>
+  <dd><code>{$conf.ldapserver}</code></dd>
+  
+  <dt>Bind-DN</dt>
+  <dd><code>leave empty</code> (anonymous bind)</dd>
+  
+  <dt>Bind-Password</dt>
+  <dd><code>leave empty</code> (anonymous bind)</dd>
+  
+  <dt>Base/Search-DN</dt>
+  <dd><code>{$conf.publicbook}</code></dd>
+</dl>
+
+<h3>Private Addressbook</h3>
+
+{if $binddn}
+<p>To access your persanal addressdata use these settings:</p>
+<dl>
+  <dt>LDAP-Server</dt>
+  <dd><code>{$conf.ldapserver}</code></dd>
+  
+  <dt>Bind-DN</dt>
+  <dd><code>{$binddn}</code></dd>
+  
+  <dt>Bind-Password</dt>
+  <dd><code>your password</code> (The one you used to login here)</dd>
+  
+  <dt>Base/Search-DN</dt>
+  <dd><code>{$conf.privatebook},{$binddn}</code></dd>
+</dl>
+{else}
+<p>Login to see this data</p>
+{/if}
+
+
+<br><br>
diff --git a/templates/import.tpl b/templates/import.tpl
new file mode 100644 (file)
index 0000000..f1dd5f5
--- /dev/null
@@ -0,0 +1,25 @@
+<table cellspacing="0" cellpadding="0" width="100%">
+<tr>
+<td class="filterrow">
+  <form action="import.php" method="post" enctype="multipart/form-data">
+    {$lang.msg_uploadvcf}: <input name="userfile" type="file" class="upload" />
+    <input type="submit" value="{$lang.upload}" class="searchbutton"/>
+  </form>
+</td>
+</tr>
+</table>
+
+<br><br>
+<table cellspacing="0" cellpadding="0" width="100%" align="center">
+  {if $list == ''}
+    <tr>
+      <td align="center">
+        {$error}
+      </td>
+    </tr>
+  {else}
+    {* $list is a concatenation of multiple importVCF_entry.tpl *}
+    {$list}
+  {/if}
+</table>
+<br><br><br>
diff --git a/templates/importVCF.tpl b/templates/importVCF.tpl
new file mode 100644 (file)
index 0000000..1e13444
--- /dev/null
@@ -0,0 +1,25 @@
+<table cellspacing="0" cellpadding="0" width="100%">
+<tr>
+<td class="filterrow">
+  <form action="importVCF.php" method="post" enctype="multipart/form-data">
+    {$lang.msg_uploadvcf}: <input name="userfile" type="file" class="upload" />
+    <input type="submit" value="{$lang.upload}" class="searchbutton"/>
+  </form>
+</td>
+</tr>
+</table>
+
+<br><br>
+<table cellspacing="0" cellpadding="0" width="100%" align="center">
+  {if $list == ''}
+    <tr>
+      <td align="center">
+        {$error}
+      </td>
+    </tr>
+  {else}
+    {* $list is a concatenation of multiple importVCF_entry.tpl *}
+    {$list}
+  {/if}
+</table>
+<br><br><br>
diff --git a/templates/importVCF_entry.tpl b/templates/importVCF_entry.tpl
new file mode 100644 (file)
index 0000000..b2361ad
--- /dev/null
@@ -0,0 +1,39 @@
+<tr>
+       <form action="entry.php" method="post" target="_blank">
+  <td class="result">
+    <b>{$entry.name}, {$entry.givenname}</b>
+  </td>
+  <td class="result">
+               <input type="hidden" name="save" value="1" />
+               <input type="hidden" name="entry[name]" value="{$entry.name|escape}" />
+               <input type="hidden" name="entry[givenname]" value="{$entry.givenname|escape}" />
+               <input type="hidden" name="entry[title]" value="{$entry.title|escape}" />
+               <input type="hidden" name="entry[organization]" value="{$entry.organization|escape}" />
+               <input type="hidden" name="entry[office]" value="{$entry.office|escape}" />
+               <input type="hidden" name="entry[street]" value="{$entry.street|escape}" />
+               <input type="hidden" name="entry[zip]" value="{$entry.zip|escape}" />
+               <input type="hidden" name="entry[location]" value="{$entry.location|escape}" />
+               <input type="hidden" name="entry[phone]" value="{$entry.phone|escape}" />
+               <input type="hidden" name="entry[fax]" value="{$entry.fax|escape}" />
+               <input type="hidden" name="entry[pager]" value="{$entry.pager|escape}" />
+               <input type="hidden" name="entry[homestreet]" value="{$entry.homestreet|escape}" />
+               <input type="hidden" name="entry[homephone]" value="{$entry.homephone|escape}" />
+               <input type="hidden" name="entry[mobile]" value="{$entry.mobile|escape}" />
+               <input type="hidden" name="entry[url]" value="{$entry.url|escape}" />
+               {foreach from=$entry.mail item=mail}
+               <input type="hidden" name="entry[mail][]" value="{$mail|escape}" />
+               {/foreach}
+               <input type="hidden" name="entry[note]" value="{$entry.note|escape}" />
+               <input type="hidden" name="entry[anniversary]" value="{$entry.anniversary|escape}" />
+       </td>
+       <td class="result" align="right">
+               <button name="type" value="public">
+       <img src="pix/public.png" border="0" width="22" height="22" align="middle">{$lang.publicbook}
+               </button>
+    <button name="type" value="private">
+       <img src="pix/private.png" border="0" width="22" height="22" align="middle">{$lang.privatebook}
+               </button>
+  </td>
+  </form>
+</tr>
+
diff --git a/templates/import_entry.tpl b/templates/import_entry.tpl
new file mode 100644 (file)
index 0000000..fd4854b
--- /dev/null
@@ -0,0 +1,39 @@
+<tr>
+       <form action="entry.php" method="post" target="_import_">
+  <td class="result">
+    <b>{$entry.name}, {$entry.givenname}</b>
+  </td>
+  <td class="result">
+               <input type="hidden" name="save" value="1" />
+               <input type="hidden" name="entry[name]" value="{$entry.name|escape}" />
+               <input type="hidden" name="entry[givenname]" value="{$entry.givenname|escape}" />
+               <input type="hidden" name="entry[title]" value="{$entry.title|escape}" />
+               <input type="hidden" name="entry[organization]" value="{$entry.organization|escape}" />
+               <input type="hidden" name="entry[office]" value="{$entry.office|escape}" />
+               <input type="hidden" name="entry[street]" value="{$entry.street|escape}" />
+               <input type="hidden" name="entry[zip]" value="{$entry.zip|escape}" />
+               <input type="hidden" name="entry[location]" value="{$entry.location|escape}" />
+               <input type="hidden" name="entry[phone]" value="{$entry.phone|escape}" />
+               <input type="hidden" name="entry[fax]" value="{$entry.fax|escape}" />
+               <input type="hidden" name="entry[pager]" value="{$entry.pager|escape}" />
+               <input type="hidden" name="entry[homestreet]" value="{$entry.homestreet|escape}" />
+               <input type="hidden" name="entry[homephone]" value="{$entry.homephone|escape}" />
+               <input type="hidden" name="entry[mobile]" value="{$entry.mobile|escape}" />
+               <input type="hidden" name="entry[url]" value="{$entry.url|escape}" />
+               {foreach from=$entry.mail item=mail}
+               <input type="hidden" name="entry[mail][]" value="{$mail|escape}" />
+               {/foreach}
+               <input type="hidden" name="entry[note]" value="{$entry.note|escape}" />
+               <input type="hidden" name="entry[anniversary]" value="{$entry.anniversary|escape}" />
+       </td>
+       <td class="result" align="right">
+               <button name="type" value="public">
+       <img src="pix/public.png" border="0" width="22" height="22" align="middle">{$lang.publicbook}
+               </button>
+    <button name="type" value="private">
+       <img src="pix/private.png" border="0" width="22" height="22" align="middle">{$lang.privatebook}
+               </button>
+  </td>
+  </form>
+</tr>
+
diff --git a/templates/ldaperror.tpl b/templates/ldaperror.tpl
new file mode 100644 (file)
index 0000000..fbcea18
--- /dev/null
@@ -0,0 +1,5 @@
+{if $LDAPERRORS != ''}
+<script>
+  window.alert('{$lang.err_ldap}:\n\n{$LDAPERRORS|escape:quotes}');
+</script>
+{/if}
diff --git a/templates/list.tpl b/templates/list.tpl
new file mode 100644 (file)
index 0000000..fe16dfd
--- /dev/null
@@ -0,0 +1,14 @@
+<br><br>
+<table cellspacing="0" cellpadding="0" width="100%" align="center">
+  {if $list == ''}
+    <tr>
+      <td align="center">
+        {$lang.err_noentries}
+      </td>
+    </tr>
+  {else}
+    {* $list is a concatenation of multiple list_entry.tpl *}
+    {$list}
+  {/if}
+</table>
+<br><br><br>
diff --git a/templates/list_entry.tpl b/templates/list_entry.tpl
new file mode 100644 (file)
index 0000000..27f4f05
--- /dev/null
@@ -0,0 +1,25 @@
+<tr>
+  <td class="result" width="25">
+    <img src="pix/{$entry.type}.png" border="0" width="22" height="22" align="middle" title="{$entry.type}">
+  </td>
+  <td class="result">
+    <b><a href="entry.php?dn={$entry.dn|escape:url}">{$entry.name}, {$entry.givenname}</a></b>
+  </td>
+  <td class="result">
+    <a href="index.php?org={$entry.organization|escape:url}">{$entry.organization}</a>&nbsp;
+  </td>
+  <td class="result">
+    {$entry.phone}&nbsp;
+  </td>
+  <td class="result">
+    <a href="mailto:{$entry.mail[0]}">{$entry.mail[0]}</a>&nbsp;
+  </td>
+  <td class="result" width="16">
+    {if $entry.photo}
+      <a href="img.php?dn={$entry.dn|escape:url}" target="_blank" title="{$lang.photo}"><img src="pix/image.png" border="0" width="16" height="16" align="middle" title="{$lang.photo}"></a>
+    {else}
+      &nbsp;
+    {/if}
+  </td>
+</tr>
+
diff --git a/templates/list_filter.tpl b/templates/list_filter.tpl
new file mode 100644 (file)
index 0000000..1b9f714
--- /dev/null
@@ -0,0 +1,50 @@
+<table cellspacing="0" cellpadding="0" width="100%">
+<tr>
+<td class="filterrow">
+  <a href="index.php?filter=a">A</a>
+  <a href="index.php?filter=b">B</a>
+  <a href="index.php?filter=c">C</a>
+  <a href="index.php?filter=d">D</a>
+  <a href="index.php?filter=e">E</a>
+  <a href="index.php?filter=f">F</a>
+  <a href="index.php?filter=g">G</a>
+  <a href="index.php?filter=h">H</a>
+  <a href="index.php?filter=i">I</a>
+  <a href="index.php?filter=j">J</a>
+  <a href="index.php?filter=k">K</a>
+  <a href="index.php?filter=l">L</a>
+  <a href="index.php?filter=m">M</a>
+  <a href="index.php?filter=n">N</a>
+  <a href="index.php?filter=o">O</a>
+  <a href="index.php?filter=p">P</a>
+  <a href="index.php?filter=q">Q</a>
+  <a href="index.php?filter=r">R</a>
+  <a href="index.php?filter=s">S</a>
+  <a href="index.php?filter=t">T</a>
+  <a href="index.php?filter=u">U</a>
+  <a href="index.php?filter=v">V</a>
+  <a href="index.php?filter=w">W</a>
+  <a href="index.php?filter=x">X</a>
+  <a href="index.php?filter=y">Y</a>
+  <a href="index.php?filter=z">Z</a>
+  <a href="index.php?filter=other">#</a>
+</td>
+{if $conf.extended}
+<td class="filterrow" align="right">
+  <form method="get" action="index.php" style="display:inline">
+    <select name="marker" class="searchfield">
+      <option value="">--- {$lang.marker} ---</option>
+      {html_options values=$markers output=$markers selected=$smarty.request.marker}
+    </select>
+    <input type="submit" value="{$lang.search}" class="searchbutton">
+  </form>
+</td>
+{/if}
+<td class="filterrow" align="right">
+  <form method="get" action="index.php" style="display:inline">
+    <input type="text" name="search" class="searchfield" value="{$smarty.request.search}">
+    <input type="submit" value="{$lang.search}" class="searchbutton">
+  </form>
+</td>
+</tr>
+</table>
diff --git a/templates/login.tpl b/templates/login.tpl
new file mode 100644 (file)
index 0000000..d4196f6
--- /dev/null
@@ -0,0 +1,21 @@
+
+  <br /><br /><br />
+  <form action="login.php" method="post">
+  <table class="tableborder" align="center">
+    <tr>
+      <td colspan="2" align="center">{$msg}<br /><br /></td>
+    </tr>
+    <tr>
+      <td align="right">{$lang.username}:</td>
+      <td><input name="username" class="input" type="text" /></td>
+    </tr>
+    <tr>
+      <td align="right">{$lang.password}:</td>
+      <td><input name="password" type="password" class="input" /></td>
+    </tr>
+    <tr>
+      <td colspan="2" align="center"><input type="submit" class="input" value="login" /></td>
+    </tr>
+  </table>
+  </form>
+  <br /><br /><br />
diff --git a/templates/orgs.tpl b/templates/orgs.tpl
new file mode 100644 (file)
index 0000000..fcfe45a
--- /dev/null
@@ -0,0 +1,18 @@
+<table width="100%">
+{foreach from=$orgs item=org}
+  <tr>
+    <td class="result" width="25">
+      <img src="pix/org.png" border="0" width="22" height="22" align="middle">
+    </td>
+    <td class="result">
+      <a href="index.php?org={$org|escape:url}">{$org}</a><br>
+    </td>
+  </tr>
+{foreachelse}
+  <tr>
+    <td align="center">
+      <br /><br />{$lang.err_noentries}<br /><br />
+    </td>
+  </tr>
+{/foreach}
+</table>
diff --git a/templates/pngbehavior.htc b/templates/pngbehavior.htc
new file mode 100644 (file)
index 0000000..7849c8d
--- /dev/null
@@ -0,0 +1,53 @@
+// this is an ugly fix to make Internet Explorer work with transparent
+// PNG images - do your self a favour and use a real browser!
+
+<public:component>
+<public:attach event="onpropertychange" onevent="propertyChanged()" />
+<script>
+
+var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32";
+var realSrc;
+var blankSrc = "templates/blank.gif";
+
+if (supported) fixImage();
+
+function propertyChanged() {
+   if (!supported) return;
+
+   var pName = event.propertyName;
+   if (pName != "src") return;
+   // if not set to blank
+   if ( ! new RegExp(blankSrc).test(src))
+      fixImage();
+};
+
+function fixImage() {
+   // get src
+   var src = element.src;
+
+   // check for real change
+   if (src == realSrc) {
+      element.src = blankSrc;
+      return;
+   }
+
+   if ( ! new RegExp(blankSrc).test(src)) {
+      // backup old src
+      realSrc = src;
+   }
+
+   // test for png
+   if ( /\.png$/.test( realSrc.toLowerCase() ) ) {
+      // set blank image
+      element.src = blankSrc;
+      // set filter
+      element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
+   }
+   else {
+      // remove filter
+      element.runtimeStyle.filter = "";
+   }
+}
+
+</script>
+</public:component>
diff --git a/templates/style.css b/templates/style.css
new file mode 100644 (file)
index 0000000..ed951e6
--- /dev/null
@@ -0,0 +1,92 @@
+body,p,div,td {font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif; font-size: 14px;}
+a:link { text-decoration:none; color:#0033CC }
+a:visited { text-decoration:none; color:#0033CC }
+a:active { text-decoration:none; color:#0033CC }
+a:hover { text-decoration:underline; color:#FF6600 }
+
+/* IE PNG bugfix */
+img { behavior: url("templates/pngbehavior.htc"); }
+
+/* debugging style
+table {background-color:#cccc00;margin:3px}
+tr    {background-color:#99FF99;margin:3px}
+td    {background-color:#66CCFF:margin:3px}
+*/
+
+.logo {font-size:25px; font-weight:bolder}
+.logosmall {font-size:16px; font-weight:bold}
+
+.headrow {border-bottom-width:1px; border-bottom-style:solid}
+.footrow {border-top-width:1px; border-top-style:solid}
+.buttonrow {border-top-width:1px;
+            border-top-style:solid;
+            border-top-color:#cccccc;}
+.filterrow {border-bottom-width:1px;
+            border-bottom-style:solid;
+            border-bottom-color:#cccccc;}
+
+.tableborder {border-width:1px; border-style:solid}
+
+.result {border-bottom-width:1px;
+         border-bottom-style:dashed;
+         border-bottom-color:#cccccc;
+         padding-top:3px;
+         padding-top:4px;}
+
+.searchfield {border-width:1px;
+              border-style:solid;
+              border-color:#000000;
+              margin:2px;
+              font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+              font-size: 12px;
+              width: 120px;}
+.searchbutton {border-width:1px;
+               border-style:solid;
+               border-color:#000000;
+               margin:2px;
+               font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+               font-size: 12px;
+               width: 70px;}
+.upload      {border-width:1px;
+              border-style:solid;
+              border-color:#000000;
+              margin:2px;
+              font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+              font-size: 12px;}
+               
+.photo {border-width:1px;
+        border-style:solid;
+        border-color:#000000;
+        padding:3px;
+        }
+
+.input {border-width:1px;
+        border-style:solid;
+        border-color:#000000;
+        font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+        font-size: 12px;
+        width: 250px;}
+
+.inputbr {border-width:1px;
+        border-style:solid;
+        border-color:#000000;
+        font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+        font-size: 12px;
+        width: 250px;
+        margin-top:3px;}
+
+
+.radio {border-width:0px;}
+
+.note {width: 350px}
+
+.ldaperror {border-width:1px;
+            border-style:solid;
+            border-color:#ff0000;
+            margin:2px;
+            font-family:Lucida,Helvetica,Verdana,Arial,Sans-Serif;
+            font-size: 12px;}
+
+code, pre {color:#008000;}
+
+.hint {font-size:10px; color:#990099;}
diff --git a/xml.php b/xml.php
new file mode 100644 (file)
index 0000000..2f987f7
--- /dev/null
+++ b/xml.php
@@ -0,0 +1,139 @@
+<?php
+###################################################################################
+#
+# XML Library, by Keith Devens, version 1.0
+# http://keithdevens.com/software/phpxml
+#
+# This code is Open Source, released under terms similar to the Artistic License.
+# Read the license at http://keithdevens.com/software/license
+#
+###################################################################################
+
+###################################################################################
+# XML_unserialize: takes raw XML as a parameter (a string)
+# and returns an equivalent PHP data structure
+###################################################################################
+function & XML_unserialize(&$xml){
+       $xml_parser = &new XML();
+       $data = &$xml_parser->parse($xml);
+       $xml_parser->destruct();
+       return $data;
+}
+###################################################################################
+# XML_serialize: serializes any PHP data structure into XML
+# Takes one parameter: the data to serialize. Must be an array.
+###################################################################################
+function & XML_serialize(&$data, $level = 0, $prior_key = NULL){
+       #assumes a hash, keys are the tag names
+       $xml_parts = '';
+       while(list($key, $value) = each($data)){
+               if(!strpos($key, ' attr')){ #if it's not an attribute
+                       #... we don't treat attributes by themselves.
+                       #note that implies that for an empty element that has attributes, you still
+                       #need to set the element to NULL
+
+                       $attributes = array();
+                       if(array_key_exists("$key attr", $data)){ #if there's an attribute for this element
+                               while(list($attr_name, $attr_value) = each($data["$key attr"]))
+                                       $attributes[] = $attr_name.'="'.htmlspecialchars($attr_value).'"';
+                               reset($data["$key attr"]);
+                       }
+
+                       if(is_array($value) and array_key_exists(0, $value)){
+                               #numeric array (note that you can't have numeric keys at two levels in a row)
+                               $xml_parts .= XML_serialize($value, $level, $key);
+                       }else{
+                               if($prior_key) $key = $prior_key;
+                               #(i.e. if we're in a numeric array, replace the number with the actual tag)
+
+                               $xml_parts .= str_repeat("\t", $level).'<'.$key;
+                               if($attributes) $xml_parts .= ' '.join(' ',$attributes);
+
+                               if(is_null($value))
+                                       $xml_parts .= " />\r\n";
+                               elseif(!is_array($value))
+                                       $xml_parts .= '>'.htmlspecialchars($value)."</$key>\r\n";
+                               else
+                                       $xml_parts .= ">\r\n".XML_serialize($value, $level+1).str_repeat("\t", $level)."</$key>\r\n";
+                       }
+               }
+       }
+       reset($data);
+       if($level == 0) return "<?xml version=\"1.0\" ?>\r\n".$xml_parts;
+       return $xml_parts;
+}
+###################################################################################
+# XML class: utility class to be used with PHP's XML handling functions
+###################################################################################
+class XML {
+       var $parser;   #a reference to the XML parser
+       var $document; #the entire XML structure built up so far
+       var $parent;   #a pointer to the current parent - the parent will be an array
+       var $stack;    #a stack of the most recent parent at each nesting level
+       var $last_opened_tag; #keeps track of the last tag opened.
+
+       function XML(){
+               $this->parser = &xml_parser_create();
+               xml_parser_set_option(&$this->parser, XML_OPTION_CASE_FOLDING, 0);
+               xml_set_object(&$this->parser, &$this);
+               xml_set_element_handler(&$this->parser, 'open','close');
+               xml_set_character_data_handler(&$this->parser, 'data');
+       }
+       function destruct(){
+               xml_parser_free(&$this->parser);
+       }
+       function & parse(&$data){
+               $this->document = array();
+               $this->parent   = &$this->document;
+               $this->stack    = array();
+               return xml_parse(&$this->parser, &$data, true) ? $this->document : NULL;
+       }
+       function open(&$parser, $tag, $attributes){
+               $this->data = array(); #stores temporary cdata
+               $this->last_opened_tag = $tag;
+               if(is_array($this->parent) and array_key_exists($tag,$this->parent)){ #if you've seen this tag before
+                       if(is_array($this->parent[$tag]) and array_key_exists(0,$this->parent[$tag])){ #if the keys are numeric
+                               #this is the third or later instance of $tag we've come across
+                               $key = count_numeric_items($this->parent[$tag]);
+                       }else{
+                               #this is the second instance of $tag that we've seen
+                               #shift around
+                               if(array_key_exists("$tag attr",$this->parent)){
+                                       $arr = array('0 attr'=>&$this->parent["$tag attr"], &$this->parent[$tag]);                                      unset($this->parent["$tag attr"]);
+                               }else{
+                                       $arr = array(&$this->parent[$tag]);
+                               }
+                               $this->parent[$tag] = &$arr;
+                               $key = 1;
+                       }
+                       $this->parent = &$this->parent[$tag];
+               }else{
+                       $key = $tag;
+               }
+               if($attributes)
+                       $this->parent["$key attr"] = $attributes;
+
+               $this->parent[$key] = NULL; #it turns out you can take a reference to NULL :)
+               $this->parent       = &$this->parent[$key];
+               $this->stack[]      = &$this->parent;
+       }
+       function data(&$parser, $data){
+               if($this->last_opened_tag != NULL) #you don't need to store whitespace in between tags
+                       $this->data[] = $data;
+       }
+       function close(&$parser, $tag){
+               static $just_closed = false;
+               if($this->last_opened_tag == $tag){
+                       if($this->data)
+                               $this->parent = join('',$this->data);
+                       $this->last_opened_tag = NULL;
+               }
+               array_pop($this->stack);
+               if($this->stack)
+                       $this->parent = &$this->stack[count($this->stack)-1];
+       }
+}
+function count_numeric_items(&$array){
+       return is_array($array) ? count(array_filter(array_keys($array), 'is_numeric')) : 0;
+}
+?>
\ No newline at end of file