From: Andreas Gohr
Date: Tue, 19 Jun 2007 14:07:20 +0000 (+0200)
Subject: file hierachy reordering
X-Git-Tag: 0.7.1~62
X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=64fca63a29fb2a863d159734627795acfb17c853;p=contagged
file hierachy reordering
darcs-hash:20070619140720-6e07b-2001276d635b4578c12c90724ce6b2cd7f41e785.gz
---
diff --git a/COPYING b/COPYING
deleted file mode 100644
index 5b6e7c6..0000000
--- a/COPYING
+++ /dev/null
@@ -1,340 +0,0 @@
- 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.
-
- 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.)
-
-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.
-
- 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.
-
- 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
-
- 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.
-
-
- Copyright (C)
-
- 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.
-
- , 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
deleted file mode 100644
index da1f16f..0000000
--- a/Contact_Vcard_Parse.php
+++ /dev/null
@@ -1,836 +0,0 @@
- |
-// +----------------------------------------------------------------------+
-//
-// $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:
-*
-*
-* // 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 '
';
-* print_r($cardinfo);
-* echo '
';
-*
-*
-*
-* @author Paul M. Jones
-*
-* @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 = '(? ;
- * \, => ,
- * 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 and his
- * vCard PHP project .
- *
- * @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
deleted file mode 100644
index d37c796..0000000
--- a/README
+++ /dev/null
@@ -1,112 +0,0 @@
-=== ABOUT ===
-
-ConTagged 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. ConTagged requires an already setup LDAP server to
-authenticate users and store it's data.
-
-=== 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 PHP (4.3 or higher). PHP 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 engine's 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.
-
-If HTTP auth is wanted have a look at the provided _htaccess file, edit it as
-needed and rename it back to .htaccess
-
-=== 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 (needed for tagging and birthday support)
-you need to copy the ldapab.schema file to /etc/ldap/schema/ and include it, too.
-
- include /etc/ldap/schema/ldapab.schema
-
-Be sure to enable the extended option in config.php.
-
-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.regex="(.*,)?ou=contacts,cn=([^,]+),ou=people,(.*)$"
- by dn.regex="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 ConTagged 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 or visit our homepage at
-http://www.cosmocode.de
-
-=== KNOWN BUGS ===
-
-ConTagged was only tested with open-ldap 2.1 - there may be problems with other
-LDAP Servers.
-
-=== LICENSE ===
-
-ConTagged - An LDAP based Company Address Book
-Copyright 2004 - 2006 - 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
deleted file mode 100644
index 478af17..0000000
--- a/TODO
+++ /dev/null
@@ -1,6 +0,0 @@
-Any help is welcome :-)
-
- - clean up the code
- - clean up the HTML (make it validate)
- - clean up the CSS (it's a mess)
- - spice up interface (some more web2.0 colors and fonts would be a start)
diff --git a/ajax.php b/ajax.php
index 997eba1..7e16b2f 100644
--- a/ajax.php
+++ b/ajax.php
@@ -1,14 +1,7 @@
-require_once('init.php');
+require_once('inc/init.php');
ldap_login();
-
-/*
-echo '<[!CDATA[';
-print_r($_REQUEST);
-echo ']]>';
-*/
-
$FIELD = preg_replace('/entry\[/','',$_REQUEST['field']);
$FIELD = preg_replace('/\W+/','',$FIELD);
@@ -46,7 +39,7 @@ function ajax_addnote($dn,$note){
ldap_modify($LDAP_CON,$dn,$entry);
- require_once(dirname(__FILE__).'/smarty/plugins/modifier.noteparser.php');
+ require_once(dirname(__FILE__).'/inc/smarty/plugins/modifier.noteparser.php');
print smarty_modifier_noteparser($note);
}
diff --git a/config.php b/config.php
deleted file mode 100644
index d46910e..0000000
--- a/config.php
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
- // LDAP URL; if set, takes precedence over ldapserver and ldapport
- #$conf['ldapurl'] = 'ldaps://ldap.example.com/';
-
- // Which LDAP Server to use?
- $conf['ldapserver'] = 'ldap';
-
- // Which LDAP Port Server to use? (389 is standard, 636 for ssl)
- $conf['ldapport'] = 389;
-
- // Use LDAP protocol version 3? (0|1)
- $conf['ldapv3'] = 1;
-
- // 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))';
-
- // Use these values to bind to the ldap directory when not logged in (leave blank for anonymous bind)
- $conf['anonbinddn'] = '';
- $conf['anonbindpw'] = '';
-
- // Which language to use (see lang directory)
- $conf['lang'] = 'en';
-
- // 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 public address book be viewable by logged in users only? (0|1)
- $conf['userlogreq'] = 0;
-
- // Should the additional schema ldapab.schema be used? (0|1)
- // Note: openxchange and extended are currently exclusive, do not use both at the same time!
- $conf['extended'] = 1;
-
- // Should we use some parts of the openxchange.schema? (0|1)
- // Note: openxchange and extended are currently exclusive, do not use both at the same time!
- $conf['openxchange'] = 0;
-
- // Should we try to login using the username and password provided by httpd? (0|1)
- $conf['httpd_auth'] = 0;
-
- // 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'] = 1;
-
-
diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/doc/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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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.
+
+
+ Copyright (C)
+
+ 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.
+
+ , 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/doc/README b/doc/README
new file mode 100644
index 0000000..d37c796
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,112 @@
+=== ABOUT ===
+
+ConTagged 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. ConTagged requires an already setup LDAP server to
+authenticate users and store it's data.
+
+=== 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 PHP (4.3 or higher). PHP 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 engine's 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.
+
+If HTTP auth is wanted have a look at the provided _htaccess file, edit it as
+needed and rename it back to .htaccess
+
+=== 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 (needed for tagging and birthday support)
+you need to copy the ldapab.schema file to /etc/ldap/schema/ and include it, too.
+
+ include /etc/ldap/schema/ldapab.schema
+
+Be sure to enable the extended option in config.php.
+
+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.regex="(.*,)?ou=contacts,cn=([^,]+),ou=people,(.*)$"
+ by dn.regex="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 ConTagged 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 or visit our homepage at
+http://www.cosmocode.de
+
+=== KNOWN BUGS ===
+
+ConTagged was only tested with open-ldap 2.1 - there may be problems with other
+LDAP Servers.
+
+=== LICENSE ===
+
+ConTagged - An LDAP based Company Address Book
+Copyright 2004 - 2006 - 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/doc/TODO b/doc/TODO
new file mode 100644
index 0000000..478af17
--- /dev/null
+++ b/doc/TODO
@@ -0,0 +1,6 @@
+Any help is welcome :-)
+
+ - clean up the code
+ - clean up the HTML (make it validate)
+ - clean up the CSS (it's a mess)
+ - spice up interface (some more web2.0 colors and fonts would be a start)
diff --git a/doc/docs b/doc/docs
new file mode 100644
index 0000000..19f2060
--- /dev/null
+++ b/doc/docs
@@ -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/doc/slapd.example b/doc/slapd.example
new file mode 100644
index 0000000..cd7643a
--- /dev/null
+++ b/doc/slapd.example
@@ -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/entries.conf b/entries.conf
deleted file mode 100644
index 0809369..0000000
--- a/entries.conf
+++ /dev/null
@@ -1,20 +0,0 @@
-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
index ec73909..e224522 100644
--- a/entry.php
+++ b/entry.php
@@ -1,141 +1,136 @@
-
- require_once('init.php');
- ldap_login();
-
- $users = get_users();
-
- //select template to use
- if (empty($_REQUEST['mode'])) { $_REQUEST['mode']='show'; }
- 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';
- }
-
- if (empty($_REQUEST['dn'])) {
- $dn = "";
- }else{
- $dn = $_REQUEST['dn'];
- #$dn = 'cn=bar foo, ou=contacts, o=cosmocode, c=de';
- }
-
- //save data if asked for
- if($_SESSION['ldapab']['username'] && !empty($_REQUEST['save']) && $_REQUEST['save']){
- // prepare special data
- $_REQUEST['entry']['photo'] = _getUploadData();
- $_REQUEST['entry']['marker'] = explode(',',$_REQUEST['entry']['markers']);
- unset($_REQUEST['entry']['markers']);
-
- foreach(array_keys($_REQUEST['entry']) as $field){
- if($FIELDS['_'.$field]){
- // entry has to be handled as array -> clean it up (trim, unique, sort)
- $_REQUEST['entry'][$field] = array_map('trim',$_REQUEST['entry'][$field]);
- $_REQUEST['entry'][$field] = array_unique($_REQUEST['entry'][$field]);
- $_REQUEST['entry'][$field] = array_filter($_REQUEST['entry'][$field]);
- natcasesort($_REQUEST['entry'][$field]);
- }
- }
- $dn = _saveData();
+ clean it up (trim, unique, sort)
+ $_REQUEST['entry'][$field] = array_map('trim',$_REQUEST['entry'][$field]);
+ $_REQUEST['entry'][$field] = array_unique($_REQUEST['entry'][$field]);
+ $_REQUEST['entry'][$field] = array_filter($_REQUEST['entry'][$field]);
+ natcasesort($_REQUEST['entry'][$field]);
+ }
}
+ $dn = _saveData();
+}
- if(empty($dn)){
- if(!$_REQUEST['mode']=='edit'){
- $smarty->assign('error','No dn was given');
- $template = 'error.tpl';
- }
- }elseif(!empty($_REQUEST['del']) && $_REQUEST['del']){
- _delEntry($dn);
- }elseif(!_fetchData($dn)){
- $smarty->assign('error',"The requested entry '$dn' was not found");
+if(empty($dn)){
+ if(!$_REQUEST['mode']=='edit'){
+ $smarty->assign('error','No dn was given');
$template = 'error.tpl';
}
-
- //prepare templates
- $smarty->assign('dn',$dn);
- $smarty->assign('managers',$users);
- tpl_std();
- tpl_orgs();
- tpl_markers();
- tpl_categories();
- tpl_timezone();
- tpl_country();
- //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\"; charset=utf-8");
- $smarty->display($template);
- }else{
- header('Content-Type: text/html; charset=utf-8');
- $smarty->display($template);
+}elseif(!empty($_REQUEST['del']) && $_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();
+//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\"; charset=utf-8");
+ $smarty->display($template);
+}else{
+ header('Content-Type: text/html; charset=utf-8');
+ $smarty->display($template);
+}
+
+//--------------------------------------------------------------
+
+/**
+ * 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];
- //--------------------------------------------------------------
-
- /**
- * 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(!empty($_REQUEST['mode']) && $_REQUEST['mode'] == 'copy'){
- $entry['dn']='';
- }
+ //remove dn from entry when copy
+ if(!empty($_REQUEST['mode']) && $_REQUEST['mode'] == 'copy'){
+ $entry['dn']='';
+ }
- //assign entry to template:
- tpl_entry($entry);
+ //assign entry to template:
+ tpl_entry($entry);
/*print '
';
print_r($entry);
print '
';*/
- // make username from dn for manager:
- if (empty($entry['manager'])) { $entry['manager']=array(""); }
- if (empty($users[$entry['manager'][0]])) { $users[$entry['manager'][0]]=''; }
- $smarty->assign('managername',$users[$entry['manager'][0]]);
- return true;
+ // make username from dn for manager:
+ if (empty($entry['manager'])) { $entry['manager']=array(""); }
+ if (empty($users[$entry['manager'][0]])) { $users[$entry['manager'][0]]=''; }
+ $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;
+ global $FIELDS;
+ global $OCLASSES;
+
+ $entry = $_REQUEST['entry'];
+ $dn = $_REQUEST['dn'];
+ //construct new dn
+ $now = time();
+ $newdn = 'uid='.$now;
+ if (empty($_REQUEST['type'])) { $_REQUEST['type']='public'; }
+ if($_REQUEST['type'] == 'private'){
+ $newdn .= ', '.$conf['privatebook'].', '.$_SESSION['ldapab']['binddn'];
+ }else{
+ $newdn .= ', '.$conf['publicbook'];
}
-
- /**
- * saves the data from $_REQUEST['entry'] to the LDAP directory
- *
- * returns given or constructed dn
- */
- function _saveData(){
- global $LDAP_CON;
- global $conf;
- global $FIELDS;
- global $OCLASSES;
-
- $entry = $_REQUEST['entry'];
- $dn = $_REQUEST['dn'];
- //construct new dn
- $now = time();
- $newdn = 'uid='.$now;
- if (empty($_REQUEST['type'])) { $_REQUEST['type']='public'; }
- if($_REQUEST['type'] == 'private'){
- $newdn .= ', '.$conf['privatebook'].', '.$_SESSION['ldapab']['binddn'];
- }else{
- $newdn .= ', '.$conf['publicbook'];
- }
- $entry['displayname'] = $entry['givenname'].' '.$entry['name'];;
- $entry = prepare_ldap_entry($entry);
+ $entry['displayname'] = $entry['givenname'].' '.$entry['name'];;
+ $entry = prepare_ldap_entry($entry);
/*
print '
';
@@ -143,68 +138,68 @@ print_r($entry);
print '
';
*/
- if(empty($dn)){
- //new entry
- $entry['uid'][] = $now;
- $r = ldap_add($LDAP_CON,$newdn,$entry);
- tpl_ldaperror();
- return $newdn;
- }else{
- // update the objectClasses
- ldap_store_objectclasses($dn,$OCLASSES);
- unset($entry['objectclass']);
-
- //modify entry attribute by attribute - this ensure we don't delete unknown stuff
- foreach (array_values($FIELDS) as $key){
- if($key == 'dn'){
+ if(empty($dn)){
+ //new entry
+ $entry['uid'][] = $now;
+ $r = ldap_add($LDAP_CON,$newdn,$entry);
+ tpl_ldaperror();
+ return $newdn;
+ }else{
+ // update the objectClasses
+ ldap_store_objectclasses($dn,$OCLASSES);
+ unset($entry['objectclass']);
+
+ //modify entry attribute by attribute - this ensure we don't delete unknown stuff
+ foreach (array_values($FIELDS) as $key){
+ if($key == 'dn'){
+ continue;
+ }elseif(empty($entry[$key])){
+ // field is empty -> handle deletion (except for photo unless deletion triggered)
+ if (empty($_REQUEST['delphoto'])) { $_REQUEST['delphoto']=0; }
+ if($key == 'jpegPhoto' && !$_REQUEST['delphoto']){
continue;
- }elseif(empty($entry[$key])){
- // field is empty -> handle deletion (except for photo unless deletion triggered)
- if (empty($_REQUEST['delphoto'])) { $_REQUEST['delphoto']=0; }
- 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");
}
+ 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;
}
+ 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;
- }
+}
+
+/**
+ * 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;
- }
+}
+
+/**
+ * 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 '';
}
+ return '';
+}
diff --git a/fields.php b/fields.php
deleted file mode 100644
index 8f3d3c3..0000000
--- a/fields.php
+++ /dev/null
@@ -1,94 +0,0 @@
- 'dn', // don't touch!
- 'name' => 'sn',
- 'displayname' => 'cn',
- 'givenname' => 'givenName',
- 'title' => 'title',
- 'organization' => 'o', // aka. company
- 'office' => 'physicalDeliveryOfficeName',
- 'street' => 'postalAddress',
- 'zip' => 'postalCode',
- 'location' => 'l', // aka. city
- 'phone' => 'telephoneNumber',
- 'fax' => 'facsimileTelephoneNumber',
- 'mobile' => 'mobile', // aka. cell phone
- 'pager' => 'pager',
- 'homephone' => 'homePhone',
- 'homestreet' => 'homePostalAddress',
- 'photo' => 'jpegPhoto',
- 'url' => 'labeledURI',
- 'note' => 'description',
- 'manager' => 'manager', // aka. key account
- '_mail' => 'mail',
-);
-
-/**
- * If the provided "extended" schema is used the following fields
- * and object classes are added
- */
-$OCLASSES[] = 'contactPerson';
-$FIELDS['anniversary'] = 'anniversary';
-$FIELDS['_marker'] = 'marker'; // aka. tags
-
-/**
- * If the open exchange schema is used the following fields
- * and object classes are added
- */
-/* comment in if you want to use it
-$OCLASSES[] = 'OXUserObject';
-$FIELDS['country'] = 'userCountry';
-$FIELDS['birthday'] = 'birthDay';
-$FIELDS['ipphone'] = 'IPPhone';
-$FIELDS['_marker'] = 'OXUserCategories';
-$FIELDS['instantmessenger'] = 'OXUserInstantMessenger';
-$FIELDS['timezone'] = 'OXTimeZone';
-$FIELDS['position'] = 'OXUserPosition';
-$FIELDS['certificate'] = 'relClientCert';
-$FIELDS['domain'] = 'domain';
-*/
-
-/**
- * If the Evolution schema is used the following fields
- * and object classes are added
- */
-/* comment in if you want to use it
-$OCLASSES[] = 'evolutionPerson';
-$OCLASSES[] = 'officePerson';
-$FIELDS['department'] = 'ou';
-$FIELDS['state'] = 'st';
-$FIELDS['country'] = 'c';
-$FIELDS['phone'] = 'primaryPhone';
-$FIELDS['switchboard'] = 'companyPhone';
-$FIELDS['note'] = 'note';
-$FIELDS['manager'] = 'seeAlso';
-$FIELDS['birthday'] = 'birthDate';
-$FIELDS['spouse'] = 'spouseName';
-*/
-
-
-/**
- * Flip the array
- */
-$RFIELDS = array_flip($FIELDS);
-
diff --git a/functions.php b/functions.php
deleted file mode 100644
index 9166d31..0000000
--- a/functions.php
+++ /dev/null
@@ -1,486 +0,0 @@
-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(){
- global $conf;
- if(!empty($_SESSION['ldapab']['username'])){
- // existing session! Check if valid
- if($_SESSION['ldapab']['browserid'] != auth_browseruid()){
- //session hijacking detected
- header('Location: login.php?username=');
- exit;
- }
- } elseif ($conf['httpd_auth'] && !empty($_SERVER['PHP_AUTH_USER'])) {
- // use HTTP auth if wanted and possible
- $_SESSION['ldapab']['username'] = $_SERVER['PHP_AUTH_USER'];
- $_SESSION['ldapab']['password'] = $_SERVER['PHP_AUTH_PW'];
- } elseif (!empty($_COOKIE['ldapabauth'])) {
- // check persistent cookie
- $cookie = base64_decode($_COOKIE['ldapabauth']);
- $cookie = x_Decrypt($cookie,get_cookie_secret());
- list($u,$p) = unserialize($cookie);
- $_SESSION['ldapab']['username'] = $u;
- $_SESSION['ldapab']['password'] = $p;
- }
-
- if(empty($_SESSION['ldapab']) ||
- !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 necessary
- if(!$LDAP_CON){
- if (!empty($conf['ldapurl'])){
- $LDAP_CON = ldap_connect($conf['ldapurl']);
- }else{
- $LDAP_CON = ldap_connect($conf['ldapserver'],$conf['ldapport']);
- }
- if(!$LDAP_CON){
- die("couldn't connect to LDAP server");
- }
- }
-
- if($conf['ldapv3']) ldap_set_option($LDAP_CON, LDAP_OPT_PROTOCOL_VERSION, 3);
-
- if(empty($dn)){
- //anonymous bind to lookup users
- //blank binddn or blank bindpw will result in anonymous bind
- if(!ldap_bind($LDAP_CON,$conf['anonbinddn'],$conf['anonbindpw'])){
- die("can not bind for user lookup");
- }
-
- //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;
-}
-
-/**
- * Builds a pseudo UID from browser and IP data
- *
- * This is neither unique nor unfakable - still it adds some
- * security. Using the first part of the IP makes sure
- * proxy farms like AOLs are stil okay.
- *
- * @author Andreas Gohr
- *
- * @return string a MD5 sum of various browser headers
- */
-function auth_browseruid(){
- $uid = '';
- if (empty($_SERVER['HTTP_USER_AGENT'])) { $_SERVER['HTTP_USER_AGENT']='USER_AGENT'; }
- if (empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { $_SERVER['HTTP_ACCEPT_ENCODING']='ACCEPT_ENCODING'; }
- if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $_SERVER['HTTP_ACCEPT_LANGUAGE']='ACCEPT_LANGUAGE'; }
- if (empty($_SERVER['HTTP_ACCEPT_CHARSET'])) { $_SERVER['HTTP_ACCEPT_CHARSET']='ACCEPT_CHARSET'; }
- $uid .= $_SERVER['HTTP_USER_AGENT'];
- $uid .= $_SERVER['HTTP_ACCEPT_ENCODING'];
- $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
- $uid .= $_SERVER['HTTP_ACCEPT_CHARSET'];
- $uid .= substr($_SERVER['REMOTE_ADDR'],0,strpos($_SERVER['REMOTE_ADDR'],'.'));
- return md5($uid);
-}
-
-
-/**
- * saves user data to Session and cookies
- */
-function set_session($user,$pass,$dn){
- global $conf;
-
- $rand = rand();
- $_SESSION['ldapab']['username'] = $user;
- $_SESSION['ldapab']['binddn'] = $dn;
- $_SESSION['ldapab']['password'] = $pass;
- $_SESSION['ldapab']['browserid'] = auth_browseruid();
-
- // (re)set the persistent auth cookie
- if($user == ''){
- setcookie('ldapabauth','',time()+60*60*24*365);
- }elseif(!empty($_REQUEST['remember'])){
- $cookie = serialize(array($user,$pass));
- $cookie = x_Encrypt($cookie,get_cookie_secret());
- $cookie = base64_encode($cookie);
- setcookie('ldapabauth',$cookie,time()+60*60*24*365);
- }
-}
-
-/**
- * Creates a random string to encrypt persistent auth
- * cookies; the string is stored inside the cache dir
- */
-function get_cookie_secret(){
- $file = dirname(__FILE__).'/cache/.htcookiesecret.php';
- if(@file_exists($file)){
- return md5(trim(file($file)));
- }
-
- $secret = '';
- if(!$fh = fopen($file,'w')) die("Couldn't write to $file");
- if(fwrite($fh, $secret) === FALSE) die("Couldn't write to $file");
- fclose($fh);
-
- return md5($secret);
-}
-
-/**
- * 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
- */
-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){
- trigger_error('deprecated namedentries called',E_USER_WARNING);
-}
-
-/**
- * Creates an array for submission to ldap from websitedata
- */
-function prepare_ldap_entry($in){
- global $conf;
- global $FIELDS;
- global $OCLASSES;
-
- //check dateformats
- if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['anniversary'])) $in['anniversary']='';
- if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['birthday'])) $in['birthday']='';
-
- // we map all internal names to the configured LDAP attributes here
- foreach($in as $key => $value){
- if($FIELDS[$key]){
- // normal mapped field
- $out[$FIELDS[$key]][] = $value;
- }elseif($FIELDS["_$key"]){
- // mapped multi field
- if(is_array($value)){
- $out[$FIELDS["_$key"]] = $value;
- }else{
- $out[$FIELDS["_$key"]][] = $value; //shouldn't happen, but to be sure
- }
- }else{
- // no mapping found - assume it to be a LDAP attribute (shouldn't happen)
- if(is_array($value)){
- $out[$key] = $value;
- }else{
- $out[$key][] = $value;
- }
- }
- }
-
- // add the Objectclasses
- $out['objectclass'] = $OCLASSES;
-
- return clear_array($out);
-}
-
-/**
- * remove empty element from arrays recursively
- *
- * @author Original by
- */
-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
- */
-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);
- $users = array();
- 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((array)$set,(array)$classes));
- $add['objectClass'] = $set;
-
- $r = @ldap_mod_replace($LDAP_CON,$dn,$add);
- tpl_ldaperror();
-
-/* print '
';
- print_r($set);
- print '
';*/
-}
-
-/**
- * Escape a string to be used in a LDAP filter
- *
- * Ported from Perl's Net::LDAP::Util escape_filter_value
- *
- * @author Andreas Gohr
- */
-function ldap_filterescape($string){
- return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
- '"\\\\\".join("",unpack("H2","$1"))',
- $string);
-}
-
-/**
- * Queries public and private addressbooks, combining the
- * results
- *
- * @todo This function should be used where ever possible, replacing
- * lots of duplicate code
- */
-function ldap_queryabooks($filter,$types){
- global $conf;
- global $LDAP_CON;
-
- // make sure $types is an array
- if(!is_array($types)){
- $types = explode(',',$types);
- $types = array_map('trim',$types);
- }
-
- $results = array();
- $result1 = array();
- $result2 = array();
-
- // public addressbook
- $sr = ldap_list($LDAP_CON,$conf['publicbook'],
- $filter,$types);
- $result1 = ldap_get_binentries($LDAP_CON, $sr);
- ldap_free_result($sr);
-
- // private addressbook
- if(!empty($_SESSION['ldapab']['binddn'])){
- $sr = @ldap_list($LDAP_CON,$conf['privatebook'].
- ','.$_SESSION['ldapab']['binddn'],
- $filter,$types);
- $result2 = ldap_get_binentries($LDAP_CON, $sr);
- }
-
- // return merged results
- return array_merge((array)$result1,(array)$result2);
-}
-
-/**
- * Makes array unique and renumbers the entries
- *
- * @author
- */
-function array_unique_renumber($somearray){
- $tmparr = array_unique($somearray);
- $i=0;
- foreach ($tmparr as $v) {
- $newarr[$i] = $v;
- $i++;
- }
- return $newarr;
-}
-
-/**
- * Simple XOR encryption
- *
- * @author Dustin Schneider
- * @link http://www.phpbuilder.com/tips/item.php?id=68
- */
-function x_Encrypt($string, $key){
- for($i=0; $i
diff --git a/help.php b/help.php
index dd933da..3efcf4d 100644
--- a/help.php
+++ b/help.php
@@ -1,11 +1,11 @@
-
- require_once('init.php');
- ldap_login();
+display('help.tpl');
+//prepare templates
+tpl_std();
+//display templates
+header('Content-Type: text/html; charset=utf-8');
+$smarty->display('help.tpl');
?>
diff --git a/img.php b/img.php
index eeb4471..27a49b6 100644
--- a/img.php
+++ b/img.php
@@ -1,16 +1,16 @@
-
- require_once('init.php');
- ldap_login();
+
diff --git a/import.php b/import.php
index c26e85c..de4d3a2 100644
--- a/import.php
+++ b/import.php
@@ -1,162 +1,126 @@
- require_once('init.php');
- require_once('Contact_Vcard_Parse.php');
- require_once('xml.php');
- ldap_login();
+require_once('inc/init.php');
+require_once('inc/Contact_Vcard_Parse.php');
+ldap_login();
- if(! $_SESSION['ldapab']['username'] ){
- header("Location: login.php");
- exit;
- }
+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";
- }
+$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']);
}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');
+ $error = "Only *.vcf accepted";
}
- }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');
+ }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
- 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');
-
+//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');
+ }
+}
-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'];
+//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');
- 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);
+$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 &&
+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){
- // 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];
+ $entry['homephone'] = $tel['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
+}
+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];
- }
+ }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 '
';
- print_r($entry);
- print '
';
+print '
';
+print_r($entry);
+print '
';
*/
- return $entry;
+return $entry;
}
diff --git a/importVCF.php b/importVCF.php
deleted file mode 100644
index 1138b99..0000000
--- a/importVCF.php
+++ /dev/null
@@ -1,127 +0,0 @@
-
- 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 '
';
- print_r($entry);
- print '
';
-*/
-
- return $entry;
-}
-
-
-?>
diff --git a/inc/Contact_Vcard_Parse.php b/inc/Contact_Vcard_Parse.php
new file mode 100644
index 0000000..da1f16f
--- /dev/null
+++ b/inc/Contact_Vcard_Parse.php
@@ -0,0 +1,836 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $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:
+*
+*
+* // 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 '
';
+* print_r($cardinfo);
+* echo '
';
+*
+*
+*
+* @author Paul M. Jones
+*
+* @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 = '(? ;
+ * \, => ,
+ * 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 and his
+ * vCard PHP project .
+ *
+ * @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/inc/config.php b/inc/config.php
new file mode 100644
index 0000000..d46910e
--- /dev/null
+++ b/inc/config.php
@@ -0,0 +1,59 @@
+
+
+ // LDAP URL; if set, takes precedence over ldapserver and ldapport
+ #$conf['ldapurl'] = 'ldaps://ldap.example.com/';
+
+ // Which LDAP Server to use?
+ $conf['ldapserver'] = 'ldap';
+
+ // Which LDAP Port Server to use? (389 is standard, 636 for ssl)
+ $conf['ldapport'] = 389;
+
+ // Use LDAP protocol version 3? (0|1)
+ $conf['ldapv3'] = 1;
+
+ // 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))';
+
+ // Use these values to bind to the ldap directory when not logged in (leave blank for anonymous bind)
+ $conf['anonbinddn'] = '';
+ $conf['anonbindpw'] = '';
+
+ // Which language to use (see lang directory)
+ $conf['lang'] = 'en';
+
+ // 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 public address book be viewable by logged in users only? (0|1)
+ $conf['userlogreq'] = 0;
+
+ // Should the additional schema ldapab.schema be used? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf['extended'] = 1;
+
+ // Should we use some parts of the openxchange.schema? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf['openxchange'] = 0;
+
+ // Should we try to login using the username and password provided by httpd? (0|1)
+ $conf['httpd_auth'] = 0;
+
+ // 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'] = 1;
+
+
diff --git a/inc/fields.php b/inc/fields.php
new file mode 100644
index 0000000..8f3d3c3
--- /dev/null
+++ b/inc/fields.php
@@ -0,0 +1,94 @@
+ 'dn', // don't touch!
+ 'name' => 'sn',
+ 'displayname' => 'cn',
+ 'givenname' => 'givenName',
+ 'title' => 'title',
+ 'organization' => 'o', // aka. company
+ 'office' => 'physicalDeliveryOfficeName',
+ 'street' => 'postalAddress',
+ 'zip' => 'postalCode',
+ 'location' => 'l', // aka. city
+ 'phone' => 'telephoneNumber',
+ 'fax' => 'facsimileTelephoneNumber',
+ 'mobile' => 'mobile', // aka. cell phone
+ 'pager' => 'pager',
+ 'homephone' => 'homePhone',
+ 'homestreet' => 'homePostalAddress',
+ 'photo' => 'jpegPhoto',
+ 'url' => 'labeledURI',
+ 'note' => 'description',
+ 'manager' => 'manager', // aka. key account
+ '_mail' => 'mail',
+);
+
+/**
+ * If the provided "extended" schema is used the following fields
+ * and object classes are added
+ */
+$OCLASSES[] = 'contactPerson';
+$FIELDS['anniversary'] = 'anniversary';
+$FIELDS['_marker'] = 'marker'; // aka. tags
+
+/**
+ * If the open exchange schema is used the following fields
+ * and object classes are added
+ */
+/* comment in if you want to use it
+$OCLASSES[] = 'OXUserObject';
+$FIELDS['country'] = 'userCountry';
+$FIELDS['birthday'] = 'birthDay';
+$FIELDS['ipphone'] = 'IPPhone';
+$FIELDS['_marker'] = 'OXUserCategories';
+$FIELDS['instantmessenger'] = 'OXUserInstantMessenger';
+$FIELDS['timezone'] = 'OXTimeZone';
+$FIELDS['position'] = 'OXUserPosition';
+$FIELDS['certificate'] = 'relClientCert';
+$FIELDS['domain'] = 'domain';
+*/
+
+/**
+ * If the Evolution schema is used the following fields
+ * and object classes are added
+ */
+/* comment in if you want to use it
+$OCLASSES[] = 'evolutionPerson';
+$OCLASSES[] = 'officePerson';
+$FIELDS['department'] = 'ou';
+$FIELDS['state'] = 'st';
+$FIELDS['country'] = 'c';
+$FIELDS['phone'] = 'primaryPhone';
+$FIELDS['switchboard'] = 'companyPhone';
+$FIELDS['note'] = 'note';
+$FIELDS['manager'] = 'seeAlso';
+$FIELDS['birthday'] = 'birthDate';
+$FIELDS['spouse'] = 'spouseName';
+*/
+
+
+/**
+ * Flip the array
+ */
+$RFIELDS = array_flip($FIELDS);
+
diff --git a/inc/functions.php b/inc/functions.php
new file mode 100644
index 0000000..9166d31
--- /dev/null
+++ b/inc/functions.php
@@ -0,0 +1,486 @@
+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(){
+ global $conf;
+ if(!empty($_SESSION['ldapab']['username'])){
+ // existing session! Check if valid
+ if($_SESSION['ldapab']['browserid'] != auth_browseruid()){
+ //session hijacking detected
+ header('Location: login.php?username=');
+ exit;
+ }
+ } elseif ($conf['httpd_auth'] && !empty($_SERVER['PHP_AUTH_USER'])) {
+ // use HTTP auth if wanted and possible
+ $_SESSION['ldapab']['username'] = $_SERVER['PHP_AUTH_USER'];
+ $_SESSION['ldapab']['password'] = $_SERVER['PHP_AUTH_PW'];
+ } elseif (!empty($_COOKIE['ldapabauth'])) {
+ // check persistent cookie
+ $cookie = base64_decode($_COOKIE['ldapabauth']);
+ $cookie = x_Decrypt($cookie,get_cookie_secret());
+ list($u,$p) = unserialize($cookie);
+ $_SESSION['ldapab']['username'] = $u;
+ $_SESSION['ldapab']['password'] = $p;
+ }
+
+ if(empty($_SESSION['ldapab']) ||
+ !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 necessary
+ if(!$LDAP_CON){
+ if (!empty($conf['ldapurl'])){
+ $LDAP_CON = ldap_connect($conf['ldapurl']);
+ }else{
+ $LDAP_CON = ldap_connect($conf['ldapserver'],$conf['ldapport']);
+ }
+ if(!$LDAP_CON){
+ die("couldn't connect to LDAP server");
+ }
+ }
+
+ if($conf['ldapv3']) ldap_set_option($LDAP_CON, LDAP_OPT_PROTOCOL_VERSION, 3);
+
+ if(empty($dn)){
+ //anonymous bind to lookup users
+ //blank binddn or blank bindpw will result in anonymous bind
+ if(!ldap_bind($LDAP_CON,$conf['anonbinddn'],$conf['anonbindpw'])){
+ die("can not bind for user lookup");
+ }
+
+ //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;
+}
+
+/**
+ * Builds a pseudo UID from browser and IP data
+ *
+ * This is neither unique nor unfakable - still it adds some
+ * security. Using the first part of the IP makes sure
+ * proxy farms like AOLs are stil okay.
+ *
+ * @author Andreas Gohr
+ *
+ * @return string a MD5 sum of various browser headers
+ */
+function auth_browseruid(){
+ $uid = '';
+ if (empty($_SERVER['HTTP_USER_AGENT'])) { $_SERVER['HTTP_USER_AGENT']='USER_AGENT'; }
+ if (empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { $_SERVER['HTTP_ACCEPT_ENCODING']='ACCEPT_ENCODING'; }
+ if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $_SERVER['HTTP_ACCEPT_LANGUAGE']='ACCEPT_LANGUAGE'; }
+ if (empty($_SERVER['HTTP_ACCEPT_CHARSET'])) { $_SERVER['HTTP_ACCEPT_CHARSET']='ACCEPT_CHARSET'; }
+ $uid .= $_SERVER['HTTP_USER_AGENT'];
+ $uid .= $_SERVER['HTTP_ACCEPT_ENCODING'];
+ $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+ $uid .= $_SERVER['HTTP_ACCEPT_CHARSET'];
+ $uid .= substr($_SERVER['REMOTE_ADDR'],0,strpos($_SERVER['REMOTE_ADDR'],'.'));
+ return md5($uid);
+}
+
+
+/**
+ * saves user data to Session and cookies
+ */
+function set_session($user,$pass,$dn){
+ global $conf;
+
+ $rand = rand();
+ $_SESSION['ldapab']['username'] = $user;
+ $_SESSION['ldapab']['binddn'] = $dn;
+ $_SESSION['ldapab']['password'] = $pass;
+ $_SESSION['ldapab']['browserid'] = auth_browseruid();
+
+ // (re)set the persistent auth cookie
+ if($user == ''){
+ setcookie('ldapabauth','',time()+60*60*24*365);
+ }elseif(!empty($_REQUEST['remember'])){
+ $cookie = serialize(array($user,$pass));
+ $cookie = x_Encrypt($cookie,get_cookie_secret());
+ $cookie = base64_encode($cookie);
+ setcookie('ldapabauth',$cookie,time()+60*60*24*365);
+ }
+}
+
+/**
+ * Creates a random string to encrypt persistent auth
+ * cookies; the string is stored inside the cache dir
+ */
+function get_cookie_secret(){
+ $file = dirname(__FILE__).'/cache/.htcookiesecret.php';
+ if(@file_exists($file)){
+ return md5(trim(file($file)));
+ }
+
+ $secret = '';
+ if(!$fh = fopen($file,'w')) die("Couldn't write to $file");
+ if(fwrite($fh, $secret) === FALSE) die("Couldn't write to $file");
+ fclose($fh);
+
+ return md5($secret);
+}
+
+/**
+ * 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
+ */
+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){
+ trigger_error('deprecated namedentries called',E_USER_WARNING);
+}
+
+/**
+ * Creates an array for submission to ldap from websitedata
+ */
+function prepare_ldap_entry($in){
+ global $conf;
+ global $FIELDS;
+ global $OCLASSES;
+
+ //check dateformats
+ if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['anniversary'])) $in['anniversary']='';
+ if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['birthday'])) $in['birthday']='';
+
+ // we map all internal names to the configured LDAP attributes here
+ foreach($in as $key => $value){
+ if($FIELDS[$key]){
+ // normal mapped field
+ $out[$FIELDS[$key]][] = $value;
+ }elseif($FIELDS["_$key"]){
+ // mapped multi field
+ if(is_array($value)){
+ $out[$FIELDS["_$key"]] = $value;
+ }else{
+ $out[$FIELDS["_$key"]][] = $value; //shouldn't happen, but to be sure
+ }
+ }else{
+ // no mapping found - assume it to be a LDAP attribute (shouldn't happen)
+ if(is_array($value)){
+ $out[$key] = $value;
+ }else{
+ $out[$key][] = $value;
+ }
+ }
+ }
+
+ // add the Objectclasses
+ $out['objectclass'] = $OCLASSES;
+
+ return clear_array($out);
+}
+
+/**
+ * remove empty element from arrays recursively
+ *
+ * @author Original by
+ */
+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
+ */
+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);
+ $users = array();
+ 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((array)$set,(array)$classes));
+ $add['objectClass'] = $set;
+
+ $r = @ldap_mod_replace($LDAP_CON,$dn,$add);
+ tpl_ldaperror();
+
+/* print '
';
+ print_r($set);
+ print '
';*/
+}
+
+/**
+ * Escape a string to be used in a LDAP filter
+ *
+ * Ported from Perl's Net::LDAP::Util escape_filter_value
+ *
+ * @author Andreas Gohr
+ */
+function ldap_filterescape($string){
+ return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
+ '"\\\\\".join("",unpack("H2","$1"))',
+ $string);
+}
+
+/**
+ * Queries public and private addressbooks, combining the
+ * results
+ *
+ * @todo This function should be used where ever possible, replacing
+ * lots of duplicate code
+ */
+function ldap_queryabooks($filter,$types){
+ global $conf;
+ global $LDAP_CON;
+
+ // make sure $types is an array
+ if(!is_array($types)){
+ $types = explode(',',$types);
+ $types = array_map('trim',$types);
+ }
+
+ $results = array();
+ $result1 = array();
+ $result2 = array();
+
+ // public addressbook
+ $sr = ldap_list($LDAP_CON,$conf['publicbook'],
+ $filter,$types);
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ ldap_free_result($sr);
+
+ // private addressbook
+ if(!empty($_SESSION['ldapab']['binddn'])){
+ $sr = @ldap_list($LDAP_CON,$conf['privatebook'].
+ ','.$_SESSION['ldapab']['binddn'],
+ $filter,$types);
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+
+ // return merged results
+ return array_merge((array)$result1,(array)$result2);
+}
+
+/**
+ * Makes array unique and renumbers the entries
+ *
+ * @author
+ */
+function array_unique_renumber($somearray){
+ $tmparr = array_unique($somearray);
+ $i=0;
+ foreach ($tmparr as $v) {
+ $newarr[$i] = $v;
+ $i++;
+ }
+ return $newarr;
+}
+
+/**
+ * Simple XOR encryption
+ *
+ * @author Dustin Schneider
+ * @link http://www.phpbuilder.com/tips/item.php?id=68
+ */
+function x_Encrypt($string, $key){
+ for($i=0; $i
diff --git a/inc/importVCF.php b/inc/importVCF.php
new file mode 100644
index 0000000..1138b99
--- /dev/null
+++ b/inc/importVCF.php
@@ -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 '
URL => enable debugging when SMARTY_DEBUG is found in the URL.
+ *
+ * @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.
+ *
+ *
0 = no caching
+ *
1 = use class cache_lifetime value
+ *
2 = use cache_lifetime in cache file
+ *
+ * @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.
+ *
+ *
0 = always regenerate cache
+ *
-1 = never expires
+ *
+ *
+ * @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 "" tags in templates.
+ * possible values:
+ *
+ *
SMARTY_PHP_PASSTHRU -> print tags as plain text
+ *
SMARTY_PHP_QUOTE -> escape tags as entities
+ *
SMARTY_PHP_REMOVE -> remove php tags
+ *
SMARTY_PHP_ALLOW -> execute php tags
+ *
+ *
+ * @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', 'null'),
+ '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 = true;
+
+ /**
+ * 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 = false;
+
+ /**
+ * 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: array('escape:"htmlall"');
+ *
+ * @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;
+
+ /**
+ * 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
+ */
+ /**
+ * 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.18';
+
+ /**
+ * 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.
+ */
+ function Smarty()
+ {
+ $this->assign('SCRIPT_NAME', isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME']
+ : @$GLOBALS['HTTP_SERVER_VARS']['SCRIPT_NAME']);
+ }
+
+ /**
+ * 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_CORE_DIR . '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_CORE_DIR . '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)
+ {
+ return $this->clear_cache(null, null, null, $exp_time);
+ }
+
+
+ /**
+ * 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_CORE_DIR . '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_CORE_DIR . '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;
+ } elseif(isset($this->_tpl_vars[$name])) {
+ return $this->_tpl_vars[$name];
+ } else {
+ // var non-existant, return valid reference
+ $_tmp = null;
+ return $_tmp;
+ }
+ }
+
+ /**
+ * 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];
+ } else {
+ // var non-existant, return valid reference
+ $_tmp = null;
+ return $_tmp;
+ }
+ }
+
+ /**
+ * 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(isset($this->error_reporting)
+ ? $this->error_reporting : error_reporting() & ~E_NOTICE);
+
+ if (!$this->debugging && $this->debugging_ctrl == 'URL') {
+ $_query_string = $this->request_use_auto_globals ? $_SERVER['QUERY_STRING'] : $GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];
+ if (@strstr($_query_string, $this->_smarty_debug_id)) {
+ if (@strstr($_query_string, $this->_smarty_debug_id . '=on')) {
+ // enable debugging for this browser session
+ @setcookie('SMARTY_DEBUG', true);
+ $this->debugging = true;
+ } elseif (@strstr($_query_string, $this->_smarty_debug_id . '=off')) {
+ // disable debugging for this browser session
+ @setcookie('SMARTY_DEBUG', false);
+ $this->debugging = false;
+ } else {
+ // enable debugging for this page
+ $this->debugging = true;
+ }
+ } else {
+ $this->debugging = (bool)($this->request_use_auto_globals ? @$_COOKIE['SMARTY_DEBUG'] : @$GLOBALS['HTTP_COOKIE_VARS']['SMARTY_DEBUG']);
+ }
+ }
+
+ if ($this->debugging) {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . '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_CORE_DIR . 'core.read_cache_file.php');
+ if (smarty_core_read_cache_file($_params, $this)) {
+ $_smarty_results = $_params['results'];
+ if (!empty($this->_cache_info['insert_tags'])) {
+ $_params = array('plugins' => $this->_cache_info['insert_tags']);
+ require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ $_params = array('results' => $_smarty_results);
+ require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
+ $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
+ }
+ if (!empty($this->_cache_info['cache_serials'])) {
+ $_params = array('results' => $_smarty_results);
+ require_once(SMARTY_CORE_DIR . '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_CORE_DIR . '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_CORE_DIR . 'core.display_debug_console.php');
+ $_smarty_results .= smarty_core_display_debug_console($_params, $this);
+ }
+ if ($this->cache_modified_check) {
+ $_server_vars = ($this->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+ $_last_modified_date = @substr($_server_vars['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_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 && $display) {
+ 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_CORE_DIR . 'core.write_cache_file.php');
+ smarty_core_write_cache_file($_params, $this);
+ require_once(SMARTY_CORE_DIR . '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_CORE_DIR . '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_CORE_DIR . '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_CORE_DIR . '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)) {
+ 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'];
+ $_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_CORE_DIR . 'core.write_compiled_include.php');
+ smarty_core_write_compiled_include(array_merge($this->_cache_include_info, array('compiled_content'=>$_compiled_content, 'resource_name'=>$resource_name)), $this);
+ }
+
+ $_params = array('compile_path'=>$compile_path, 'compiled_content' => $_compiled_content);
+ require_once(SMARTY_CORE_DIR . 'core.write_compiled_resource.php');
+ smarty_core_write_compiled_resource($_params, $this);
+
+ return true;
+ } else {
+ 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->use_sub_dirs = $this->use_sub_dirs;
+ $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;
+
+ if (isset($cache_include_path) && isset($this->_cache_serials[$cache_include_path])) {
+ $smarty_compiler->_cache_serial = $this->_cache_serials[$cache_include_path];
+ }
+ $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'];
+ else
+ $_params['resource_base_path'] = $this->template_dir;
+
+ 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_CORE_DIR . '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
+ foreach ((array)$params['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_CORE_DIR . 'core.get_include_path.php');
+ if(smarty_core_get_include_path($_params, $this)) {
+ $params['resource_name'] = $_params['new_file_path'];
+ return true;
+ }
+ }
+ return false;
+ } else {
+ /* absolute path */
+ return file_exists($params['resource_name']);
+ }
+ } elseif (empty($this->_plugins['resource'][$params['resource_type']])) {
+ $_params = array('type' => $params['resource_type']);
+ require_once(SMARTY_CORE_DIR . '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 ((substr($string, 0, 1) == "'" || substr($string, 0, 1) == '"') &&
+ substr($string, -1) == substr($string, 0, 1))
+ return substr($string, 1, -1);
+ else
+ return $string;
+ }
+
+
+ /**
+ * read in a file
+ *
+ * @param string $filename
+ * @return string
+ */
+ function _read_file($filename)
+ {
+ if ( file_exists($filename) && ($fd = @fopen($filename, 'rb')) ) {
+ $contents = '';
+ while (!feof($fd)) {
+ $contents .= fread($fd, 8192);
+ }
+ fclose($fd);
+ return $contents;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 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 : '^';
+ $_return = $auto_base . DIRECTORY_SEPARATOR;
+
+ 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 = sprintf('%08X', crc32($auto_source));
+ // prepend %% to avoid name conflicts with
+ // with $params['auto_id'] names
+ $_crc32 = substr($_crc32, 0, 2) . $_compile_dir_sep .
+ 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 = '';
+ }
+ if (isset($tpl_line) && isset($tpl_file)) {
+ $this->trigger_error('[in ' . $tpl_file . ' line ' . $tpl_line . "]: $error_msg$info", $error_type);
+ } else {
+ $this->trigger_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_CORE_DIR . '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_CORE_DIR . '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/inc/smarty/Smarty_Compiler.class.php b/inc/smarty/Smarty_Compiler.class.php
new file mode 100644
index 0000000..f54cc21
--- /dev/null
+++ b/inc/smarty/Smarty_Compiler.class.php
@@ -0,0 +1,2327 @@
+
+ * @author Andrei Zmievski
+ * @version 2.6.18
+ * @copyright 2001-2005 New Digital Group, Inc.
+ * @package Smarty
+ */
+
+/* $Id: Smarty_Compiler.class.php,v 1.395 2007/03/06 10:40:06 messju Exp $ */
+
+/**
+ * Template compiling class
+ * @package Smarty
+ */
+class Smarty_Compiler extends Smarty {
+
+ // internal vars
+ /**#@+
+ * @access private
+ */
+ var $_folded_blocks = array(); // keeps folded template 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 $_reg_obj_regexp = null;
+ var $_var_bracket_regexp = null;
+ var $_num_const_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 numerical constants
+ // 30
+ // -12
+ // 13.22
+ $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
+
+ // 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_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
+ . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $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 (one level of object nesting 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()
+ // $foo->bar($foo->bar)
+ // $foo->bar($foo->bar())
+ // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
+ $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
+ $this->_obj_restricted_param_regexp = '(?:'
+ . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
+ . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
+ $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
+ . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
+ $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
+ . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
+ $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
+ $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_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->_num_const_regexp . '|'
+ . $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 . '|' . $this->_num_const_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");
+ }
+ }
+ }
+
+ /* fetch all special blocks */
+ $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
+
+ preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
+ $this->_folded_blocks = $match;
+ reset($this->_folded_blocks);
+
+ /* replace special blocks by "{php}" */
+ $source_content = preg_replace($search.'e', "'"
+ . $this->_quote_replace($this->left_delimiter) . 'php'
+ . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
+ . $this->_quote_replace($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*[\"\']?\s*php\s*[\"\']?)~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.'%%%', ''."\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', ''."\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");
+ }
+ if (count($this->_tag_stack)>0) {
+ list($_open_tag, $_line_no) = end($this->_tag_stack);
+ $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
+ return;
+ }
+
+ /* Reformat $text_blocks between 'strip' and '/strip' tags,
+ removing spaces, tabs and newlines. */
+ $strip = false;
+ for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
+ if ($compiled_tags[$i] == '{strip}') {
+ $compiled_tags[$i] = '';
+ $strip = true;
+ /* remove leading whitespaces */
+ $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
+ }
+ if ($strip) {
+ /* strip all $text_blocks before the next '/strip' */
+ for ($j = $i + 1; $j < $for_max; $j++) {
+ /* remove leading and trailing whitespaces of each line */
+ $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
+ if ($compiled_tags[$j] == '{/strip}') {
+ /* remove trailing whitespaces from the last text_block */
+ $text_blocks[$j] = rtrim($text_blocks[$j]);
+ }
+ $text_blocks[$j] = ""\'", "\\"=>"\\\\")) . "'; ?>";
+ if ($compiled_tags[$j] == '{/strip}') {
+ $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
+ if a newline is following the closing strip-tag */
+ $strip = false;
+ $i = $j;
+ break;
+ }
+ }
+ }
+ }
+ $compiled_content = '';
+
+ $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
+
+ /* 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]);
+ }
+ // replace legit PHP tags with placeholder
+ $text_blocks[$i] = str_replace('', $tag_guard, $text_blocks[$i]);
+ $compiled_tags[$i] = str_replace('', $tag_guard, $compiled_tags[$i]);
+
+ $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
+ }
+ $compiled_content .= str_replace('', $tag_guard, $text_blocks[$i]);
+
+ // escape php tags created by interleaving
+ $compiled_content = str_replace('', "\n", $compiled_content);
+ $compiled_content = preg_replace("~(?\n", $compiled_content);
+
+ // recover legit tags
+ $compiled_content = str_replace($tag_guard, '', $compiled_content);
+
+ // remove \n from the end of the file, if any
+ if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
+ $compiled_content = substr($compiled_content, 0, -1);
+ }
+
+ if (!empty($this->_cache_serial)) {
+ $compiled_content = "_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $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 = "_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', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
+ $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
+ }
+ }
+ $_plugins_params .= '))';
+ $plugins_code = "\n";
+ $template_header .= $plugins_code;
+ $this->_plugin_info = array();
+ $this->_plugins_code = $plugins_code;
+ }
+
+ if ($this->_init_smarty_vars) {
+ $template_header .= "\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 (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
+ return '';
+
+ /* Split tag into two three parts: command, command modifiers and the arguments. */
+ if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $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->_num_const_regexp . '|' . $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);
+ 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':
+ $this->_push_tag('if');
+ return $this->_compile_if_tag($tag_args);
+
+ case 'else':
+ list($_open_tag) = end($this->_tag_stack);
+ if ($_open_tag != 'if' && $_open_tag != 'elseif')
+ $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
+ else
+ $this->_push_tag('else');
+ return '';
+
+ case 'elseif':
+ list($_open_tag) = end($this->_tag_stack);
+ if ($_open_tag != 'if' && $_open_tag != 'elseif')
+ $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
+ if ($_open_tag == 'if')
+ $this->_push_tag('elseif');
+ return $this->_compile_if_tag($tag_args, true);
+
+ case '/if':
+ $this->_pop_tag('if');
+ return '';
+
+ 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':
+ $this->_push_tag('section');
+ return $this->_compile_section_start($tag_args);
+
+ case 'sectionelse':
+ $this->_push_tag('sectionelse');
+ return "";
+ break;
+
+ case '/section':
+ $_open_tag = $this->_pop_tag('section');
+ if ($_open_tag == 'sectionelse')
+ return "";
+ else
+ return "";
+
+ case 'foreach':
+ $this->_push_tag('foreach');
+ return $this->_compile_foreach_start($tag_args);
+ break;
+
+ case 'foreachelse':
+ $this->_push_tag('foreachelse');
+ return "";
+
+ case '/foreach':
+ $_open_tag = $this->_pop_tag('foreach');
+ if ($_open_tag == 'foreachelse')
+ return "";
+ else
+ return "";
+ break;
+
+ case 'strip':
+ case '/strip':
+ if (substr($tag_command, 0, 1)=='/') {
+ $this->_pop_tag('strip');
+ if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
+ $this->_additional_newline = "\n";
+ return '{' . $tag_command . '}';
+ }
+ } else {
+ $this->_push_tag('strip');
+ if ($this->_strip_depth++==0) { /* outermost opening {strip} */
+ $this->_additional_newline = "";
+ return '{' . $tag_command . '}';
+ }
+ }
+ return '';
+
+ case 'php':
+ /* handle folded tags replaced by {php} */
+ list(, $block) = each($this->_folded_blocks);
+ $this->_current_line_no += substr_count($block[0], "\n");
+ /* the number of matched elements in the regexp in _compile_file()
+ determins the type of folded tag that was found */
+ switch (count($block)) {
+ case 2: /* comment */
+ return '';
+
+ case 3: /* literal */
+ return ""\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
+
+ case 4: /* php */
+ if ($this->security && !$this->security_settings['PHP_TAGS']) {
+ $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
+ return;
+ }
+ return '';
+ }
+ break;
+
+ 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 if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
+ return $output;
+ } else {
+ $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ }
+ }
+
+
+ /**
+ * 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 = '_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 (substr($tag_command, 0, 1) == '/') {
+ $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)
+ $this->_push_tag($tag_command);
+ else
+ $this->_pop_tag($tag_command);
+
+ if ($start_tag) {
+ $output = '_push_cacheable_state('block', $tag_command);
+ $attrs = $this->_parse_attrs($tag_args);
+ $_cache_attrs='';
+ $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
+ $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
+ $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
+ $output .= 'while ($_block_repeat) { ob_start(); ?>';
+ } else {
+ $output = '_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
+ if ($tag_modifier != '') {
+ $this->_parse_modifiers($_out_tag_text, $tag_modifier);
+ }
+ $output .= '$_block_repeat=false;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, &$output)
+ {
+ $found = false;
+ $have_function = true;
+
+ /*
+ * First we check if the custom function has already been registered
+ * or loaded from a plugin file.
+ */
+ if (isset($this->_plugins['function'][$tag_command])) {
+ $found = true;
+ $plugin_func = $this->_plugins['function'][$tag_command][0];
+ if (!is_callable($plugin_func)) {
+ $message = "custom 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('function', $tag_command)) {
+ $found = true;
+
+ include_once $plugin_file;
+
+ $plugin_func = 'smarty_function_' . $tag_command;
+ if (!function_exists($plugin_func)) {
+ $message = "plugin function $plugin_func() not found in $plugin_file\n";
+ $have_function = false;
+ } else {
+ $this->_plugins['function'][$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;
+ }
+
+ /* declare plugin to be loaded on display of the template that
+ we compile right now */
+ $this->_add_plugin('function', $tag_command);
+
+ $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
+ $attrs = $this->_parse_attrs($tag_args);
+ $_cache_attrs = '';
+ $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
+
+ $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
+ if($tag_modifier != '') {
+ $this->_parse_modifiers($output, $tag_modifier);
+ }
+
+ if($output != '') {
+ $output = '_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
+ }
+
+ return true;
+ }
+
+ /**
+ * 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 (substr($tag_command, 0, 1) == '/') {
+ $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" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } 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'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } 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 .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
+ $prefix .= "while (\$_block_repeat) { ob_start();";
+ $return = null;
+ $postfix = '';
+ } else {
+ $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
+ $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
+ $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 '" . $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)) {
+ return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (!preg_match('~^\w+$~', $name)) {
+ return $this->_syntax_error("'insert: 'name' must be an insert function 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 "" . $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 = '_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';
+
+ $arg_list = array();
+ 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(',', $arg_list)."))";
+
+ return "" . $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 = '_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ $output .= "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'])) {
+ return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $from = $attrs['from'];
+
+ if (empty($attrs['item'])) {
+ return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $item = $this->_dequote($attrs['item']);
+ if (!preg_match('~^\w+$~', $item)) {
+ return $this->_syntax_error("'foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (isset($attrs['key'])) {
+ $key = $this->_dequote($attrs['key']);
+ if (!preg_match('~^\w+$~', $key)) {
+ return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $key_part = "\$this->_tpl_vars['$key'] => ";
+ } else {
+ $key = null;
+ $key_part = '';
+ }
+
+ if (isset($attrs['name'])) {
+ $name = $attrs['name'];
+ } else {
+ $name = null;
+ }
+
+ $output = '_foreach[$name]";
+ $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
+ $output .= "if ({$foreach_props}['total'] > 0):\n";
+ $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
+ $output .= " {$foreach_props}['iteration']++;\n";
+ } else {
+ $output .= "if (count(\$_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 = "";
+ $this->_capture_stack[] = array($buffer, $assign);
+ } else {
+ list($buffer, $assign) = array_pop($this->_capture_stack);
+ $output = "_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];
+
+ if(empty($tokens)) {
+ $_error_msg = $elseif ? "'elseif'" : "'if'";
+ $_error_msg .= ' statement requires arguments';
+ $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+
+ // 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->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
+ // variable function call
+ $this->_syntax_error("variable function call '$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 '';
+ else
+ return '';
+ }
+
+
+ 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
+ $expr_end++;
+ $expr_arg = $tokens[$expr_end++];
+ $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
+ } else
+ $expr = "!(1 & $is_arg)";
+ break;
+
+ case 'odd':
+ if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
+ $expr_end++;
+ $expr_arg = $tokens[$expr_end++];
+ $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
+ } else
+ $expr = "(1 & $is_arg)";
+ 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('~^' . $this->_num_const_regexp . '|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]);
+ $modifiers = $match[2];
+ if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
+ $_default_mod_string = implode('|',(array)$this->default_modifiers);
+ $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
+ }
+ $this->_parse_modifiers($return, $modifiers);
+ 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->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // numerical constant
+ preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
+ if($match[2] != '') {
+ $this->_parse_modifiers($match[1], $match[2]);
+ return $match[1];
+ }
+ }
+ 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('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
+ }
+ 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('~(?:\`(?_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?_parse_var(str_replace('`','',$_var)) . ')."';
+ }
+ $var_expr = strtr($var_expr, $_replace);
+ $_return = preg_replace('~\.""|(?_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ if(count($_math_vars) > 1) {
+ $_first_var = "";
+ $_complete_var = "";
+ $_output = "";
+ // 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 {
+ $_complete_var .= $_math_var;
+ }
+ }
+ }
+ if($_has_math) {
+ if(!empty($_complete_var) || is_numeric($_complete_var))
+ $_output .= $this->_parse_var($_complete_var);
+
+ // 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(substr($var_expr, 0, 1)))
+ $_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(substr($var_expr, 0, 1))) {
+ // 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 (substr($_index, 0, 1) == '[') {
+ $_index = substr($_index, 1, -1);
+ if (is_numeric($_index)) {
+ $_output .= "[$_index]";
+ } elseif (substr($_index, 0, 1) == '$') {
+ 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 (substr($_index, 0, 1) == '.') {
+ if (substr($_index, 1, 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 (substr($_index, 2, 1) == '$') {
+ 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 (substr($_index, 0, 1) == '(') {
+ $_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);
+ $orig_vals = $match = $match[0];
+ $this->_parse_vars_props($match);
+ $replace = array();
+ for ($i = 0, $count = count($match); $i < $count; $i++) {
+ $replace[$orig_vals[$i]] = $match[$i];
+ }
+ return strtr($parenth_args, $replace);
+ }
+
+ /**
+ * 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 (substr($_modifier_name, 0, 1) == '@') {
+ $_map_array = false;
+ $_modifier_name = substr($_modifier_name, 1);
+ } else {
+ $_map_array = true;
+ }
+
+ 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" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } else {
+ $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
+ }
+ }
+ $this->_add_plugin('modifier', $_modifier_name);
+
+ $this->_parse_vars_props($_modifier_args);
+
+ if($_modifier_name == 'default') {
+ // supress notifications of default modifier vars and args
+ if(substr($output, 0, 1) == '$') {
+ $output = '@' . $output;
+ }
+ if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
+ $_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 (substr($_index, 0, 1) != '.' && $_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':
+ array_shift($indexes);
+ $_var = $this->_parse_var_props(substr($indexes[0], 1));
+ $_propname = substr($indexes[1], 1);
+ $_max_index = 1;
+ switch ($_propname) {
+ case 'index':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
+ break;
+
+ case 'first':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
+ break;
+
+ case 'last':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
+ break;
+
+ case 'show':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
+ break;
+
+ default:
+ unset($_max_index);
+ $compiled_ref = "\$this->_foreach[$_var]";
+ }
+ break;
+
+ case 'section':
+ array_shift($indexes);
+ $_var = $this->_parse_var_props(substr($indexes[0], 1));
+ $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':
+ if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
+ $this->_syntax_error("(secure mode) constants not permitted",
+ E_USER_WARNING, __FILE__, __LINE__);
+ return;
+ }
+ array_shift($indexes);
+ if (preg_match('!^\.\w+$!', $indexes[0])) {
+ $compiled_ref = '@' . substr($indexes[0], 1);
+ } else {
+ $_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 = 3;
+ break;
+
+ case 'ldelim':
+ $compiled_ref = "'$this->left_delimiter'";
+ break;
+
+ case 'rdelim':
+ $compiled_ref = "'$this->right_delimiter'";
+ 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_CORE_DIR . '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_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Quote subpattern references
+ *
+ * @param string $string
+ * @return string
+ */
+ function _quote_replace($string)
+ {
+ return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
+ }
+
+ /**
+ * 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)
+ {
+ $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $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 && !$this->_cache_including): echo \'{nocache:'
+ . $this->_cache_serial . '#' . $this->_nocache_count
+ . '}\'; endif;';
+ 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 && !$this->_cache_including): echo \'{/nocache:'
+ . $this->_cache_serial . '#' . ($this->_nocache_count++)
+ . '}\'; endif;';
+ }
+
+
+ /**
+ * push opening tag-name, file-name and line-number on the tag-stack
+ * @param string the opening tag's name
+ */
+ function _push_tag($open_tag)
+ {
+ array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
+ }
+
+ /**
+ * pop closing tag-name
+ * raise an error if this stack-top doesn't match with the closing tag
+ * @param string the closing tag's name
+ * @return string the opening tag's name
+ */
+ function _pop_tag($close_tag)
+ {
+ $message = '';
+ if (count($this->_tag_stack)>0) {
+ list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
+ if ($close_tag == $_open_tag) {
+ return $_open_tag;
+ }
+ if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
+ return $this->_pop_tag($close_tag);
+ }
+ if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
+ $this->_pop_tag($close_tag);
+ return $_open_tag;
+ }
+ if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
+ $this->_pop_tag($close_tag);
+ return $_open_tag;
+ }
+ if ($_open_tag == 'else' || $_open_tag == 'elseif') {
+ $_open_tag = 'if';
+ } elseif ($_open_tag == 'sectionelse') {
+ $_open_tag = 'section';
+ } elseif ($_open_tag == 'foreachelse') {
+ $_open_tag = 'foreach';
+ }
+ $message = " expected {/$_open_tag} (opened line $_line_no).";
+ }
+ $this->_syntax_error("mismatched tag {/$close_tag}.$message",
+ E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+}
+
+/**
+ * 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/inc/smarty/debug.tpl b/inc/smarty/debug.tpl
new file mode 100644
index 0000000..c05ef5d
--- /dev/null
+++ b/inc/smarty/debug.tpl
@@ -0,0 +1,157 @@
+{* Smarty *}
+{* debug.tpl, last updated version 2.1.0 *}
+{assign_debug_info}
+{capture assign=debug_output}
+
+
+
+ Smarty Debug Console
+{literal}
+
+{/literal}
+
+
+
+
Smarty Debug Console
+
+
included templates & config files (load time in seconds)