Import upstream 0.7.1
[manu/suphp.git] / src / PathMatcher.cpp
diff --git a/src/PathMatcher.cpp b/src/PathMatcher.cpp
new file mode 100644 (file)
index 0000000..07bddfd
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+    suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
+
+    This file is part of suPHP.
+
+    suPHP 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.
+
+    suPHP 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 suPHP; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "PathMatcher.hpp"
+#include "Util.hpp"
+
+using namespace suPHP;
+
+suPHP::PathMatcher::PathMatcher(const UserInfo& user, const GroupInfo& group) {
+    this->user = user;
+    this->group = group;
+}
+
+bool suPHP::PathMatcher::matches(std::string pattern, std::string path) 
+    throw (KeyNotFoundException, ParsingException) {
+    std::string remainingPath = path;
+    std::string remainingPattern = pattern;
+    
+    while (remainingPath.length() > 0 && remainingPattern.length() > 0) {
+        bool escapeNext = false;
+        for (int i = 0; i < remainingPattern.length(); i++) {
+            char c = remainingPattern.at(i);
+            if (escapeNext) {
+                escapeNext = false;
+                if (c == '\\' || c == '*' || c == '$') {
+                    // Backslash was used as an escape character
+                    if (remainingPath.at(i-1) == c) {
+                        remainingPattern = remainingPattern.substr(i + 1);
+                        remainingPath = remainingPath.substr(i);
+                        break;
+                    } else {
+                        return false;
+                    }
+                } else {
+                    if (remainingPath.at(i-1) == '\\') {
+                        remainingPattern = remainingPattern.substr(i);
+                        remainingPath = remainingPath.substr(i);
+                        break;
+                    } else {
+                        return false;
+                    }
+                }
+            } else {
+                if (c == '\\') {
+                    escapeNext = true;
+                } else if (c == '*') {
+                    remainingPattern = remainingPattern.substr(i + 1);
+                    remainingPath = remainingPath.substr(i);
+                    if (matches(remainingPattern, remainingPath)) {
+                        return true;
+                    }
+                    std::string testPrefix;
+                    for (int j = 0; j < remainingPath.length(); j++) {
+                        char c2 = remainingPath.at(j);
+                        if (c2 == '/') {
+                            return false;
+                        }
+                        if (c2 == '\\' || c2 == '*' || c2 == '$') {
+                            testPrefix += "\\";
+                        }
+                        testPrefix += c2;
+                        if (matches(testPrefix + remainingPattern, 
+                                    remainingPath)) {
+                            return true;
+                        }
+                    }
+                } else if (c == '$') {
+                    if (remainingPattern.length() < i + 3) {
+                        throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
+                    }
+                    if (remainingPattern.at(i + 1) != '{') {
+                        throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
+                    }
+                    std::string::size_type closingBrace = remainingPattern.find('}', i);
+                    if (closingBrace == std::string::npos) {
+                        throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
+                    }
+                    std::string varName = remainingPattern.substr(i + 2, closingBrace - i - 2);
+                    remainingPattern = lookupVariable(varName) + remainingPattern.substr(closingBrace + 1);
+                    break;
+                } else {
+                    if (i >= remainingPath.length() || c != remainingPath.at(i)) {
+                        return false;
+                    }
+                    if (i == remainingPattern.length() - 1) {
+                        if (c == '/' || (i + 1 < remainingPath.length() && remainingPath.at(i + 1) == '/')) {
+                            // Path represents file in subdirectory
+                            return true;
+                        } else if (remainingPath.length() == remainingPattern.length()) {
+                            // Exact match
+                            return true;
+                        } else {
+                            return false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
+
+std::string suPHP::PathMatcher::lookupVariable(std::string str) 
+    throw (KeyNotFoundException) {
+    std::string rv;
+    if (str == "USERNAME") {
+        rv = user.getUsername();
+    } else if (str == "UID") {
+        rv = Util::intToStr(user.getUid());
+    } else if (str == "HOME") {
+        rv = user.getHomeDirectory();
+    } else if (str == "GROUPNAME") {
+        rv = group.getGroupname();
+    } else if (str == "GID") {
+        rv = Util::intToStr(group.getGid());
+    } else {
+        throw KeyNotFoundException("Key \"" + str + 
+            "\" does not represent a valid variable name", __FILE__, __LINE__);
+    }
+    return rv;
+}
+
+std::string suPHP::PathMatcher::resolveVariables(std::string str) throw (KeyNotFoundException, ParsingException) {
+    std::string out;
+    bool escapeNext = false;
+    for (int i = 0; i < str.length(); i++) {
+        char c = str.at(i);
+        if (escapeNext) {
+            escapeNext = false;
+            if (c == '\\' || c == '$') {
+                // Backslash was used as an escape character
+                out += c;
+            } else {
+                out += '\\';
+                out += c;
+            }
+        } else {
+            if (c == '\\') {
+                escapeNext = true;
+            } else if (c == '$') {
+                if (str.length() < i + 3) {
+                    throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
+                }
+                if (str.at(i + 1) != '{') {
+                    throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
+                }
+                std::string::size_type closingBrace = str.find('}', i);
+                if (closingBrace == std::string::npos) {
+                    throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
+                }
+                std::string varName = str.substr(i + 2, closingBrace - i - 2);
+                out += lookupVariable(varName);
+                i = closingBrace + 1;
+            } else {
+                out += c;
+            }
+        }
+    }
+    return out;
+}