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.
36 #include "CacheManager.h"
37 #include "errorpage.h"
38 #include "HttpReply.h"
39 #include "HttpRequest.h"
42 #include "SquidTime.h"
46 /// \ingroup CacheManagerInternal
47 #define MGR_PASSWD_SZ 128
51 \ingroup CacheManagerInternals
52 * Constructor. Its purpose is to register internal commands
54 CacheManager::CacheManager()
56 registerAction(new OfflineToggleAction
);
57 registerAction(new ShutdownAction
);
58 registerAction(new ReconfigureAction
);
59 registerAction(new MenuAction(this));
63 \ingroup CacheManagerAPI
64 * Registers a C-style action, which is implemented as a pointer to a function
65 * taking as argument a pointer to a StoreEntry and returning void.
66 * Implemented via CacheManagerActionLegacy.
69 CacheManager::registerAction(char const * action
, char const * desc
, OBJH
* handler
, int pw_req_flag
, int atomic
)
71 debugs(16, 3, "CacheManager::registerAction: registering legacy " << action
);
72 registerAction(new CacheManagerActionLegacy(action
,desc
,pw_req_flag
,atomic
,handler
));
76 \ingroup CacheManagerAPI
77 * Registers a C++-style action, via a poiner to a subclass of
78 * a CacheManagerAction object, whose run() method will be invoked when
79 * CacheManager identifies that the user has requested the action.
82 CacheManager::registerAction(CacheManagerAction
*anAction
)
84 char *action
= anAction
->action
;
85 if (findAction(action
) != NULL
) {
86 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action
<< "'. Skipping.");
90 assert (strstr (" ", action
) == NULL
);
92 ActionsList
+= anAction
;
94 debugs(16, 3, "CacheManager::registerAction: registered " << action
);
99 \ingroup CacheManagerInternal
100 * Locates an action in the actions registry ActionsList.
101 \retval NULL if Action not found
102 \retval CacheManagerAction* if the action was found
105 CacheManager::findAction(char const * action
)
107 CacheManagerActionList::iterator a
;
109 debugs(16, 5, "CacheManager::findAction: looking for action " << action
);
110 for ( a
= ActionsList
.begin(); a
!= ActionsList
.end(); a
++) {
111 if (0 == strcmp((*a
)->action
, action
)) {
112 debugs(16, 6, " found");
117 debugs(16, 6, "Action not found.");
122 \ingroup CacheManagerInternal
123 * define whether the URL is a cache-manager URL and parse the action
124 * requested by the user. Checks via CacheManager::ActionProtection() that the
125 * item is accessible by the user.
126 \retval CacheManager::cachemgrStateData state object for the following handling
127 \retval NULL if the action can't be found or can't be accessed by the user
129 CacheManager::cachemgrStateData
*
130 CacheManager::ParseUrl(const char *url
)
133 LOCAL_ARRAY(char, host
, MAX_URL
);
134 LOCAL_ARRAY(char, request
, MAX_URL
);
135 LOCAL_ARRAY(char, password
, MAX_URL
);
136 CacheManagerAction
*a
;
137 cachemgrStateData
*mgr
= NULL
;
139 t
= sscanf(url
, "cache_object://%[^/]/%[^@]@%s", host
, request
, password
);
142 xstrncpy(request
, "menu", MAX_URL
);
145 * emx's sscanf insists of returning 2 because it sets request
148 } else if (request
[0] == '\0') {
149 xstrncpy(request
, "menu", MAX_URL
);
152 } else if ((a
= findAction(request
)) == NULL
) {
153 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
156 prot
= ActionProtection(a
);
158 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
159 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
164 /* set absent entries to NULL so we can test if they are present later */
165 mgr
= (cachemgrStateData
*)xcalloc(1, sizeof(cachemgrStateData
));
167 mgr
->user_name
= NULL
;
169 mgr
->passwd
= t
== 3 ? xstrdup(password
) : NULL
;
171 mgr
->action
= xstrdup(request
);
176 /// \ingroup CacheManagerInternal
178 \ingroup CacheManagerInternal
179 * Decodes the headers needed to perform user authentication and fills
180 * the details into the cachemgrStateData argument
183 CacheManager::ParseHeaders(cachemgrStateData
* mgr
, const HttpRequest
* request
)
185 const char *basic_cookie
; /* base 64 _decoded_ user:passwd pair */
186 const char *passwd_del
;
187 assert(mgr
&& request
);
188 basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
193 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
194 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
198 /* found user:password pair, reset old values */
199 safe_free(mgr
->user_name
);
201 safe_free(mgr
->passwd
);
203 mgr
->user_name
= xstrdup(basic_cookie
);
205 mgr
->user_name
[passwd_del
- basic_cookie
] = '\0';
207 mgr
->passwd
= xstrdup(passwd_del
+ 1);
209 /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */
210 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << mgr
->user_name
<< "' passwd: '" << mgr
->passwd
<< "'");
214 \ingroup CacheManagerInternal
216 \retval 0 if mgr->password is good or "none"
217 \retval 1 if mgr->password is "disable"
218 \retval !0 if mgr->password does not match configured password
221 CacheManager::CheckPassword(cachemgrStateData
* mgr
)
223 char *pwd
= PasswdGet(Config
.passwd_list
, mgr
->action
);
224 CacheManagerAction
*a
= findAction(mgr
->action
);
226 debugs(16, 4, "CacheManager::CheckPassword for action " << mgr
->action
);
230 return a
->flags
.pw_req
;
232 if (strcmp(pwd
, "disable") == 0)
235 if (strcmp(pwd
, "none") == 0)
241 return strcmp(pwd
, mgr
->passwd
);
244 /// \ingroup CacheManagerInternal
246 CacheManager::StateFree(cachemgrStateData
* mgr
)
248 safe_free(mgr
->action
);
249 safe_free(mgr
->user_name
);
250 safe_free(mgr
->passwd
);
251 mgr
->entry
->unlock();
256 \ingroup CacheManagerAPI
257 * Main entry point in the Cache Manager's activity. Gets called as part
258 * of the forward chain if the right URL is detected there. Initiates
259 * all needed internal work and renders the response.
262 CacheManager::Start(int fd
, HttpRequest
* request
, StoreEntry
* entry
)
264 cachemgrStateData
*mgr
= NULL
;
265 ErrorState
*err
= NULL
;
266 CacheManagerAction
*a
;
267 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
269 if ((mgr
= ParseUrl(entry
->url())) == NULL
) {
270 err
= errorCon(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
271 err
->url
= xstrdup(entry
->url());
272 errorAppendEntry(entry
, err
);
273 entry
->expires
= squid_curtime
;
280 entry
->expires
= squid_curtime
;
282 debugs(16, 5, "CacheManager: " << fd_table
[fd
].ipaddr
<< " requesting '" << mgr
->action
<< "'");
284 /* get additional info from request headers */
285 ParseHeaders(mgr
, request
);
289 if (CheckPassword(mgr
) != 0) {
290 /* build error message */
293 err
= errorCon(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
294 /* warn if user specified incorrect password */
297 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
298 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
299 fd_table
[fd
].ipaddr
<< ": incorrect password for '" <<
300 mgr
->action
<< "'" );
302 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
303 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
304 fd_table
[fd
].ipaddr
<< ": password needed for '" <<
305 mgr
->action
<< "'" );
307 rep
= err
->BuildHttpReply();
312 * add Authenticate header, use 'action' as a realm because
313 * password depends on action
315 rep
->header
.putAuth("Basic", mgr
->action
);
317 /* store the reply */
318 entry
->replaceHttpReply(rep
);
320 entry
->expires
= squid_curtime
;
329 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
330 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
331 fd_table
[fd
].ipaddr
<< " requesting '" <<
332 mgr
->action
<< "'" );
333 /* retrieve object requested */
334 a
= findAction(mgr
->action
);
340 HttpVersion
version(1,0);
341 HttpReply
*rep
= new HttpReply
;
342 rep
->setHeaders(version
,
347 squid_curtime
, /* LMT */
349 entry
->replaceHttpReply(rep
);
362 /// \ingroup CacheManagerInternal
363 void CacheManager::ShutdownAction::run(StoreEntry
*sentry
)
365 debugs(16, DBG_CRITICAL
, "Shutdown by Cache Manager command.");
368 /// \ingroup CacheManagerInternal
369 CacheManager::ShutdownAction::ShutdownAction() : CacheManagerAction("shutdown","Shut Down the Squid Process", 1, 1) { }
371 /// \ingroup CacheManagerInternal
373 CacheManager::ReconfigureAction::run(StoreEntry
* sentry
)
375 debugs(16, DBG_IMPORTANT
, "Reconfigure by Cache Manager command.");
376 storeAppendPrintf(sentry
, "Reconfiguring Squid Process ....");
379 /// \ingroup CacheManagerInternal
380 CacheManager::ReconfigureAction::ReconfigureAction() : CacheManagerAction("reconfigure","Reconfigure Squid", 1, 1) { }
382 /// \ingroup CacheManagerInternal
384 CacheManager::OfflineToggleAction::run(StoreEntry
* sentry
)
386 Config
.onoff
.offline
= !Config
.onoff
.offline
;
387 debugs(16, DBG_IMPORTANT
, "offline_mode now " << (Config
.onoff
.offline
? "ON" : "OFF") << " by Cache Manager request.");
389 storeAppendPrintf(sentry
, "offline_mode is now %s\n",
390 Config
.onoff
.offline
? "ON" : "OFF");
392 /// \ingroup CacheManagerInternal
393 CacheManager::OfflineToggleAction::OfflineToggleAction() : CacheManagerAction ("offline_toggle", "Toggle offline_mode setting", 1, 1) { }
396 \ingroup CacheManagerInternal
397 * Renders the protection level text for an action.
398 * Also doubles as a check for the protection level.
401 CacheManager::ActionProtection(const CacheManagerAction
* at
)
405 pwd
= PasswdGet(Config
.passwd_list
, at
->action
);
408 return at
->flags
.pw_req
? "hidden" : "public";
410 if (!strcmp(pwd
, "disable"))
413 if (strcmp(pwd
, "none") == 0)
419 /// \ingroup CacheManagerInternal
421 CacheManager::MenuAction::run(StoreEntry
* sentry
)
423 CacheManagerActionList::iterator a
;
425 debugs(16, 4, "CacheManager::MenuCommand invoked");
426 for (a
= cmgr
->ActionsList
.begin(); a
!= cmgr
->ActionsList
.end(); ++a
) {
427 debugs(16, 5, " showing action " << (*a
)->action
);
428 storeAppendPrintf(sentry
, " %-22s\t%-32s\t%s\n",
429 (*a
)->action
, (*a
)->desc
, cmgr
->ActionProtection(*a
));
432 /// \ingroup CacheManagerInternal
433 CacheManager::MenuAction::MenuAction(CacheManager
*aMgr
) : CacheManagerAction ("menu", "Cache Manager Menu", 0, 1), cmgr(aMgr
) { }
436 \ingroup CacheManagerInternal
437 * gets from the global Config the password the user would need to supply
438 * for the action she queried
441 CacheManager::PasswdGet(cachemgr_passwd
* a
, const char *action
)
446 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
447 if (0 == strcmp(w
->key
, action
))
450 if (0 == strcmp(w
->key
, "all"))
460 CacheManager
* CacheManager::instance
=0;
463 \ingroup CacheManagerAPI
464 * Singleton accessor method.
467 CacheManager::GetInstance()
470 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
471 instance
= new CacheManager
;
477 /// \ingroup CacheManagerInternal
478 void CacheManagerActionLegacy::run(StoreEntry
*sentry
)
482 /// \ingroup CacheManagerInternal
483 CacheManagerAction::CacheManagerAction(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
)
485 flags
.pw_req
= isPwReq
;
486 flags
.atomic
= isAtomic
;
487 action
= xstrdup (anAction
);
488 desc
= xstrdup (aDesc
);
490 /// \ingroup CacheManagerInternal
491 CacheManagerAction::~CacheManagerAction()
497 /// \ingroup CacheManagerInternal
498 CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
, OBJH
*aHandler
) : CacheManagerAction(anAction
, aDesc
, isPwReq
, isAtomic
), handler(aHandler
)