]>
Commit | Line | Data |
---|---|---|
22f3fd98 | 1 | |
2 | /* | |
262a0e14 | 3 | * $Id$ |
22f3fd98 | 4 | * |
5 | * DEBUG: section 16 Cache Manager Objects | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
22f3fd98 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
22f3fd98 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
26ac0430 | 24 | * |
22f3fd98 | 25 | * This program is distributed in the hope that it will be useful, |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
26ac0430 | 29 | * |
22f3fd98 | 30 | * You should have received a copy of the GNU General Public License |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
22f3fd98 | 34 | */ |
35 | ||
f7f3304a | 36 | #include "squid.h" |
8822ebee | 37 | #include "base/TextException.h" |
62ee09ca | 38 | #include "CacheManager.h" |
5c336a3b | 39 | #include "comm/Connection.h" |
8822ebee | 40 | #include "Debug.h" |
aa839030 | 41 | #include "errorpage.h" |
8822ebee | 42 | #include "fde.h" |
528b2c61 | 43 | #include "HttpReply.h" |
44 | #include "HttpRequest.h" | |
8822ebee AR |
45 | #include "mgr/ActionCreator.h" |
46 | #include "mgr/Action.h" | |
47 | #include "mgr/ActionProfile.h" | |
48 | #include "mgr/BasicActions.h" | |
49 | #include "mgr/Command.h" | |
50 | #include "mgr/Forwarder.h" | |
51 | #include "mgr/FunAction.h" | |
b8151fa1 | 52 | #include "mgr/QueryParams.h" |
8822ebee | 53 | #include "protos.h" /* rotate_logs() */ |
985c86bc | 54 | #include "SquidTime.h" |
8822ebee | 55 | #include "Store.h" |
d295d770 | 56 | #include "wordlist.h" |
8822ebee | 57 | #include <algorithm> |
22f3fd98 | 58 | |
63be0a78 | 59 | /// \ingroup CacheManagerInternal |
22f3fd98 | 60 | #define MGR_PASSWD_SZ 128 |
61 | ||
8822ebee AR |
62 | /// creates Action using supplied Action::Create method and command |
63 | class ClassActionCreator: public Mgr::ActionCreator | |
64 | { | |
65 | public: | |
66 | typedef Mgr::Action::Pointer Handler(const Mgr::Command::Pointer &cmd); | |
c83f0bd5 | 67 | |
8822ebee AR |
68 | public: |
69 | ClassActionCreator(Handler *aHandler): handler(aHandler) {} | |
70 | ||
d9fc6862 | 71 | virtual Mgr::Action::Pointer create(const Mgr::Command::Pointer &cmd) const { |
8822ebee AR |
72 | return handler(cmd); |
73 | } | |
74 | ||
75 | private: | |
76 | Handler *handler; | |
77 | }; | |
78 | ||
8822ebee AR |
79 | /// Registers new profiles, ignoring attempts to register a duplicate |
80 | void | |
81 | CacheManager::registerProfile(const Mgr::ActionProfile::Pointer &profile) | |
62ee09ca | 82 | { |
8822ebee | 83 | Must(profile != NULL); |
ef890f9d | 84 | if (std::find(menu_.begin(), menu_.end(), profile) == menu_.end()) { |
8822ebee AR |
85 | menu_.push_back(profile); |
86 | debugs(16, 3, HERE << "registered profile: " << *profile); | |
87 | } else { | |
88 | debugs(16, 2, HERE << "skipped duplicate profile: " << *profile); | |
89 | } | |
62ee09ca | 90 | } |
22f3fd98 | 91 | |
50847dca AJ |
92 | /** |
93 | \ingroup CacheManagerAPI | |
94 | * Registers a C-style action, which is implemented as a pointer to a function | |
95 | * taking as argument a pointer to a StoreEntry and returning void. | |
96 | * Implemented via CacheManagerActionLegacy. | |
97 | */ | |
22f3fd98 | 98 | void |
8822ebee | 99 | CacheManager::registerProfile(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic) |
22f3fd98 | 100 | { |
8822ebee AR |
101 | debugs(16, 3, HERE << "registering legacy " << action); |
102 | const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action, | |
d9fc6862 | 103 | desc, pw_req_flag, atomic, new Mgr::FunActionCreator(handler)); |
8822ebee | 104 | registerProfile(profile); |
1d8395bd | 105 | } |
62e76326 | 106 | |
d154d3ec | 107 | /** |
e0d28505 | 108 | * \ingroup CacheManagerAPI |
a750e510 | 109 | * Registers a C++-style action, via a pointer to a subclass of |
d154d3ec FC |
110 | * a CacheManagerAction object, whose run() method will be invoked when |
111 | * CacheManager identifies that the user has requested the action. | |
112 | */ | |
1d8395bd | 113 | void |
8822ebee | 114 | CacheManager::registerProfile(char const * action, char const * desc, |
d9fc6862 A |
115 | ClassActionCreator::Handler *handler, |
116 | int pw_req_flag, int atomic) | |
1d8395bd | 117 | { |
8822ebee | 118 | const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action, |
d9fc6862 | 119 | desc, pw_req_flag, atomic, new ClassActionCreator(handler)); |
8822ebee | 120 | registerProfile(profile); |
62ee09ca | 121 | } |
122 | ||
d154d3ec FC |
123 | /** |
124 | \ingroup CacheManagerInternal | |
125 | * Locates an action in the actions registry ActionsList. | |
126 | \retval NULL if Action not found | |
127 | \retval CacheManagerAction* if the action was found | |
128 | */ | |
8822ebee AR |
129 | Mgr::ActionProfile::Pointer |
130 | CacheManager::findAction(char const * action) const | |
22f3fd98 | 131 | { |
8822ebee AR |
132 | Must(action != NULL); |
133 | Menu::const_iterator a; | |
03c4599f K |
134 | |
135 | debugs(16, 5, "CacheManager::findAction: looking for action " << action); | |
8822ebee AR |
136 | for (a = menu_.begin(); a != menu_.end(); ++a) { |
137 | if (0 == strcmp((*a)->name, action)) { | |
03c4599f K |
138 | debugs(16, 6, " found"); |
139 | return *a; | |
140 | } | |
22f3fd98 | 141 | } |
62e76326 | 142 | |
03c4599f | 143 | debugs(16, 6, "Action not found."); |
8822ebee AR |
144 | return Mgr::ActionProfilePointer(); |
145 | } | |
146 | ||
147 | Mgr::Action::Pointer | |
148 | CacheManager::createNamedAction(const char *actionName) | |
149 | { | |
150 | Must(actionName); | |
151 | ||
152 | Mgr::Command::Pointer cmd = new Mgr::Command; | |
153 | cmd->profile = findAction(actionName); | |
154 | cmd->params.actionName = actionName; | |
155 | ||
156 | Must(cmd->profile != NULL); | |
157 | return cmd->profile->creator->create(cmd); | |
158 | } | |
159 | ||
160 | Mgr::Action::Pointer | |
161 | CacheManager::createRequestedAction(const Mgr::ActionParams ¶ms) | |
162 | { | |
163 | Mgr::Command::Pointer cmd = new Mgr::Command; | |
164 | cmd->params = params; | |
165 | cmd->profile = findAction(params.actionName.termedBuf()); | |
166 | Must(cmd->profile != NULL); | |
167 | return cmd->profile->creator->create(cmd); | |
22f3fd98 | 168 | } |
169 | ||
832c08ab FC |
170 | /** |
171 | \ingroup CacheManagerInternal | |
172 | * define whether the URL is a cache-manager URL and parse the action | |
173 | * requested by the user. Checks via CacheManager::ActionProtection() that the | |
174 | * item is accessible by the user. | |
175 | \retval CacheManager::cachemgrStateData state object for the following handling | |
176 | \retval NULL if the action can't be found or can't be accessed by the user | |
177 | */ | |
8822ebee | 178 | Mgr::Command::Pointer |
c83f0bd5 | 179 | CacheManager::ParseUrl(const char *url) |
22f3fd98 | 180 | { |
181 | int t; | |
182 | LOCAL_ARRAY(char, host, MAX_URL); | |
183 | LOCAL_ARRAY(char, request, MAX_URL); | |
184 | LOCAL_ARRAY(char, password, MAX_URL); | |
b8151fa1 CT |
185 | LOCAL_ARRAY(char, params, MAX_URL); |
186 | host[0] = 0; | |
187 | request[0] = 0; | |
188 | password[0] = 0; | |
189 | params[0] = 0; | |
190 | int pos = -1; | |
191 | int len = strlen(url); | |
192 | Must(len > 0); | |
193 | t = sscanf(url, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host, request, &pos, password, params); | |
5366b99b AJ |
194 | if (t < 3) { |
195 | t = sscanf(url, "cache_object://%[^/]/%[^?]%n?%s", host, request, &pos, params); | |
196 | } | |
e37bd29b AJ |
197 | if (t < 1) { |
198 | t = sscanf(url, "http://%[^/]/squid-internal-mgr/%[^?]%n?%s", host, request, &pos, params); | |
199 | } | |
200 | if (t < 1) { | |
201 | t = sscanf(url, "https://%[^/]/squid-internal-mgr/%[^?]%n?%s", host, request, &pos, params); | |
202 | } | |
f9c6f861 AJ |
203 | if (t < 2) { |
204 | if (strncmp("cache_object://",url,15)==0) | |
205 | xstrncpy(request, "menu", MAX_URL); | |
206 | else | |
207 | xstrncpy(request, "index", MAX_URL); | |
208 | } | |
8822ebee | 209 | |
1191b93b | 210 | #if _SQUID_OS2_ |
8822ebee | 211 | if (t == 2 && request[0] == '\0') { |
62e76326 | 212 | /* |
213 | * emx's sscanf insists of returning 2 because it sets request | |
214 | * to null | |
215 | */ | |
f9c6f861 AJ |
216 | if (strncmp("cache_object://",url,15)==0) |
217 | xstrncpy(request, "menu", MAX_URL); | |
218 | else | |
219 | xstrncpy(request, "index", MAX_URL); | |
8822ebee | 220 | } |
cd377065 | 221 | #endif |
62e76326 | 222 | |
5366b99b AJ |
223 | debugs(16, 3, HERE << "MGR request: t=" << t << ", host='" << host << "', request='" << request << "', pos=" << pos << |
224 | ", password='" << password << "', params='" << params << "'"); | |
225 | ||
8822ebee AR |
226 | Mgr::ActionProfile::Pointer profile = findAction(request); |
227 | if (!profile) { | |
3e1da049 | 228 | debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found"); |
62e76326 | 229 | return NULL; |
22f3fd98 | 230 | } |
62e76326 | 231 | |
8822ebee AR |
232 | const char *prot = ActionProtection(profile); |
233 | if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) { | |
234 | debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot); | |
235 | return NULL; | |
236 | } | |
62e76326 | 237 | |
8822ebee | 238 | Mgr::Command::Pointer cmd = new Mgr::Command; |
b8151fa1 CT |
239 | if (!Mgr::QueryParams::Parse(params, cmd->params.queryParams)) |
240 | return NULL; | |
8822ebee AR |
241 | cmd->profile = profile; |
242 | cmd->params.httpUri = url; | |
243 | cmd->params.userName = String(); | |
b8151fa1 | 244 | cmd->params.password = password; |
8822ebee AR |
245 | cmd->params.actionName = request; |
246 | return cmd; | |
22f3fd98 | 247 | } |
248 | ||
63be0a78 | 249 | /// \ingroup CacheManagerInternal |
832c08ab FC |
250 | /* |
251 | \ingroup CacheManagerInternal | |
252 | * Decodes the headers needed to perform user authentication and fills | |
253 | * the details into the cachemgrStateData argument | |
254 | */ | |
c83f0bd5 | 255 | void |
8822ebee | 256 | CacheManager::ParseHeaders(const HttpRequest * request, Mgr::ActionParams ¶ms) |
63259c34 | 257 | { |
8822ebee AR |
258 | assert(request); |
259 | ||
260 | params.httpMethod = request->method.id(); | |
261 | params.httpFlags = request->flags; | |
262 | ||
9da6b594 AJ |
263 | #if HAVE_AUTH_MODULE_BASIC |
264 | // TODO: use the authentication system decode to retrieve these details properly. | |
265 | ||
266 | /* base 64 _decoded_ user:passwd pair */ | |
267 | const char *basic_cookie = request->header.getAuth(HDR_AUTHORIZATION, "Basic"); | |
62e76326 | 268 | |
99edd1c3 | 269 | if (!basic_cookie) |
62e76326 | 270 | return; |
271 | ||
9da6b594 | 272 | const char *passwd_del; |
63259c34 | 273 | if (!(passwd_del = strchr(basic_cookie, ':'))) { |
3e1da049 | 274 | debugs(16, DBG_IMPORTANT, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'"); |
62e76326 | 275 | return; |
63259c34 | 276 | } |
62e76326 | 277 | |
63259c34 | 278 | /* found user:password pair, reset old values */ |
8822ebee AR |
279 | params.userName.limitInit(basic_cookie, passwd_del - basic_cookie); |
280 | params.password = passwd_del + 1; | |
62e76326 | 281 | |
9da6b594 | 282 | /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */ |
8822ebee | 283 | debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << |
d9fc6862 | 284 | params.userName << "' passwd: '" << params.password << "'"); |
9da6b594 | 285 | #endif |
63259c34 | 286 | } |
287 | ||
63be0a78 | 288 | /** |
289 | \ingroup CacheManagerInternal | |
290 | * | |
291 | \retval 0 if mgr->password is good or "none" | |
292 | \retval 1 if mgr->password is "disable" | |
293 | \retval !0 if mgr->password does not match configured password | |
22f3fd98 | 294 | */ |
c83f0bd5 | 295 | int |
8822ebee | 296 | CacheManager::CheckPassword(const Mgr::Command &cmd) |
22f3fd98 | 297 | { |
8822ebee AR |
298 | assert(cmd.profile != NULL); |
299 | const char *action = cmd.profile->name; | |
300 | char *pwd = PasswdGet(Config.passwd_list, action); | |
03c4599f | 301 | |
8822ebee | 302 | debugs(16, 4, "CacheManager::CheckPassword for action " << action); |
62e76326 | 303 | |
22f3fd98 | 304 | if (pwd == NULL) |
8822ebee | 305 | return cmd.profile->isPwReq; |
62e76326 | 306 | |
22f3fd98 | 307 | if (strcmp(pwd, "disable") == 0) |
62e76326 | 308 | return 1; |
309 | ||
22f3fd98 | 310 | if (strcmp(pwd, "none") == 0) |
62e76326 | 311 | return 0; |
312 | ||
8822ebee | 313 | if (!cmd.params.password.size()) |
62e76326 | 314 | return 1; |
315 | ||
8822ebee | 316 | return cmd.params.password != pwd; |
22f3fd98 | 317 | } |
318 | ||
832c08ab FC |
319 | /** |
320 | \ingroup CacheManagerAPI | |
321 | * Main entry point in the Cache Manager's activity. Gets called as part | |
322 | * of the forward chain if the right URL is detected there. Initiates | |
323 | * all needed internal work and renders the response. | |
324 | */ | |
22f3fd98 | 325 | void |
5c336a3b | 326 | CacheManager::Start(const Comm::ConnectionPointer &client, HttpRequest * request, StoreEntry * entry) |
22f3fd98 | 327 | { |
832c08ab | 328 | debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" ); |
62e76326 | 329 | |
8822ebee AR |
330 | Mgr::Command::Pointer cmd = ParseUrl(entry->url()); |
331 | if (!cmd) { | |
913524f0 | 332 | ErrorState *err = new ErrorState(ERR_INVALID_URL, HTTP_NOT_FOUND, request); |
3900307b | 333 | err->url = xstrdup(entry->url()); |
62e76326 | 334 | errorAppendEntry(entry, err); |
335 | entry->expires = squid_curtime; | |
336 | return; | |
22f3fd98 | 337 | } |
62e76326 | 338 | |
8822ebee | 339 | const char *actionName = cmd->profile->name; |
34266cde | 340 | |
22f3fd98 | 341 | entry->expires = squid_curtime; |
34266cde | 342 | |
a750e510 | 343 | debugs(16, 5, "CacheManager: " << client << " requesting '" << actionName << "'"); |
34266cde | 344 | |
63259c34 | 345 | /* get additional info from request headers */ |
8822ebee AR |
346 | ParseHeaders(request, cmd->params); |
347 | ||
348 | const char *userName = cmd->params.userName.size() ? | |
d9fc6862 | 349 | cmd->params.userName.termedBuf() : "unknown"; |
34266cde | 350 | |
22f3fd98 | 351 | /* Check password */ |
62e76326 | 352 | |
8822ebee | 353 | if (CheckPassword(*cmd) != 0) { |
62e76326 | 354 | /* build error message */ |
913524f0 | 355 | ErrorState errState(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED, request); |
62e76326 | 356 | /* warn if user specified incorrect password */ |
357 | ||
8822ebee | 358 | if (cmd->params.password.size()) { |
26ac0430 | 359 | debugs(16, DBG_IMPORTANT, "CacheManager: " << |
8822ebee | 360 | userName << "@" << |
5c336a3b | 361 | client << ": incorrect password for '" << |
8822ebee AR |
362 | actionName << "'" ); |
363 | } else { | |
26ac0430 | 364 | debugs(16, DBG_IMPORTANT, "CacheManager: " << |
8822ebee | 365 | userName << "@" << |
5c336a3b | 366 | client << ": password needed for '" << |
8822ebee AR |
367 | actionName << "'" ); |
368 | } | |
62e76326 | 369 | |
913524f0 | 370 | HttpReply *rep = errState.BuildHttpReply(); |
62e76326 | 371 | |
9da6b594 | 372 | #if HAVE_AUTH_MODULE_BASIC |
62e76326 | 373 | /* |
8822ebee AR |
374 | * add Authenticate header using action name as a realm because |
375 | * password depends on the action | |
62e76326 | 376 | */ |
8822ebee | 377 | rep->header.putAuth("Basic", actionName); |
9da6b594 | 378 | #endif |
3865965d AJ |
379 | // Allow cachemgr and other XHR scripts access to our version string |
380 | if (request->header.has(HDR_ORIGIN)) { | |
381 | rep->header.putExt("Access-Control-Allow-Origin",request->header.getStr(HDR_ORIGIN)); | |
382 | #if HAVE_AUTH_MODULE_BASIC | |
383 | rep->header.putExt("Access-Control-Allow-Credentials","true"); | |
384 | #endif | |
385 | rep->header.putExt("Access-Control-Expose-Headers","Server"); | |
386 | } | |
62e76326 | 387 | |
388 | /* store the reply */ | |
db237875 | 389 | entry->replaceHttpReply(rep); |
62e76326 | 390 | |
391 | entry->expires = squid_curtime; | |
392 | ||
393 | entry->complete(); | |
394 | ||
62e76326 | 395 | return; |
22f3fd98 | 396 | } |
62e76326 | 397 | |
3865965d AJ |
398 | if (request->header.has(HDR_ORIGIN)) { |
399 | cmd->params.httpOrigin = request->header.getStr(HDR_ORIGIN); | |
400 | } | |
401 | ||
0be039f4 | 402 | debugs(16, 2, "CacheManager: " << |
8822ebee | 403 | userName << "@" << |
5c336a3b | 404 | client << " requesting '" << |
8822ebee | 405 | actionName << "'" ); |
62e76326 | 406 | |
b073fc4b AJ |
407 | // special case: /squid-internal-mgr/ index page |
408 | if (!strcmp(cmd->profile->name, "index")) { | |
409 | ErrorState err(MGR_INDEX, HTTP_OK, request); | |
410 | err.url = xstrdup(entry->url()); | |
411 | HttpReply *rep = err.BuildHttpReply(); | |
412 | if (strncmp(rep->body.content(),"Internal Error:", 15) == 0) | |
413 | rep->sline.status = HTTP_NOT_FOUND; | |
414 | // Allow cachemgr and other XHR scripts access to our version string | |
415 | if (request->header.has(HDR_ORIGIN)) { | |
416 | rep->header.putExt("Access-Control-Allow-Origin",request->header.getStr(HDR_ORIGIN)); | |
417 | #if HAVE_AUTH_MODULE_BASIC | |
418 | rep->header.putExt("Access-Control-Allow-Credentials","true"); | |
419 | #endif | |
420 | rep->header.putExt("Access-Control-Expose-Headers","Server"); | |
421 | } | |
422 | entry->replaceHttpReply(rep); | |
423 | entry->complete(); | |
424 | return; | |
425 | } | |
426 | ||
8822ebee | 427 | if (UsingSmp() && IamWorkerProcess()) { |
1b76e6c1 | 428 | // is client the right connection to pass here? |
25b481e6 | 429 | AsyncJob::Start(new Mgr::Forwarder(client, cmd->params, request, entry)); |
8822ebee | 430 | return; |
cb69b4c7 | 431 | } |
62e76326 | 432 | |
8822ebee AR |
433 | Mgr::Action::Pointer action = cmd->profile->creator->create(cmd); |
434 | Must(action != NULL); | |
435 | action->run(entry, true); | |
22f3fd98 | 436 | } |
437 | ||
832c08ab FC |
438 | /* |
439 | \ingroup CacheManagerInternal | |
440 | * Renders the protection level text for an action. | |
441 | * Also doubles as a check for the protection level. | |
832c08ab | 442 | */ |
c83f0bd5 | 443 | const char * |
8822ebee | 444 | CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer &profile) |
7395afb8 | 445 | { |
8822ebee AR |
446 | assert(profile != NULL); |
447 | const char *pwd = PasswdGet(Config.passwd_list, profile->name); | |
62e76326 | 448 | |
7395afb8 | 449 | if (!pwd) |
8822ebee | 450 | return profile->isPwReq ? "hidden" : "public"; |
62e76326 | 451 | |
7395afb8 | 452 | if (!strcmp(pwd, "disable")) |
62e76326 | 453 | return "disabled"; |
454 | ||
7395afb8 | 455 | if (strcmp(pwd, "none") == 0) |
62e76326 | 456 | return "public"; |
457 | ||
7395afb8 | 458 | return "protected"; |
459 | } | |
460 | ||
832c08ab FC |
461 | /* |
462 | \ingroup CacheManagerInternal | |
463 | * gets from the global Config the password the user would need to supply | |
464 | * for the action she queried | |
465 | */ | |
c83f0bd5 K |
466 | char * |
467 | CacheManager::PasswdGet(cachemgr_passwd * a, const char *action) | |
22f3fd98 | 468 | { |
469 | wordlist *w; | |
62e76326 | 470 | |
22f3fd98 | 471 | while (a != NULL) { |
62e76326 | 472 | for (w = a->actions; w != NULL; w = w->next) { |
473 | if (0 == strcmp(w->key, action)) | |
474 | return a->passwd; | |
475 | ||
476 | if (0 == strcmp(w->key, "all")) | |
477 | return a->passwd; | |
478 | } | |
479 | ||
480 | a = a->next; | |
22f3fd98 | 481 | } |
62e76326 | 482 | |
22f3fd98 | 483 | return NULL; |
484 | } | |
c83f0bd5 K |
485 | |
486 | CacheManager* CacheManager::instance=0; | |
487 | ||
832c08ab FC |
488 | /** |
489 | \ingroup CacheManagerAPI | |
490 | * Singleton accessor method. | |
491 | */ | |
c83f0bd5 | 492 | CacheManager* |
26ac0430 AJ |
493 | CacheManager::GetInstance() |
494 | { | |
495 | if (instance == 0) { | |
496 | debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up"); | |
497 | instance = new CacheManager; | |
8822ebee | 498 | Mgr::RegisterBasics(); |
26ac0430 AJ |
499 | } |
500 | return instance; | |
c83f0bd5 | 501 | } |