2 * DEBUG: section 16 Cache Manager Objects
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "base/TextException.h"
35 #include "CacheMgrPasswd.h"
36 #include "CacheManager.h"
37 #include "comm/Connection.h"
39 #include "errorpage.h"
41 #include "HttpReply.h"
42 #include "HttpRequest.h"
43 #include "mgr/ActionCreator.h"
44 #include "mgr/Action.h"
45 #include "mgr/ActionProfile.h"
46 #include "mgr/BasicActions.h"
47 #include "mgr/Command.h"
48 #include "mgr/Forwarder.h"
49 #include "mgr/FunAction.h"
50 #include "mgr/QueryParams.h"
53 #include "SquidConfig.h"
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 {
80 /// Registers new profiles, ignoring attempts to register a duplicate
82 CacheManager::registerProfile(const Mgr::ActionProfile::Pointer
&profile
)
84 Must(profile
!= NULL
);
85 if (std::find(menu_
.begin(), menu_
.end(), profile
) == menu_
.end()) {
86 menu_
.push_back(profile
);
87 debugs(16, 3, HERE
<< "registered profile: " << *profile
);
89 debugs(16, 2, HERE
<< "skipped duplicate profile: " << *profile
);
94 \ingroup CacheManagerAPI
95 * Registers a C-style action, which is implemented as a pointer to a function
96 * taking as argument a pointer to a StoreEntry and returning void.
97 * Implemented via CacheManagerActionLegacy.
100 CacheManager::registerProfile(char const * action
, char const * desc
, OBJH
* handler
, int pw_req_flag
, int atomic
)
102 debugs(16, 3, HERE
<< "registering legacy " << action
);
103 const Mgr::ActionProfile::Pointer profile
= new Mgr::ActionProfile(action
,
104 desc
, pw_req_flag
, atomic
, new Mgr::FunActionCreator(handler
));
105 registerProfile(profile
);
109 * \ingroup CacheManagerAPI
110 * Registers a C++-style action, via a pointer to a subclass of
111 * a CacheManagerAction object, whose run() method will be invoked when
112 * CacheManager identifies that the user has requested the action.
115 CacheManager::registerProfile(char const * action
, char const * desc
,
116 ClassActionCreator::Handler
*handler
,
117 int pw_req_flag
, int atomic
)
119 const Mgr::ActionProfile::Pointer profile
= new Mgr::ActionProfile(action
,
120 desc
, pw_req_flag
, atomic
, new ClassActionCreator(handler
));
121 registerProfile(profile
);
125 \ingroup CacheManagerInternal
126 * Locates an action in the actions registry ActionsList.
127 \retval NULL if Action not found
128 \retval CacheManagerAction* if the action was found
130 Mgr::ActionProfile::Pointer
131 CacheManager::findAction(char const * action
) const
133 Must(action
!= NULL
);
134 Menu::const_iterator a
;
136 debugs(16, 5, "CacheManager::findAction: looking for action " << action
);
137 for (a
= menu_
.begin(); a
!= menu_
.end(); ++a
) {
138 if (0 == strcmp((*a
)->name
, action
)) {
139 debugs(16, 6, " found");
144 debugs(16, 6, "Action not found.");
145 return Mgr::ActionProfilePointer();
149 CacheManager::createNamedAction(const char *actionName
)
153 Mgr::Command::Pointer cmd
= new Mgr::Command
;
154 cmd
->profile
= findAction(actionName
);
155 cmd
->params
.actionName
= actionName
;
157 Must(cmd
->profile
!= NULL
);
158 return cmd
->profile
->creator
->create(cmd
);
162 CacheManager::createRequestedAction(const Mgr::ActionParams
¶ms
)
164 Mgr::Command::Pointer cmd
= new Mgr::Command
;
165 cmd
->params
= params
;
166 cmd
->profile
= findAction(params
.actionName
.termedBuf());
167 Must(cmd
->profile
!= NULL
);
168 return cmd
->profile
->creator
->create(cmd
);
172 \ingroup CacheManagerInternal
173 * define whether the URL is a cache-manager URL and parse the action
174 * requested by the user. Checks via CacheManager::ActionProtection() that the
175 * item is accessible by the user.
176 \retval CacheManager::cachemgrStateData state object for the following handling
177 \retval NULL if the action can't be found or can't be accessed by the user
179 Mgr::Command::Pointer
180 CacheManager::ParseUrl(const char *url
)
183 LOCAL_ARRAY(char, host
, MAX_URL
);
184 LOCAL_ARRAY(char, request
, MAX_URL
);
185 LOCAL_ARRAY(char, password
, MAX_URL
);
186 LOCAL_ARRAY(char, params
, MAX_URL
);
192 int len
= strlen(url
);
194 t
= sscanf(url
, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host
, request
, &pos
, password
, params
);
196 t
= sscanf(url
, "cache_object://%[^/]/%[^?]%n?%s", host
, request
, &pos
, params
);
199 t
= sscanf(url
, "http://%[^/]/squid-internal-mgr/%[^?]%n?%s", host
, request
, &pos
, params
);
202 t
= sscanf(url
, "https://%[^/]/squid-internal-mgr/%[^?]%n?%s", host
, request
, &pos
, params
);
205 if (strncmp("cache_object://",url
,15)==0)
206 xstrncpy(request
, "menu", MAX_URL
);
208 xstrncpy(request
, "index", MAX_URL
);
212 if (t
== 2 && request
[0] == '\0') {
214 * emx's sscanf insists of returning 2 because it sets request
217 if (strncmp("cache_object://",url
,15)==0)
218 xstrncpy(request
, "menu", MAX_URL
);
220 xstrncpy(request
, "index", MAX_URL
);
224 debugs(16, 3, HERE
<< "MGR request: t=" << t
<< ", host='" << host
<< "', request='" << request
<< "', pos=" << pos
<<
225 ", password='" << password
<< "', params='" << params
<< "'");
227 Mgr::ActionProfile::Pointer profile
= findAction(request
);
229 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
233 const char *prot
= ActionProtection(profile
);
234 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
235 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
239 Mgr::Command::Pointer cmd
= new Mgr::Command
;
240 if (!Mgr::QueryParams::Parse(params
, cmd
->params
.queryParams
))
242 cmd
->profile
= profile
;
243 cmd
->params
.httpUri
= url
;
244 cmd
->params
.userName
= String();
245 cmd
->params
.password
= password
;
246 cmd
->params
.actionName
= request
;
250 /// \ingroup CacheManagerInternal
252 \ingroup CacheManagerInternal
253 * Decodes the headers needed to perform user authentication and fills
254 * the details into the cachemgrStateData argument
257 CacheManager::ParseHeaders(const HttpRequest
* request
, Mgr::ActionParams
¶ms
)
261 params
.httpMethod
= request
->method
.id();
262 params
.httpFlags
= request
->flags
;
264 #if HAVE_AUTH_MODULE_BASIC
265 // TODO: use the authentication system decode to retrieve these details properly.
267 /* base 64 _decoded_ user:passwd pair */
268 const char *basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
273 const char *passwd_del
;
274 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
275 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
279 /* found user:password pair, reset old values */
280 params
.userName
.limitInit(basic_cookie
, passwd_del
- basic_cookie
);
281 params
.password
= passwd_del
+ 1;
283 /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */
284 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" <<
285 params
.userName
<< "' passwd: '" << params
.password
<< "'");
290 \ingroup CacheManagerInternal
292 \retval 0 if mgr->password is good or "none"
293 \retval 1 if mgr->password is "disable"
294 \retval !0 if mgr->password does not match configured password
297 CacheManager::CheckPassword(const Mgr::Command
&cmd
)
299 assert(cmd
.profile
!= NULL
);
300 const char *action
= cmd
.profile
->name
;
301 char *pwd
= PasswdGet(Config
.passwd_list
, action
);
303 debugs(16, 4, "CacheManager::CheckPassword for action " << action
);
306 return cmd
.profile
->isPwReq
;
308 if (strcmp(pwd
, "disable") == 0)
311 if (strcmp(pwd
, "none") == 0)
314 if (!cmd
.params
.password
.size())
317 return cmd
.params
.password
!= pwd
;
321 \ingroup CacheManagerAPI
322 * Main entry point in the Cache Manager's activity. Gets called as part
323 * of the forward chain if the right URL is detected there. Initiates
324 * all needed internal work and renders the response.
327 CacheManager::Start(const Comm::ConnectionPointer
&client
, HttpRequest
* request
, StoreEntry
* entry
)
329 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
331 Mgr::Command::Pointer cmd
= ParseUrl(entry
->url());
333 ErrorState
*err
= new ErrorState(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
334 err
->url
= xstrdup(entry
->url());
335 errorAppendEntry(entry
, err
);
336 entry
->expires
= squid_curtime
;
340 const char *actionName
= cmd
->profile
->name
;
342 entry
->expires
= squid_curtime
;
344 debugs(16, 5, "CacheManager: " << client
<< " requesting '" << actionName
<< "'");
346 /* get additional info from request headers */
347 ParseHeaders(request
, cmd
->params
);
349 const char *userName
= cmd
->params
.userName
.size() ?
350 cmd
->params
.userName
.termedBuf() : "unknown";
354 if (CheckPassword(*cmd
) != 0) {
355 /* build error message */
356 ErrorState
errState(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
357 /* warn if user specified incorrect password */
359 if (cmd
->params
.password
.size()) {
360 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
362 client
<< ": incorrect password for '" <<
365 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
367 client
<< ": password needed for '" <<
371 HttpReply
*rep
= errState
.BuildHttpReply();
373 #if HAVE_AUTH_MODULE_BASIC
375 * add Authenticate header using action name as a realm because
376 * password depends on the action
378 rep
->header
.putAuth("Basic", actionName
);
380 // Allow cachemgr and other XHR scripts access to our version string
381 if (request
->header
.has(HDR_ORIGIN
)) {
382 rep
->header
.putExt("Access-Control-Allow-Origin",request
->header
.getStr(HDR_ORIGIN
));
383 #if HAVE_AUTH_MODULE_BASIC
384 rep
->header
.putExt("Access-Control-Allow-Credentials","true");
386 rep
->header
.putExt("Access-Control-Expose-Headers","Server");
389 /* store the reply */
390 entry
->replaceHttpReply(rep
);
392 entry
->expires
= squid_curtime
;
399 if (request
->header
.has(HDR_ORIGIN
)) {
400 cmd
->params
.httpOrigin
= request
->header
.getStr(HDR_ORIGIN
);
403 debugs(16, 2, "CacheManager: " <<
405 client
<< " requesting '" <<
408 // special case: /squid-internal-mgr/ index page
409 if (!strcmp(cmd
->profile
->name
, "index")) {
410 ErrorState
err(MGR_INDEX
, HTTP_OK
, request
);
411 err
.url
= xstrdup(entry
->url());
412 HttpReply
*rep
= err
.BuildHttpReply();
413 if (strncmp(rep
->body
.content(),"Internal Error:", 15) == 0)
414 rep
->sline
.status
= HTTP_NOT_FOUND
;
415 // Allow cachemgr and other XHR scripts access to our version string
416 if (request
->header
.has(HDR_ORIGIN
)) {
417 rep
->header
.putExt("Access-Control-Allow-Origin",request
->header
.getStr(HDR_ORIGIN
));
418 #if HAVE_AUTH_MODULE_BASIC
419 rep
->header
.putExt("Access-Control-Allow-Credentials","true");
421 rep
->header
.putExt("Access-Control-Expose-Headers","Server");
423 entry
->replaceHttpReply(rep
);
428 if (UsingSmp() && IamWorkerProcess()) {
429 // is client the right connection to pass here?
430 AsyncJob::Start(new Mgr::Forwarder(client
, cmd
->params
, request
, entry
));
434 Mgr::Action::Pointer action
= cmd
->profile
->creator
->create(cmd
);
435 Must(action
!= NULL
);
436 action
->run(entry
, true);
440 \ingroup CacheManagerInternal
441 * Renders the protection level text for an action.
442 * Also doubles as a check for the protection level.
445 CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer
&profile
)
447 assert(profile
!= NULL
);
448 const char *pwd
= PasswdGet(Config
.passwd_list
, profile
->name
);
451 return profile
->isPwReq
? "hidden" : "public";
453 if (!strcmp(pwd
, "disable"))
456 if (strcmp(pwd
, "none") == 0)
463 * \ingroup CacheManagerInternal
464 * gets from the global Config the password the user would need to supply
465 * for the action she queried
468 CacheManager::PasswdGet(CacheMgrPasswd
* a
, const char *action
)
473 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
474 if (0 == strcmp(w
->key
, action
))
477 if (0 == strcmp(w
->key
, "all"))
487 CacheManager
* CacheManager::instance
=0;
490 \ingroup CacheManagerAPI
491 * Singleton accessor method.
494 CacheManager::GetInstance()
497 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
498 instance
= new CacheManager
;
499 Mgr::RegisterBasics();