5 * DEBUG: section 16 Cache Manager Objects
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
37 #include "base/TextException.h"
38 #include "CacheManager.h"
39 #include "comm/Connection.h"
41 #include "errorpage.h"
43 #include "HttpReply.h"
44 #include "HttpRequest.h"
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"
52 /* for rotate_logs() */
54 #include "SquidTime.h"
60 /// \ingroup CacheManagerInternal
61 #define MGR_PASSWD_SZ 128
63 /// creates Action using supplied Action::Create method and command
64 class ClassActionCreator
: public Mgr::ActionCreator
67 typedef Mgr::Action::Pointer
Handler(const Mgr::Command::Pointer
&cmd
);
70 ClassActionCreator(Handler
*aHandler
): handler(aHandler
) {}
72 virtual Mgr::Action::Pointer
create(const Mgr::Command::Pointer
&cmd
) const {
81 /// Registers new profiles, ignoring attempts to register a duplicate
83 CacheManager::registerProfile(const Mgr::ActionProfile::Pointer
&profile
)
85 Must(profile
!= NULL
);
86 if (std::find(menu_
.begin(), menu_
.end(), profile
) == menu_
.end()) {
87 menu_
.push_back(profile
);
88 debugs(16, 3, HERE
<< "registered profile: " << *profile
);
90 debugs(16, 2, HERE
<< "skipped duplicate profile: " << *profile
);
95 \ingroup CacheManagerAPI
96 * Registers a C-style action, which is implemented as a pointer to a function
97 * taking as argument a pointer to a StoreEntry and returning void.
98 * Implemented via CacheManagerActionLegacy.
101 CacheManager::registerProfile(char const * action
, char const * desc
, OBJH
* handler
, int pw_req_flag
, int atomic
)
103 debugs(16, 3, HERE
<< "registering legacy " << action
);
104 const Mgr::ActionProfile::Pointer profile
= new Mgr::ActionProfile(action
,
105 desc
, pw_req_flag
, atomic
, new Mgr::FunActionCreator(handler
));
106 registerProfile(profile
);
110 * \ingroup CacheManagerAPI
111 * Registers a C++-style action, via a pointer to a subclass of
112 * a CacheManagerAction object, whose run() method will be invoked when
113 * CacheManager identifies that the user has requested the action.
116 CacheManager::registerProfile(char const * action
, char const * desc
,
117 ClassActionCreator::Handler
*handler
,
118 int pw_req_flag
, int atomic
)
120 const Mgr::ActionProfile::Pointer profile
= new Mgr::ActionProfile(action
,
121 desc
, pw_req_flag
, atomic
, new ClassActionCreator(handler
));
122 registerProfile(profile
);
126 \ingroup CacheManagerInternal
127 * Locates an action in the actions registry ActionsList.
128 \retval NULL if Action not found
129 \retval CacheManagerAction* if the action was found
131 Mgr::ActionProfile::Pointer
132 CacheManager::findAction(char const * action
) const
134 Must(action
!= NULL
);
135 Menu::const_iterator a
;
137 debugs(16, 5, "CacheManager::findAction: looking for action " << action
);
138 for (a
= menu_
.begin(); a
!= menu_
.end(); ++a
) {
139 if (0 == strcmp((*a
)->name
, action
)) {
140 debugs(16, 6, " found");
145 debugs(16, 6, "Action not found.");
146 return Mgr::ActionProfilePointer();
150 CacheManager::createNamedAction(const char *actionName
)
154 Mgr::Command::Pointer cmd
= new Mgr::Command
;
155 cmd
->profile
= findAction(actionName
);
156 cmd
->params
.actionName
= actionName
;
158 Must(cmd
->profile
!= NULL
);
159 return cmd
->profile
->creator
->create(cmd
);
163 CacheManager::createRequestedAction(const Mgr::ActionParams
¶ms
)
165 Mgr::Command::Pointer cmd
= new Mgr::Command
;
166 cmd
->params
= params
;
167 cmd
->profile
= findAction(params
.actionName
.termedBuf());
168 Must(cmd
->profile
!= NULL
);
169 return cmd
->profile
->creator
->create(cmd
);
173 \ingroup CacheManagerInternal
174 * define whether the URL is a cache-manager URL and parse the action
175 * requested by the user. Checks via CacheManager::ActionProtection() that the
176 * item is accessible by the user.
177 \retval CacheManager::cachemgrStateData state object for the following handling
178 \retval NULL if the action can't be found or can't be accessed by the user
180 Mgr::Command::Pointer
181 CacheManager::ParseUrl(const char *url
)
184 LOCAL_ARRAY(char, host
, MAX_URL
);
185 LOCAL_ARRAY(char, request
, MAX_URL
);
186 LOCAL_ARRAY(char, password
, MAX_URL
);
187 t
= sscanf(url
, "cache_object://%[^/]/%[^@]@%s", host
, request
, password
);
190 xstrncpy(request
, "menu", MAX_URL
);
193 if (t
== 2 && request
[0] == '\0') {
195 * emx's sscanf insists of returning 2 because it sets request
198 xstrncpy(request
, "menu", MAX_URL
);
202 Mgr::ActionProfile::Pointer profile
= findAction(request
);
204 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
208 const char *prot
= ActionProtection(profile
);
209 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
210 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
214 Mgr::Command::Pointer cmd
= new Mgr::Command
;
215 cmd
->profile
= profile
;
216 cmd
->params
.httpUri
= url
;
217 cmd
->params
.userName
= String();
218 cmd
->params
.password
= t
== 3 ? String(password
) : String();
219 cmd
->params
.actionName
= request
;
223 /// \ingroup CacheManagerInternal
225 \ingroup CacheManagerInternal
226 * Decodes the headers needed to perform user authentication and fills
227 * the details into the cachemgrStateData argument
230 CacheManager::ParseHeaders(const HttpRequest
* request
, Mgr::ActionParams
¶ms
)
234 params
.httpMethod
= request
->method
.id();
235 params
.httpFlags
= request
->flags
;
237 #if HAVE_AUTH_MODULE_BASIC
238 // TODO: use the authentication system decode to retrieve these details properly.
240 /* base 64 _decoded_ user:passwd pair */
241 const char *basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
246 const char *passwd_del
;
247 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
248 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
252 /* found user:password pair, reset old values */
253 params
.userName
.limitInit(basic_cookie
, passwd_del
- basic_cookie
);
254 params
.password
= passwd_del
+ 1;
256 /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */
257 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" <<
258 params
.userName
<< "' passwd: '" << params
.password
<< "'");
263 \ingroup CacheManagerInternal
265 \retval 0 if mgr->password is good or "none"
266 \retval 1 if mgr->password is "disable"
267 \retval !0 if mgr->password does not match configured password
270 CacheManager::CheckPassword(const Mgr::Command
&cmd
)
272 assert(cmd
.profile
!= NULL
);
273 const char *action
= cmd
.profile
->name
;
274 char *pwd
= PasswdGet(Config
.passwd_list
, action
);
276 debugs(16, 4, "CacheManager::CheckPassword for action " << action
);
279 return cmd
.profile
->isPwReq
;
281 if (strcmp(pwd
, "disable") == 0)
284 if (strcmp(pwd
, "none") == 0)
287 if (!cmd
.params
.password
.size())
290 return cmd
.params
.password
!= pwd
;
294 \ingroup CacheManagerAPI
295 * Main entry point in the Cache Manager's activity. Gets called as part
296 * of the forward chain if the right URL is detected there. Initiates
297 * all needed internal work and renders the response.
300 CacheManager::Start(const Comm::ConnectionPointer
&client
, HttpRequest
* request
, StoreEntry
* entry
)
302 ErrorState
*err
= NULL
;
303 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
305 Mgr::Command::Pointer cmd
= ParseUrl(entry
->url());
307 err
= errorCon(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
308 err
->url
= xstrdup(entry
->url());
309 errorAppendEntry(entry
, err
);
310 entry
->expires
= squid_curtime
;
314 const char *actionName
= cmd
->profile
->name
;
316 entry
->expires
= squid_curtime
;
318 debugs(16, 5, "CacheManager: " << client
<< " requesting '" << actionName
<< "'");
320 /* get additional info from request headers */
321 ParseHeaders(request
, cmd
->params
);
323 const char *userName
= cmd
->params
.userName
.size() ?
324 cmd
->params
.userName
.termedBuf() : "unknown";
328 if (CheckPassword(*cmd
) != 0) {
329 /* build error message */
330 ErrorState
*errState
;
332 errState
= errorCon(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
333 /* warn if user specified incorrect password */
335 if (cmd
->params
.password
.size()) {
336 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
338 client
<< ": incorrect password for '" <<
341 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
343 client
<< ": password needed for '" <<
347 rep
= errState
->BuildHttpReply();
349 errorStateFree(errState
);
351 #if HAVE_AUTH_MODULE_BASIC
353 * add Authenticate header using action name as a realm because
354 * password depends on the action
356 rep
->header
.putAuth("Basic", actionName
);
359 /* store the reply */
360 entry
->replaceHttpReply(rep
);
362 entry
->expires
= squid_curtime
;
369 debugs(16, 2, "CacheManager: " <<
371 client
<< " requesting '" <<
374 if (UsingSmp() && IamWorkerProcess()) {
375 AsyncJob::Start(new Mgr::Forwarder(client
, cmd
->params
, request
, entry
));
379 Mgr::Action::Pointer action
= cmd
->profile
->creator
->create(cmd
);
380 Must(action
!= NULL
);
381 action
->run(entry
, true);
385 \ingroup CacheManagerInternal
386 * Renders the protection level text for an action.
387 * Also doubles as a check for the protection level.
390 CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer
&profile
)
392 assert(profile
!= NULL
);
393 const char *pwd
= PasswdGet(Config
.passwd_list
, profile
->name
);
396 return profile
->isPwReq
? "hidden" : "public";
398 if (!strcmp(pwd
, "disable"))
401 if (strcmp(pwd
, "none") == 0)
408 \ingroup CacheManagerInternal
409 * gets from the global Config the password the user would need to supply
410 * for the action she queried
413 CacheManager::PasswdGet(cachemgr_passwd
* a
, const char *action
)
418 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
419 if (0 == strcmp(w
->key
, action
))
422 if (0 == strcmp(w
->key
, "all"))
432 CacheManager
* CacheManager::instance
=0;
435 \ingroup CacheManagerAPI
436 * Singleton accessor method.
439 CacheManager::GetInstance()
442 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
443 instance
= new CacheManager
;
444 Mgr::RegisterBasics();