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 #include "mgr/QueryParams.h"
53 #include "protos.h" /* 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 LOCAL_ARRAY(char, params
, MAX_URL
);
193 int len
= strlen(url
);
195 t
= sscanf(url
, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host
, request
, &pos
, password
, params
);
197 if (pos
>0 && url
[pos
] == '?') {
200 xstrncpy(params
, url
+ pos
, sizeof(params
));
204 xstrncpy(request
, "menu", MAX_URL
);
207 if (t
== 2 && request
[0] == '\0') {
209 * emx's sscanf insists of returning 2 because it sets request
212 xstrncpy(request
, "menu", MAX_URL
);
216 Mgr::ActionProfile::Pointer profile
= findAction(request
);
218 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
222 const char *prot
= ActionProtection(profile
);
223 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
224 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
228 Mgr::Command::Pointer cmd
= new Mgr::Command
;
229 if (!Mgr::QueryParams::Parse(params
, cmd
->params
.queryParams
))
231 cmd
->profile
= profile
;
232 cmd
->params
.httpUri
= url
;
233 cmd
->params
.userName
= String();
234 cmd
->params
.password
= password
;
235 cmd
->params
.actionName
= request
;
239 /// \ingroup CacheManagerInternal
241 \ingroup CacheManagerInternal
242 * Decodes the headers needed to perform user authentication and fills
243 * the details into the cachemgrStateData argument
246 CacheManager::ParseHeaders(const HttpRequest
* request
, Mgr::ActionParams
¶ms
)
250 params
.httpMethod
= request
->method
.id();
251 params
.httpFlags
= request
->flags
;
253 #if HAVE_AUTH_MODULE_BASIC
254 // TODO: use the authentication system decode to retrieve these details properly.
256 /* base 64 _decoded_ user:passwd pair */
257 const char *basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
262 const char *passwd_del
;
263 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
264 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
268 /* found user:password pair, reset old values */
269 params
.userName
.limitInit(basic_cookie
, passwd_del
- basic_cookie
);
270 params
.password
= passwd_del
+ 1;
272 /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */
273 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" <<
274 params
.userName
<< "' passwd: '" << params
.password
<< "'");
279 \ingroup CacheManagerInternal
281 \retval 0 if mgr->password is good or "none"
282 \retval 1 if mgr->password is "disable"
283 \retval !0 if mgr->password does not match configured password
286 CacheManager::CheckPassword(const Mgr::Command
&cmd
)
288 assert(cmd
.profile
!= NULL
);
289 const char *action
= cmd
.profile
->name
;
290 char *pwd
= PasswdGet(Config
.passwd_list
, action
);
292 debugs(16, 4, "CacheManager::CheckPassword for action " << action
);
295 return cmd
.profile
->isPwReq
;
297 if (strcmp(pwd
, "disable") == 0)
300 if (strcmp(pwd
, "none") == 0)
303 if (!cmd
.params
.password
.size())
306 return cmd
.params
.password
!= pwd
;
310 \ingroup CacheManagerAPI
311 * Main entry point in the Cache Manager's activity. Gets called as part
312 * of the forward chain if the right URL is detected there. Initiates
313 * all needed internal work and renders the response.
316 CacheManager::Start(const Comm::ConnectionPointer
&client
, HttpRequest
* request
, StoreEntry
* entry
)
318 ErrorState
*err
= NULL
;
319 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
321 Mgr::Command::Pointer cmd
= ParseUrl(entry
->url());
323 err
= errorCon(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
324 err
->url
= xstrdup(entry
->url());
325 errorAppendEntry(entry
, err
);
326 entry
->expires
= squid_curtime
;
330 const char *actionName
= cmd
->profile
->name
;
332 entry
->expires
= squid_curtime
;
334 debugs(16, 5, "CacheManager: " << client
<< " requesting '" << actionName
<< "'");
336 /* get additional info from request headers */
337 ParseHeaders(request
, cmd
->params
);
339 const char *userName
= cmd
->params
.userName
.size() ?
340 cmd
->params
.userName
.termedBuf() : "unknown";
344 if (CheckPassword(*cmd
) != 0) {
345 /* build error message */
346 ErrorState
*errState
;
348 errState
= errorCon(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
349 /* warn if user specified incorrect password */
351 if (cmd
->params
.password
.size()) {
352 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
354 client
<< ": incorrect password for '" <<
357 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
359 client
<< ": password needed for '" <<
363 rep
= errState
->BuildHttpReply();
365 errorStateFree(errState
);
367 #if HAVE_AUTH_MODULE_BASIC
369 * add Authenticate header using action name as a realm because
370 * password depends on the action
372 rep
->header
.putAuth("Basic", actionName
);
375 /* store the reply */
376 entry
->replaceHttpReply(rep
);
378 entry
->expires
= squid_curtime
;
385 debugs(16, 2, "CacheManager: " <<
387 client
<< " requesting '" <<
390 if (UsingSmp() && IamWorkerProcess()) {
391 // is client the right connection to pass here?
392 AsyncJob::Start(new Mgr::Forwarder(client
, cmd
->params
, request
, entry
));
396 Mgr::Action::Pointer action
= cmd
->profile
->creator
->create(cmd
);
397 Must(action
!= NULL
);
398 action
->run(entry
, true);
402 \ingroup CacheManagerInternal
403 * Renders the protection level text for an action.
404 * Also doubles as a check for the protection level.
407 CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer
&profile
)
409 assert(profile
!= NULL
);
410 const char *pwd
= PasswdGet(Config
.passwd_list
, profile
->name
);
413 return profile
->isPwReq
? "hidden" : "public";
415 if (!strcmp(pwd
, "disable"))
418 if (strcmp(pwd
, "none") == 0)
425 \ingroup CacheManagerInternal
426 * gets from the global Config the password the user would need to supply
427 * for the action she queried
430 CacheManager::PasswdGet(cachemgr_passwd
* a
, const char *action
)
435 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
436 if (0 == strcmp(w
->key
, action
))
439 if (0 == strcmp(w
->key
, "all"))
449 CacheManager
* CacheManager::instance
=0;
452 \ingroup CacheManagerAPI
453 * Singleton accessor method.
456 CacheManager::GetInstance()
459 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
460 instance
= new CacheManager
;
461 Mgr::RegisterBasics();