summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorschneefux <schneefux+commit@schneefux.xyz>2016-06-16 21:06:38 +0200
committerschneefux <schneefux+commit@schneefux.xyz>2016-06-16 21:06:38 +0200
commit7627939253a431578e79cb0d157d552f15d802a1 (patch)
tree9488ee68ee4a5a827e0225a191e44396cc098138
downloadoc-pam-7627939253a431578e79cb0d157d552f15d802a1.tar.gz
oc-pam-7627939253a431578e79cb0d157d552f15d802a1.zip
hack together a working versionHEADmaster
-rw-r--r--.gitmodules3
-rw-r--r--README.md31
-rw-r--r--appinfo/app.php30
-rw-r--r--appinfo/info.xml15
-rw-r--r--members_src/Makefile42
-rwxr-xr-xmembers_src/membersbin0 -> 41408 bytes
-rw-r--r--members_src/members.159
-rw-r--r--members_src/members.cc510
-rw-r--r--members_src/members.obin0 -> 62536 bytes
m---------pwauth0
-rw-r--r--user_pam.php91
11 files changed, 781 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..7eea220
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "pwauth"]
+ path = pwauth
+ url = https://github.com/phokz/pwauth
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d9e58c9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+Based on [User PAM backend 0.1](https://apps.owncloud.com/content/show.php/User+PAM+backend?content=174684).
+
+Edit `pwauth/pwauth/config.h` and set the web server's user id:
+
+```c
+#define SERVER_UIDS x
+```
+
+Compile pwauth: `cd pwauth/pwauth && make`.
+
+Compile members: `cd members_src && make`.
+
+Copy both binaries to some directory (e.g. `/usr/local/bin`), configure plugin's `appinfo/app.php`
+
+Set suid-flag to allow the script to read `/etc/passwd`: `chmod u+s pwauth`
+
+If something does not work, start debugging by adding this line in `user_pam.php`:
+
+```diff
+if (fwrite($handle, "$uid\n$password\n") === false)
+ return false
+
+$result = pclose( $handle );
+if (0 === $result)
+ return $uid;
+
++ error_log($result);
+return false;
+```
+
+Error codes are at the bottom of pwauth's `INSTALL`.
diff --git a/appinfo/app.php b/appinfo/app.php
new file mode 100644
index 0000000..9e783b1
--- /dev/null
+++ b/appinfo/app.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author CSharplie
+ *
+ * @copyright Copyright (c) 2016, CSharplie
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+require_once('apps/user_pam/user_pam.php');
+
+define('OC_USER_BACKEND_PAM_PATH', '/etc/nixos/bin/pwauth');
+define('OC_USER_BACKEND_MEMBERS_PATH', '/etc/nixos/bin/members');
+define('OC_USER_BACKEND_PAM_GROUP', 'users');
+
+OC_User::useBackend(new \OCA\user_pam\USER_PAM());
+
+?>
diff --git a/appinfo/info.xml b/appinfo/info.xml
new file mode 100644
index 0000000..b4ab7ac
--- /dev/null
+++ b/appinfo/info.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<info>
+ <id>user_pam</id>
+ <name>Unix PAM backend</name>
+ <description>Authenticate users by PAM according a unix group - Based on Unix user backend app</description>
+ <licence>MIT</licence>
+ <author>CSharplie</author>
+ <version>0.1</version>
+ <dependencies>
+ <owncloud min-version="8.0" max-version="9.0" />
+ </dependencies>
+ <types>
+ <logging/>
+ </types>
+</info>
diff --git a/members_src/Makefile b/members_src/Makefile
new file mode 100644
index 0000000..fa194a5
--- /dev/null
+++ b/members_src/Makefile
@@ -0,0 +1,42 @@
+#this is the -*- makefile -*- for members.
+#
+# members is the complement of groups: whereas groups shows the groups a
+# specified user belongs to, members shows users belonging to a specified
+# group.
+
+# Copyright (c) 1997 by Jim Lynch.
+# This software comes with NO WARRANTY WHATSOEVER.
+#
+# 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; version 2 dated June, 1991, 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
+#
+# On Debian Linux systems, the complete text of the GNU General
+# Public License can be found in `/usr/doc/copyright/GPL' (on some
+# installations) or /usr/share/common-licenses/GPL (on newer
+# ones).
+
+CPPFLAGS = -g -Wno-deprecated
+
+members: members.o
+ g++ -o members members.o
+
+main.o: members.cc
+
+clean:
+ rm -f members *.o *~ core
+
+install: members
+ cp members $(DESTDIR)/usr/bin/members
+ chmod 755 $(DESTDIR)/usr/bin/members
+ chown bin.bin $(DESTDIR)/usr/bin/members
diff --git a/members_src/members b/members_src/members
new file mode 100755
index 0000000..1b5fc5e
--- /dev/null
+++ b/members_src/members
Binary files differ
diff --git a/members_src/members.1 b/members_src/members.1
new file mode 100644
index 0000000..b473ffa
--- /dev/null
+++ b/members_src/members.1
@@ -0,0 +1,59 @@
+.TH MEMBERS 1
+.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection
+.\" other parms are allowed: see man(7), man(1)
+.SH NAME
+members \- outputs members of a group
+.SH SYNOPSIS
+.B members
+.I "groupname"
+.SH "DESCRIPTION"
+This manual page documents briefly the
+.BR members
+commands.
+This manual page was written for the Debian GNU/Linux distribution.
+.PP
+.B members
+is a program that sends a space-separated list of secondary member names
+to its standard output.
+.SH OPTIONS
+The programs follow the usual GNU command line syntax, with long
+options starting with two dashes (`-').
+A summary of options is included below.
+.\" For a complete description, see the Info files.
+.TP
+.B \-a, \-\-all
+Show all group members on one line. This is the default.
+.TP
+.B \-p, \-\-primary
+Show only primary group members.
+.TP
+.B \-s, \-\-secondary
+Show only secondary group members.
+.TP
+.B \-t, \-\-two-lines
+Send two lines to standard output. First line is primary members, second
+line is secondary members. NOTE: This always displays two lines, even if
+there are no members at all.
+.TP
+.B \-h, \-\-help
+Show summary of options.
+.\" .TP
+.\" .B \-v, \-\-version
+.\" Show version of program.
+.SH DIAGNOSTICS
+.PP
+Exit status is 0 (i.e. "success") if the group was found,
+and 1 (i.e., "failure") if the group was not found.
+.PP
+Technically, the exit status hinges on the output of
+.B getgrnam(3)
+as follows: if
+.B getgrnam(3)
+returns a null pointer, the exit status is 1, and 0 otherwise.
+.SH BUGS
+I don't know of any! If you find one, please let me know!
+.SH "SEE ALSO"
+groups(1)
+.SH AUTHOR
+This manual page was written by Jim Lynch <jim@laney.edu>,
+for the Debian GNU/Linux system (but may be used by others).
diff --git a/members_src/members.cc b/members_src/members.cc
new file mode 100644
index 0000000..a2d0532
--- /dev/null
+++ b/members_src/members.cc
@@ -0,0 +1,510 @@
+// members is the complement of groups: whereas groups shows the groups a
+// specified user belongs to, members shows users belonging to a specified
+// group.
+
+// PRESENTLY WORKING ON FULL OPTION PARSING (search for /**/ for work pt)
+
+// Copyright (c) 1997 by Jim Lynch.
+// This software comes with NO WARRANTY WHATSOEVER.
+//
+// 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; version 2 dated June, 1991, 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
+//
+// On Debian Linux systems, the complete text of the GNU General
+// Public License can be found in `/usr/doc/copyright/GPL' (on some
+// installations) or /usr/share/common-licenses/GPL (on newer
+// ones).
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cstdio>
+#include <iostream>
+
+ using std::cerr;
+ using std::ostream;
+ using std::endl;
+ using std::cout;
+ using std::hex;
+ using std::endl;
+
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <getopt.h>
+
+#define ALL_OPT 'a'
+#define PRIMARY_OPT 'p'
+#define SECONDARY_OPT 's'
+#define TWO_LINES_OPT 't'
+#define HELP_OPT 'h'
+
+#define END_SHORT_OPT '\0'
+#define END_LONG_OPT {(char *) 0, 0, (int *) 0, '\0'}
+
+const char short_blank = END_SHORT_OPT;
+const struct option long_blank = END_LONG_OPT;
+
+struct option longopts[] =
+{
+ {"all", no_argument, (int *) 0, ALL_OPT },
+ {"primary", no_argument, (int *) 0, PRIMARY_OPT },
+ {"secondary", no_argument, (int *) 0, SECONDARY_OPT },
+ {"two-lines", no_argument, (int *) 0, TWO_LINES_OPT },
+ {"help", no_argument, (int *) 0, HELP_OPT },
+ END_LONG_OPT
+};
+
+char opt_string[] =
+{
+ ALL_OPT,
+ PRIMARY_OPT,
+ SECONDARY_OPT,
+ TWO_LINES_OPT,
+ HELP_OPT,
+ END_SHORT_OPT
+};
+
+int optionlen(struct option *option_array)
+{
+ int result = 0;
+
+ while(option_array[result].name != NULL)
+ result++;
+
+ return result;
+}
+
+void remove_short_option(char option, char *option_array)
+{
+ int i;
+ int found_index = -1; /* index of found index !or! -1 if none */
+ int size = strlen(option_array);
+
+ for(i = 0; i < size; i++)
+ {
+ if(option_array[i] == option)
+ {
+ found_index = i;
+ break;
+ }
+ }
+
+ if(found_index != -1)
+ {
+ if(found_index != size - 1) /* not last item? */
+ {
+ /* replace found item with last item */
+
+ option_array[found_index] = option_array[size - 1];
+ }
+
+ /* blank out last item */
+ option_array[size - 1] = short_blank;
+ }
+}
+
+void remove_long_option(char option, struct option *option_array)
+{
+ int i;
+ int found_index = -1; /* index of found index !or! -1 if none */
+ int size = optionlen(option_array);
+
+ for(i = 0; i < size; i++)
+ {
+ if(option_array[i].val == option)
+ {
+ found_index = i;
+ break;
+ }
+ }
+
+ if(found_index != -1)
+ {
+ if(found_index != size - 1) /* not last item? */
+ {
+ /* replace found item with last item */
+
+ option_array[found_index] = option_array[size - 1];
+ }
+
+ /* blank out last item */
+ option_array[size - 1] = long_blank;
+ }
+}
+
+void print_option_array(struct option *option_array)
+{
+ int i = 0;
+
+ while(option_array[i].val != short_blank)
+ {
+ if(option_array[i].flag != NULL)
+ {
+ cout
+ << "{"
+ << option_array[i].name << ", "
+ << option_array[i].has_arg << ", "
+ << (*option_array[i].flag) << ", '"
+ << ( (char) option_array[i].val ) << "'=0x"
+ << std::hex << option_array[i].val << "}" << std::dec << endl;
+ }
+ else
+ {
+ cout
+ << "{"
+ << option_array[i].name << ", "
+ << option_array[i].has_arg << ", NULL, '"
+ << ( (char) option_array[i].val ) << "'=0x"
+ << std::hex << option_array[i].val << "}" << std::dec << endl;
+ }
+
+ i++;
+ }
+}
+
+void disable_option(char *opt_chars)
+{
+ while(*opt_chars)
+ {
+ remove_short_option(*opt_chars, opt_string);
+ remove_long_option(*opt_chars, longopts);
+
+ opt_chars++;
+ }
+}
+
+void usage(ostream &out, char *name)
+{
+ out << endl << "Usage: " << name;
+ out << " [ -apst --all --primary --secondary --two-lines ] <group>" << endl;
+}
+
+//receives:
+// - C string containing group name
+// - pointer to int containing (!assumed initialized!) flag
+// indicating whether a member name was printed on this line.
+// (if true, at least one member had been printed)
+// returns true if group found.
+
+int display_primaries(const char *groupString, int *printedOne)
+{
+ int result = 0;
+ struct group *theGroup;
+
+ theGroup = getgrnam(groupString);
+
+ if(theGroup)
+ {
+ struct passwd *thePasswd;
+
+ setpwent();
+
+ thePasswd = getpwent();
+
+ if(thePasswd)
+ {
+ thePasswd = getpwent();
+
+ while(thePasswd)
+ {
+ if(thePasswd->pw_gid == theGroup->gr_gid)
+ {
+ // if there is at least one, print it without any space
+ // if there is at least one more, print space then it
+
+ if(*printedOne)
+ cout << " ";
+ else
+ *printedOne = 1;
+
+ cout << thePasswd->pw_name;
+ }
+
+ thePasswd = getpwent();
+ }
+ }
+
+ endpwent();
+
+ result = 1;
+ }
+
+ return result;
+}
+
+//receives:
+// - C string containing group name
+// - pointer to int containing (!assumed initialized!) flag
+// indicating whether a member name was printed on this line.
+// (if true, at least one member had been printed)
+// returns true if group found.
+
+int display_secondaries(const char *groupString, int *printedOne)
+{
+ int result = 0;
+ struct group *theGroup;
+
+ theGroup = getgrnam(groupString);
+
+ if(theGroup)
+ {
+ char **member = theGroup->gr_mem;
+
+ result = 1;
+
+ // if there is at least one, print it without any space
+ // if there is at least one more, print space then it
+
+ while(*member)
+ {
+ if(*printedOne)
+ cout << " ";
+ else
+ *printedOne = 1;
+
+ cout << *member++;
+ }
+ }
+
+ return result;
+}
+
+int main(int argc, char *argv[])
+{
+ int result = 1; // pessimistic since we do something if success
+ int first_nonopt = 1; /* index of the first non-option in argv */
+ int is_bad_opt = 0; /* true if unrecognized option */
+ int getopt_result;
+ int longindex = 0;
+
+ int wants_help = 0; /* boolean from options */
+ int wants_all_members = 0;
+ int wants_primary = 0;
+ int wants_secondary = 0;
+ int wants_two_lines = 0;
+
+ getopt_result =
+ getopt_long
+ (
+ argc,
+ argv,
+ opt_string,
+ longopts,
+ &longindex
+ );
+
+ while(getopt_result != EOF)
+ {
+ /* process opts here */
+
+ switch(getopt_result)
+ {
+ case ALL_OPT:
+ wants_all_members = 1;
+
+ disable_option("apst");
+
+ break;
+
+ case PRIMARY_OPT:
+ wants_primary = 1;
+
+ disable_option("apst");
+
+ break;
+
+ case SECONDARY_OPT:
+ wants_secondary = 1;
+
+ disable_option("apst");
+
+ break;
+
+ case TWO_LINES_OPT:
+ wants_two_lines = 1;
+
+ disable_option("apst");
+
+ break;
+
+ case HELP_OPT:
+ wants_help = 1;
+
+ disable_option("apst");
+
+ break;
+
+ case '?':
+ default:
+ is_bad_opt = 1;
+ break;
+ }
+
+ if(is_bad_opt)
+ break;
+ else
+ getopt_result =
+ getopt_long
+ (
+ argc,
+ argv,
+ opt_string,
+ longopts,
+ &longindex
+ );
+ }
+
+ first_nonopt = optind;
+
+#ifdef SKIP
+ // display results of option parsing
+ cout << "wants_help: " << ( wants_help ? "yes" : "no" ) << endl;
+ cout << "wants_all_members: " << ( wants_all_members ? "yes" : "no" ) << endl;
+ cout << "wants_primary: " << ( wants_primary ? "yes" : "no" ) << endl;
+ cout << "wants_secondary: " << ( wants_secondary ? "yes" : "no" ) << endl;
+ cout << "wants_two_lines: " << ( wants_two_lines ? "yes" : "no" ) << endl;
+ cout << "argc: " << argc << "; first_nonopt: " << first_nonopt << endl;
+#endif
+
+ if(wants_help)
+ {
+ usage(cout, argv[0]);
+
+ cout << "You can use -one- of -a, -p, -s, or -t." << endl;
+
+ cout << endl;
+ cout << endl;
+ cout << "OPTIONS" << endl;
+ cout << endl;
+ cout << " -a or --all" << endl;
+ cout << " [default] show all members, both primary and secondary." << endl;
+ cout << endl;
+ cout << " -p or --primary" << endl;
+ cout << " show only primary members" << endl;
+ cout << " -s or --secondary" << endl;
+ cout << " show only secondary members" << endl;
+ cout << "" << endl;
+ cout << " -t or --two-lines" << endl;
+ cout << " output two lines, first is primary group members," << endl;
+ cout << " secondary members on the second line." << endl;
+ cout << "" << endl;
+ cout << " -h or --help" << endl;
+ cout << " print this message and exit successfully" << endl;
+ cout << "" << endl;
+ cout << "ARGUMENTS" << endl;
+ cout << "" << endl;
+ cout << " group (required)" << endl;
+ cout << " the group to find members in." << endl;
+ cout << "" << endl;
+ cout << "SEE ALSO: man members for details." << endl;
+ cout << "" << endl;
+
+ return 0;
+ }
+ else if(argc != first_nonopt + 1)
+ {
+ usage(cerr, argv[0]);
+
+ return 1;
+ }
+
+ if(argc == first_nonopt + 1)
+ {
+ int bad = 0;
+ int sbad = 0;
+ int printedOne;
+
+ if // user wants default (i.e., no option selected)?
+ (
+ !
+ (
+ wants_all_members ||
+ wants_primary ||
+ wants_secondary ||
+ wants_two_lines
+ )
+ )
+ {
+ wants_all_members = 1; // the default for this app
+ }
+
+ // fill in responses to errors
+
+ if(wants_all_members)
+ {
+ printedOne = 0;
+
+ if(display_primaries(argv[first_nonopt], &printedOne))
+ {
+ if(display_secondaries(argv[first_nonopt], &printedOne))
+ if(printedOne)
+ cout << endl;
+ else
+ sbad = 1;
+ }
+ else
+ bad = 1;
+ }
+ else if(wants_primary)
+ {
+ printedOne = 0;
+
+ bad = ! display_primaries(argv[first_nonopt], &printedOne);
+
+ if(! bad)
+ cout << endl;
+ }
+ else if(wants_secondary)
+ {
+ printedOne = 0;
+
+ bad = ! display_secondaries(argv[first_nonopt], &printedOne);
+
+ if( (! bad) && printedOne)
+ cout << endl;
+ }
+ else if(wants_two_lines)
+ {
+ printedOne = 0;
+
+ bad = ! display_primaries(argv[first_nonopt], &printedOne);
+
+ if(! bad)
+ {
+ cout << endl;
+
+ printedOne = 0;
+
+ sbad = ! display_secondaries(argv[first_nonopt], &printedOne);
+
+ cout << endl;
+ }
+ }
+
+ if(bad) // presumably, group does not exist
+ {
+ cerr << "members: group " << argv[first_nonopt] << " does not exist";
+ cerr << endl;
+ }
+
+ result = bad || sbad;
+ }
+ else
+ result = 1;
+
+ return result;
+}
+
diff --git a/members_src/members.o b/members_src/members.o
new file mode 100644
index 0000000..ddc356b
--- /dev/null
+++ b/members_src/members.o
Binary files differ
diff --git a/pwauth b/pwauth
new file mode 160000
+Subproject 3b4ba71005c4e5436d0f2d1638bddaa86950e81
diff --git a/user_pam.php b/user_pam.php
new file mode 100644
index 0000000..7368cb5
--- /dev/null
+++ b/user_pam.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * @author CSharplie
+ *
+ * @copyright Copyright (c) 2016, CSharplie
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\user_pam;
+
+class USER_PAM extends \OC_User_Backend implements \OCP\UserInterface {
+ protected $pam_bin_path;
+ private $user_search;
+
+ public function __construct() {
+ $this->pam_bin_path = \OCP\Config::getAppValue('user_pam', 'pam_path', OC_USER_BACKEND_PAM_PATH);
+ $this->members_bin_path = \OCP\Config::getAppValue('user_pam', 'members_path', OC_USER_BACKEND_MEMBERS_PATH);
+ }
+
+ public function implementsAction($actions) {
+ return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_SET_PASSWORD) & $actions);
+ }
+
+ private function userMatchesFilter($user) {
+ return (strripos($user, $this->user_search) !== false);
+ }
+
+ public function deleteUser($_uid) {
+ return false;
+ }
+
+ public function checkPassword( $uid, $password ) {
+ $uid = strtolower($uid);
+
+ if (!preg_match('/[A-Za-z_]+/', $uid))
+ return false;
+
+ $userGroups = shell_exec("groups $uid");
+ if (!preg_match('/' . OC_USER_BACKEND_PAM_GROUP . '/', $userGroups))
+ return false;
+
+ $handle = popen($this->pam_bin_path, 'w');
+ if ($handle === false)
+ return false;
+
+ if (fwrite($handle, "$uid\n$password\n") === false)
+ return false;
+
+ $result = pclose( $handle );
+ if (0 === $result)
+ return $uid;
+
+ return false;
+ }
+
+ public function setPassword( $uid, $password ) {
+ return false;
+ }
+
+ public function userExists( $uid ){
+ $user = posix_getpwnam( strtolower($uid) );
+ return is_array($user);
+ }
+
+ public function getUsers($search = '', $limit = 10, $offset = 10){
+ $returnArray = explode(" ", shell_exec(OC_USER_BACKEND_MEMBERS_PATH . " " . OC_USER_BACKEND_PAM_GROUP));
+
+ $this->user_search = $search;
+ if(!empty($this->user_search))
+ $returnArray = array_filter($returnArray, array($this, 'userMatchesFilter'));
+
+ if($limit = -1)
+ $limit = null;
+ return array_slice($returnArray, $offset, $limit);
+ }
+}
+
+?>