Update changelog
[manu/suphp.git] / src / PathMatcher.cpp
1 /*
2     suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
3
4     This file is part of suPHP.
5
6     suPHP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     suPHP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with suPHP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "PathMatcher.hpp"
22 #include "Util.hpp"
23
24 using namespace suPHP;
25
26 suPHP::PathMatcher::PathMatcher(const UserInfo& user, const GroupInfo& group) {
27     this->user = user;
28     this->group = group;
29 }
30
31 bool suPHP::PathMatcher::matches(std::string pattern, std::string path) 
32     throw (KeyNotFoundException, ParsingException) {
33     std::string remainingPath = path;
34     std::string remainingPattern = pattern;
35     
36     while (remainingPath.length() > 0 && remainingPattern.length() > 0) {
37         bool escapeNext = false;
38         for (int i = 0; i < remainingPattern.length(); i++) {
39             char c = remainingPattern.at(i);
40             if (escapeNext) {
41                 escapeNext = false;
42                 if (c == '\\' || c == '*' || c == '$') {
43                     // Backslash was used as an escape character
44                     if (remainingPath.at(i-1) == c) {
45                         remainingPattern = remainingPattern.substr(i + 1);
46                         remainingPath = remainingPath.substr(i);
47                         break;
48                     } else {
49                         return false;
50                     }
51                 } else {
52                     if (remainingPath.at(i-1) == '\\') {
53                         remainingPattern = remainingPattern.substr(i);
54                         remainingPath = remainingPath.substr(i);
55                         break;
56                     } else {
57                         return false;
58                     }
59                 }
60             } else {
61                 if (c == '\\') {
62                     escapeNext = true;
63                 } else if (c == '*') {
64                     remainingPattern = remainingPattern.substr(i + 1);
65                     remainingPath = remainingPath.substr(i);
66                     if (matches(remainingPattern, remainingPath)) {
67                         return true;
68                     }
69                     std::string testPrefix;
70                     for (int j = 0; j < remainingPath.length(); j++) {
71                         char c2 = remainingPath.at(j);
72                         if (c2 == '/') {
73                             return false;
74                         }
75                         if (c2 == '\\' || c2 == '*' || c2 == '$') {
76                             testPrefix += "\\";
77                         }
78                         testPrefix += c2;
79                         if (matches(testPrefix + remainingPattern, 
80                                     remainingPath)) {
81                             return true;
82                         }
83                     }
84                 } else if (c == '$') {
85                     if (remainingPattern.length() < i + 3) {
86                         throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
87                     }
88                     if (remainingPattern.at(i + 1) != '{') {
89                         throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
90                     }
91                     std::string::size_type closingBrace = remainingPattern.find('}', i);
92                     if (closingBrace == std::string::npos) {
93                         throw ParsingException("Incorrect use of $ in pattern \"" + pattern + "\".", __FILE__, __LINE__);
94                     }
95                     std::string varName = remainingPattern.substr(i + 2, closingBrace - i - 2);
96                     remainingPattern = lookupVariable(varName) + remainingPattern.substr(closingBrace + 1);
97                     break;
98                 } else {
99                     if (i >= remainingPath.length() || c != remainingPath.at(i)) {
100                         return false;
101                     }
102                     if (i == remainingPattern.length() - 1) {
103                         if (c == '/' || (i + 1 < remainingPath.length() && remainingPath.at(i + 1) == '/')) {
104                             // Path represents file in subdirectory
105                             return true;
106                         } else if (remainingPath.length() == remainingPattern.length()) {
107                             // Exact match
108                             return true;
109                         } else {
110                             return false;
111                         }
112                     }
113                 }
114             }
115         }
116     }
117     return false;
118 }
119
120 std::string suPHP::PathMatcher::lookupVariable(std::string str) 
121     throw (KeyNotFoundException) {
122     std::string rv;
123     if (str == "USERNAME") {
124         rv = user.getUsername();
125     } else if (str == "UID") {
126         rv = Util::intToStr(user.getUid());
127     } else if (str == "HOME") {
128         rv = user.getHomeDirectory();
129     } else if (str == "GROUPNAME") {
130         rv = group.getGroupname();
131     } else if (str == "GID") {
132         rv = Util::intToStr(group.getGid());
133     } else {
134         throw KeyNotFoundException("Key \"" + str + 
135             "\" does not represent a valid variable name", __FILE__, __LINE__);
136     }
137     return rv;
138 }
139
140 std::string suPHP::PathMatcher::resolveVariables(std::string str) throw (KeyNotFoundException, ParsingException) {
141     std::string out;
142     bool escapeNext = false;
143     for (int i = 0; i < str.length(); i++) {
144         char c = str.at(i);
145         if (escapeNext) {
146             escapeNext = false;
147             if (c == '\\' || c == '$') {
148                 // Backslash was used as an escape character
149                 out += c;
150             } else {
151                 out += '\\';
152                 out += c;
153             }
154         } else {
155             if (c == '\\') {
156                 escapeNext = true;
157             } else if (c == '$') {
158                 if (str.length() < i + 3) {
159                     throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
160                 }
161                 if (str.at(i + 1) != '{') {
162                     throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
163                 }
164                 std::string::size_type closingBrace = str.find('}', i);
165                 if (closingBrace == std::string::npos) {
166                     throw ParsingException("Incorrect use of $ in string \"" + str + "\".", __FILE__, __LINE__);
167                 }
168                 std::string varName = str.substr(i + 2, closingBrace - i - 2);
169                 out += lookupVariable(varName);
170                 i = closingBrace + 1;
171             } else {
172                 out += c;
173             }
174         }
175     }
176     return out;
177 }