Squid Web Cache v8/master
Loading...
Searching...
No Matches
Notes.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9#include "squid.h"
10#include "AccessLogEntry.h"
11#include "acl/FilledChecklist.h"
12#include "acl/Gadgets.h"
13#include "acl/Tree.h"
14#include "client_side.h"
15#include "ConfigParser.h"
16#include "globals.h"
17#include "http/Stream.h"
18#include "HttpReply.h"
19#include "HttpRequest.h"
20#include "parser/Tokenizer.h"
21#include "sbuf/Stream.h"
22#include "sbuf/StringConvert.h"
23#include "SquidConfig.h"
24#include "Store.h"
25#include "StrList.h"
26
27#include <algorithm>
28#include <ostream>
29#include <string>
30
36
37Note::Value::Value(const char *aVal, const bool quoted, const char *descr, const Method m)
38 : aclList(nullptr), valueFormat(nullptr), theValue(aVal), theMethod(m)
39{
40 if (quoted) {
41 valueFormat = new Format::Format(descr ? descr : "Notes");
43 delete valueFormat;
44 throw TextException(ToSBuf("failed to parse annotation value ", theValue), Here());
45 }
46 }
47}
48
49const SBuf &
51{
52 if (al && valueFormat) {
53 static MemBuf mb;
54 mb.reset();
55 valueFormat->assemble(mb, al, 0);
56 theFormattedValue.assign(mb.content());
57 return theFormattedValue;
58 }
59 return theValue;
60}
61
63Note::addValue(const char *value, const bool quoted, const char *descr, const Value::Method m)
64{
65 values.push_back(new Value(value, quoted, descr, m));
66 return values.back();
67}
68
69bool
70Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al, SBuf &matched)
71{
72 ACLFilledChecklist ch(nullptr, request);
73 ch.updateAle(al);
74 ch.updateReply(reply);
75 ch.syncAle(request, nullptr);
76
77 for (const auto &v: values) {
78 assert(v->aclList);
79 const auto &ret = ch.fastCheck(v->aclList);
80 debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
81 ", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
82 if (ret.allowed()) {
83 matched = v->format(al);
84 return true;
85 }
86 }
87 matched.clear();
88 return false;
89}
90
91void
93{
94 for (const auto &v: values) {
95 const SBuf &formatted = v->format(al);
96 if (!pairs->empty() && v->method() == Value::mhReplace)
97 pairs->remove(theKey);
98 if (delimiters)
99 pairs->addStrList(key(), formatted, *delimiters);
100 else
101 pairs->add(key(), formatted);
102 }
103}
104
105void
106Note::printAsNoteDirective(StoreEntry * const entry, const char * const directiveName) const
107{
108 PackableStream os(*entry);
109 for (const auto &v: values) {
110 os << directiveName << ' ' << key() << ' ' << ConfigParser::QuoteString(SBufToString(v->value()));
111 if (v->aclList) {
112 // TODO: Use Acl::dump() after fixing the XXX in dump_acl_list().
113 for (const auto &item: ToTree(v->aclList).treeDump("", &Acl::AllowOrDeny)) {
114 if (item.isEmpty()) // treeDump("") adds this prefix
115 continue;
116 if (item.cmp("\n") == 0) // treeDump() adds this suffix
117 continue;
118 os << ' ' << item; // ACL name
119 }
120 }
121 os << '\n';
122 }
123}
124
125void
127{
128 auto separator = "";
129 for (const auto &v: values) {
130 os << separator;
131 os << key() << (v->method() == Value::mhReplace ? "=" : "+=") << v->value();
132 separator = " ";
133 }
134}
135
136const Notes::Keys &
138{
139 // these keys are used for internal Squid-helper communication
140 static const char *names[] = {
141 "group",
142 "ha1",
143 "log",
144 "message",
145 "password",
146 "rewrite-url",
147 "status",
148 "tag",
149 "ttl",
150 "url",
151 "user"
152 };
153
154 static Keys keys(std::begin(names), std::end(names));
155 return keys;
156}
157
158Notes::Notes(const char *aDescr, const Keys *extraReservedKeys, bool allowFormatted):
159 descr(aDescr),
160 formattedValues(allowFormatted)
161{
162 if (extraReservedKeys)
163 reservedKeys = *extraReservedKeys;
164}
165
167Notes::add(const SBuf &noteKey)
168{
169 if (Note::Pointer p = find(noteKey))
170 return p;
171 notes.push_back(new Note(noteKey));
172 return notes.back();
173}
174
176Notes::find(const SBuf &noteKey)
177{
178 for (const auto &n: notes)
179 if (n->key() == noteKey)
180 return n;
181 return nullptr;
182}
183
184void
185Notes::banReservedKey(const SBuf &key, const Keys &banned) const
186{
187 if (std::find(banned.begin(), banned.end(), key) != banned.end())
188 throw TextException(ToSBuf("cannot use a reserved ", descr, " name: ", key), Here());
189}
190
191void
192Notes::validateKey(const SBuf &key) const
193{
196
197 // TODO: fix code duplication: the same set of specials is produced
198 // by isKeyNameChar().
199 static const CharacterSet allowedSpecials = CharacterSet::ALPHA +
200 CharacterSet::DIGIT + CharacterSet("specials", "-_");
201 const auto specialIndex = key.findFirstNotOf(allowedSpecials);
202 if (specialIndex != SBuf::npos) {
203 debugs(28, DBG_CRITICAL, "WARNING: used special character '" <<
204 key[specialIndex] << "' within annotation name. " <<
205 "Future Squid versions will not support this.");
206 }
207}
208
211{
212 const char *tok = ConfigParser::NextToken();
213 if (!tok)
214 fatalf("FATAL: Missing note key");
215 SBuf key(tok);
216 validateKey(key);
218 const char *val = ConfigParser::NextQuotedToken();
219 if (!val)
220 fatalf("FATAL: Missing note value");
222 Note::Pointer note = add(key);
223 Note::Value::Pointer noteValue = note->addValue(val, formattedValues && ConfigParser::LastTokenWasQuoted(), descr);
224 key.append('=');
225 key.append(val);
226 aclParseAclList(parser, &noteValue->aclList, key.c_str());
227 return note;
228}
229
230void
232 char *k, *v;
233 int parsedPairs = 0;
234 while (ConfigParser::NextKvPair(k, v)) {
235 int keyLen = strlen(k);
236 const Note::Value::Method method = (k[keyLen - 1] == '+') ? Note::Value::mhAppend : Note::Value::mhReplace;
237 if (method == Note::Value::mhAppend)
238 keyLen--;
239 else {
241 if (Note::Pointer oldNote = find(SBuf(k, keyLen)))
242 debugs(28, DBG_CRITICAL, "WARNING: annotation configuration with key " << k <<
243 " already exists and will be overwritten");
244 }
245 SBuf key(k, keyLen);
246 validateKey(key);
247 Note::Pointer note = add(key);
248 (void)note->addValue(v, formattedValues && ConfigParser::LastTokenWasQuoted(), descr, method);
249 parsedPairs++;
250 }
251 if (!parsedPairs)
252 fatalf("FATAL: Missing annotation kv pair");
253}
254
255void
257{
258 for (const auto &n: notes)
259 n->updateNotePairs(pairs, delimiters, al);
260}
261
262void
263Notes::printAsNoteDirectives(StoreEntry *entry, const char * const directiveName) const
264{
265 for (const auto &n: notes)
266 n->printAsNoteDirective(entry, directiveName);
267}
268
269void
271{
272 const char *separator = "";
273 for (const auto &note: notes) {
274 os << separator;
275 note->printAsAnnotationAclParameters(os);
276 separator = " ";
277 }
278}
279
280std::optional<SBuf>
281NotePairs::find(const char *noteKey, const char *sep) const
282{
283 SBuf resultNote;
284
285 for (const auto &e: entries) {
286 if (!e->name().cmp(noteKey)) {
287 if (!resultNote.isEmpty())
288 resultNote.append(sep);
289 resultNote.append(e->value());
290 }
291 }
292
293 if (resultNote.isEmpty())
294 return std::nullopt;
295
296 return resultNote;
297}
298
299void
300NotePairs::print(std::ostream &os, const char * const nameValueSeparator, const char * const entryTerminator) const
301{
302 for (const auto &e: entries)
303 os << e->name() << nameValueSeparator << e->value() << entryTerminator;
304}
305
306const char *
307NotePairs::findFirst(const char *noteKey) const
308{
309 for (const auto &e: entries)
310 if (!e->name().cmp(noteKey))
311 return const_cast<SBuf &>(e->value()).c_str();
312 return nullptr;
313}
314
315void
316NotePairs::add(const char *key, const char *note)
317{
318 entries.push_back(new NotePairs::Entry(key, note));
319}
320
321void
322NotePairs::add(const SBuf &key, const SBuf &note)
323{
324 entries.push_back(new NotePairs::Entry(key, note));
325}
326
327void
328NotePairs::remove(const char *key)
329{
330 Entries::iterator i = entries.begin();
331 while (i != entries.end())
332 i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
333}
334
335void
337{
338 Entries::iterator i = entries.begin();
339 while (i != entries.end())
340 i = (*i)->name() == key ? entries.erase(i) : i+1;
341}
342
343static void
344AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
345{
347 const auto tokenCharacters = delimiters.complement("non-delimiters");
348 do {
349 SBuf token;
350 (void)tok.prefix(token, tokenCharacters);
351 entries.push_back(new NotePairs::Entry(key, token)); // token may be empty
352 } while (tok.skipOne(delimiters));
353}
354
355const NotePairs::Entries &
357{
358 if (delimiters) {
359 static NotePairs::Entries expandedEntries;
360 expandedEntries.clear();
361 for (const auto &entry: entries)
362 AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
363 return expandedEntries;
364 }
365 return entries;
366}
367
368void
369NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
370{
371 AppendTokens(entries, key, values, delimiters);
372}
373
374bool
375NotePairs::hasPair(const SBuf &key, const SBuf &value) const
376{
377 for (const auto &e: entries)
378 if (e->name() == key && e->value() == value)
379 return true;
380 return false;
381}
382
383void
385{
386 for (const auto &e: src->entries)
387 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
388}
389
390void
392{
393 for (const auto &e: src->entries) {
394 if (!hasPair(e->name(), e->value()))
395 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
396 }
397}
398
399void
401{
402 for (const auto &e: src->entries) {
403 if (std::find(appendables.begin(), appendables.end(), e->name()) == appendables.end())
404 remove(e->name());
405 }
406 append(src);
407}
408
409void
411{
412 for (const auto &e: src->entries)
413 remove(e->name());
414 append(src);
415}
416
#define Here()
source code location of the caller
Definition Here.h:15
static void AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
Definition Notes.cc:344
String SBufToString(const SBuf &s)
size_t aclParseAclList(ConfigParser &, ACLList **config, const char *label)
Definition Gadgets.cc:184
#define assert(EX)
Definition assert.h:17
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
void updateAle(const AccessLogEntry::Pointer &)
void updateReply(const HttpReply::Pointer &)
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
SBufList treeDump(const char *name, ActionToStringConverter converter) const
Definition Tree.h:60
optimized set of C chars, with quick membership test and merge support
CharacterSet complement(const char *complementLabel=nullptr) const
static const CharacterSet DIGIT
static const CharacterSet ALPHA
static void DisableMacros()
Do not allow macros inside quoted strings.
static char * NextQuotedToken()
static bool NextKvPair(char *&key, char *&value)
static bool LastTokenWasQuoted()
static char * NextToken()
static void EnableMacros()
Allow macros inside quoted strings.
static const char * QuoteString(const String &var)
bool parse(const char *def)
Definition Format.cc:66
char * content()
start of the added data
Definition MemBuf.h:41
void reset()
Definition MemBuf.cc:129
Used to store a note key/value pair.
Definition Notes.h:187
void appendNewOnly(const NotePairs *src)
Definition Notes.cc:391
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition Notes.cc:384
bool empty() const
Definition Notes.h:260
std::vector< SBuf > Names
Definition Notes.h:207
std::vector< Entry::Pointer > Entries
The key/value pair entries.
Definition Notes.h:206
std::optional< SBuf > find(const char *noteKey, const char *sep=",") const
Definition Notes.cc:281
void add(const SBuf &key, const SBuf &value)
Definition Notes.cc:322
void print(std::ostream &os, const char *nameValueSeparator, const char *entryTerminator) const
Definition Notes.cc:300
void remove(const char *key)
Definition Notes.cc:328
void replaceOrAdd(const NotePairs *src)
Definition Notes.cc:410
bool hasPair(const SBuf &key, const SBuf &value) const
Definition Notes.cc:375
const Entries & expandListEntries(const CharacterSet *delimiters) const
Definition Notes.cc:356
const char * findFirst(const char *noteKey) const
Definition Notes.cc:307
void addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
Definition Notes.cc:369
Entries entries
The key/value pair entries.
Definition Notes.h:269
void replaceOrAddOrAppend(const NotePairs *src, const Names &appendables)
Definition Notes.cc:400
Stores a value for the note.
Definition Notes.h:45
@ mhReplace
Definition Notes.h:50
@ mhAppend
Definition Notes.h:50
~Value() override
Definition Notes.cc:31
SBuf theValue
Definition Notes.h:68
Format::Format * valueFormat
Compiled annotation value format.
Definition Notes.h:67
ACLList * aclList
The access list used to determine if this value is valid for a request.
Definition Notes.h:60
Value(const char *aVal, const bool quoted, const char *descr, const Method method=mhReplace)
Definition Notes.cc:37
const SBuf & format(const AccessLogEntryPointer &al)
Definition Notes.cc:50
Definition Notes.h:39
Value::Pointer addValue(const char *value, const bool quoted, const char *descr, const Value::Method m=Value::mhAppend)
Definition Notes.cc:63
bool match(HttpRequest *request, HttpReply *reply, const AccessLogEntryPointer &al, SBuf &matched)
Definition Notes.cc:70
Values values
The possible values list for the note.
Definition Notes.h:105
void printAsNoteDirective(StoreEntry *, const char *directiveName) const
Prints key and value(s) using a "note" directive format (including directive name).
Definition Notes.cc:106
SBuf theKey
The note key.
Definition Notes.h:104
void printAsAnnotationAclParameters(std::ostream &) const
Definition Notes.cc:126
const SBuf & key() const
Definition Notes.h:93
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
Definition Notes.cc:92
Note::Pointer find(const SBuf &noteKey)
Definition Notes.cc:176
Note::Pointer parse(ConfigParser &parser)
Parses a notes line and returns a pointer to the parsed Note object.
Definition Notes.cc:210
Notes()=default
std::vector< SBuf > Keys
unordered annotation names
Definition Notes.h:117
Note::Pointer add(const SBuf &noteKey)
Definition Notes.cc:167
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
Definition Notes.cc:256
const char * descr
identifies note source in error messages
Definition Notes.h:169
void printAsNoteDirectives(StoreEntry *, const char *directiveName) const
Prints notes using "note" squid.conf directive format, one directive per stored note.
Definition Notes.cc:263
void banReservedKey(const SBuf &key, const Keys &banned) const
Makes sure the given key is not on the given list of banned names.
Definition Notes.cc:185
NotesList notes
The Note::Pointer objects array list.
Definition Notes.h:168
bool formattedValues
whether to expand quoted logformat codes
Definition Notes.h:172
static const Notes::Keys & ReservedKeys()
always prohibited key names
Definition Notes.cc:137
void printAsAnnotationAclParameters(std::ostream &) const
Definition Notes.cc:270
void validateKey(const SBuf &key) const
Definition Notes.cc:192
void parseKvPair()
Parses an annotate line with "key=value" or "key+=value" formats.
Definition Notes.cc:231
Keys reservedKeys
a list of additional prohibited key names
Definition Notes.h:171
Definition SBuf.h:94
void push_back(char)
Append a single character. The character may be NUL (\0).
Definition SBuf.cc:208
static const size_type npos
Definition SBuf.h:100
const char * c_str()
Definition SBuf.cc:516
size_type findFirstNotOf(const CharacterSet &set, size_type startPos=0) const
Definition SBuf.cc:746
bool isEmpty() const
Definition SBuf.h:435
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
void clear()
Definition SBuf.cc:175
an std::runtime_error with thrower location info
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
void fatalf(const char *fmt,...)
Definition fatal.cc:68
void aclDestroyAclList(ACLList **list)
Definition Gadgets.cc:214
const char * AllowOrDeny(const Answer &action)
Definition Tree.h:53
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
Definition parse.c:160
static char * keys[]