2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
10 #include "AccessLogEntry.h"
11 #include "acl/FilledChecklist.h"
12 #include "acl/Gadgets.h"
13 #include "client_side.h"
14 #include "ConfigParser.h"
16 #include "http/Stream.h"
17 #include "HttpReply.h"
18 #include "HttpRequest.h"
19 #include "parser/Tokenizer.h"
20 #include "sbuf/Stream.h"
21 #include "sbuf/StringConvert.h"
22 #include "SquidConfig.h"
31 aclDestroyAclList(&aclList
);
35 Note::Value::Value(const char *aVal
, const bool quoted
, const char *descr
, const Method m
)
36 : aclList(nullptr), valueFormat(nullptr), theValue(aVal
), theMethod(m
)
39 valueFormat
= new Format::Format(descr
? descr
: "Notes");
40 if (!valueFormat
->parse(theValue
.c_str())) {
43 exceptionMsg
.Printf("failed to parse annotation value %s", theValue
.c_str());
44 throw TexcHere(exceptionMsg
.c_str());
50 Note::Value::format(const AccessLogEntryPointer
&al
)
52 if (al
&& valueFormat
) {
55 valueFormat
->assemble(mb
, al
, 0);
56 theFormattedValue
.assign(mb
.content());
57 return theFormattedValue
;
63 Note::addValue(const char *value
, const bool quoted
, const char *descr
, const Value::Method m
)
65 values
.push_back(new Value(value
, quoted
, descr
, m
));
70 Note::match(HttpRequest
*request
, HttpReply
*reply
, const AccessLogEntry::Pointer
&al
, SBuf
&matched
)
72 ACLFilledChecklist
ch(nullptr, request
, nullptr);
75 ch
.syncAle(request
, nullptr);
77 HTTPMSGLOCK(ch
.reply
);
79 for (const auto &v
: values
) {
81 const auto ret
= ch
.fastCheck(v
->aclList
);
82 debugs(93, 5, "Check for header name: " << theKey
<< ": " << v
->value() <<
83 ", HttpRequest: " << request
<< " HttpReply: " << reply
<< " matched: " << ret
);
85 matched
= v
->format(al
);
94 Note::updateNotePairs(NotePairs::Pointer pairs
, const CharacterSet
*delimiters
, const AccessLogEntryPointer
&al
)
96 for (const auto &v
: values
) {
97 const SBuf
&formatted
= v
->format(al
);
98 if (!pairs
->empty() && v
->method() == Value::mhReplace
)
99 pairs
->remove(theKey
);
101 pairs
->addStrList(key(), formatted
, *delimiters
);
103 pairs
->add(key(), formatted
);
108 Note::dump(StoreEntry
*entry
, const char *k
)
110 for (const auto &v
: values
) {
111 storeAppendPrintf(entry
, "%s %.*s %s",
112 k
, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v
->value())));
113 dump_acl_list(entry
, v
->aclList
);
114 storeAppendPrintf(entry
, "\n");
119 Note::toString(const char *sep
) const
122 for (const auto &val
: values
)
123 result
.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
124 val
->value().length(), val
->value().rawContent(), sep
);
129 Notes::ReservedKeys()
131 // these keys are used for internal Squid-helper communication
132 static const char *names
[] = {
146 static Keys
keys(std::begin(names
), std::end(names
));
150 Notes::Notes(const char *aDescr
, const Keys
*extraReservedKeys
, bool allowFormatted
):
152 formattedValues(allowFormatted
)
154 if (extraReservedKeys
)
155 reservedKeys
= *extraReservedKeys
;
159 Notes::add(const SBuf
¬eKey
)
161 if (Note::Pointer p
= find(noteKey
))
163 notes
.push_back(new Note(noteKey
));
168 Notes::find(const SBuf
¬eKey
)
170 for (const auto &n
: notes
)
171 if (n
->key() == noteKey
)
177 Notes::banReservedKey(const SBuf
&key
, const Keys
&banned
) const
179 if (std::find(banned
.begin(), banned
.end(), key
) != banned
.end())
180 throw TextException(ToSBuf("cannot use a reserved ", descr
, " name: ", key
), Here());
184 Notes::validateKey(const SBuf
&key
) const
186 banReservedKey(key
, ReservedKeys());
187 banReservedKey(key
, reservedKeys
);
189 // TODO: fix code duplication: the same set of specials is produced
190 // by isKeyNameChar().
191 static const CharacterSet allowedSpecials
= CharacterSet::ALPHA
+
192 CharacterSet::DIGIT
+ CharacterSet("specials", "-_");
193 const auto specialIndex
= key
.findFirstNotOf(allowedSpecials
);
194 if (specialIndex
!= SBuf::npos
) {
195 debugs(28, DBG_CRITICAL
, "Warning: used special character '" <<
196 key
[specialIndex
] << "' within annotation name. " <<
197 "Future Squid versions will not support this.");
202 Notes::parse(ConfigParser
&parser
)
204 const char *tok
= ConfigParser::NextToken();
206 fatalf("FATAL: Missing note key");
209 ConfigParser::EnableMacros();
210 const char *val
= ConfigParser::NextQuotedToken();
212 fatalf("FATAL: Missing note value");
213 ConfigParser::DisableMacros();
214 Note::Pointer note
= add(key
);
215 Note::Value::Pointer noteValue
= note
->addValue(val
, formattedValues
&& ConfigParser::LastTokenWasQuoted(), descr
);
218 aclParseAclList(parser
, ¬eValue
->aclList
, key
.c_str());
223 Notes::parseKvPair() {
226 while (ConfigParser::NextKvPair(k
, v
)) {
227 int keyLen
= strlen(k
);
228 const Note::Value::Method method
= (k
[keyLen
- 1] == '+') ? Note::Value::mhAppend
: Note::Value::mhReplace
;
229 if (method
== Note::Value::mhAppend
)
232 assert(method
== Note::Value::mhReplace
);
233 if (Note::Pointer oldNote
= find(SBuf(k
, keyLen
)))
234 debugs(28, DBG_CRITICAL
, "Warning: annotation configuration with key " << k
<<
235 " already exists and will be overwritten");
239 Note::Pointer note
= add(key
);
240 (void)note
->addValue(v
, formattedValues
&& ConfigParser::LastTokenWasQuoted(), descr
, method
);
244 fatalf("FATAL: Missing annotation kv pair");
248 Notes::updateNotePairs(NotePairs::Pointer pairs
, const CharacterSet
*delimiters
, const AccessLogEntry::Pointer
&al
)
250 for (const auto &n
: notes
)
251 n
->updateNotePairs(pairs
, delimiters
, al
);
255 Notes::dump(StoreEntry
*entry
, const char *key
)
257 for (const auto &n
: notes
)
262 Notes::toString(const char *sep
) const
266 for (const auto ¬e
: notes
)
267 result
.append(note
->toString(sep
));
268 return result
.isEmpty() ? nullptr : result
.c_str();
272 NotePairs::find(SBuf
&resultNote
, const char *noteKey
, const char *sep
) const
275 for (const auto &e
: entries
) {
276 if (!e
->name().cmp(noteKey
)) {
277 if (!resultNote
.isEmpty())
278 resultNote
.append(sep
);
279 resultNote
.append(e
->value());
282 return resultNote
.length();
286 NotePairs::toString(const char *sep
) const
290 for (const auto &e
: entries
)
291 result
.appendf("%.*s: %.*s%s", e
->name().length(), e
->name().rawContent(),
292 e
->value().length(), e
->value().rawContent(), sep
);
293 return result
.isEmpty() ? nullptr : result
.c_str();
297 NotePairs::findFirst(const char *noteKey
) const
299 for (const auto &e
: entries
)
300 if (!e
->name().cmp(noteKey
))
301 return const_cast<SBuf
&>(e
->value()).c_str();
306 NotePairs::add(const char *key
, const char *note
)
308 entries
.push_back(new NotePairs::Entry(key
, note
));
312 NotePairs::add(const SBuf
&key
, const SBuf
¬e
)
314 entries
.push_back(new NotePairs::Entry(key
, note
));
318 NotePairs::remove(const char *key
)
320 Entries::iterator i
= entries
.begin();
321 while (i
!= entries
.end())
322 i
= (*i
)->name().cmp(key
) ? i
+1 : entries
.erase(i
);
326 NotePairs::remove(const SBuf
&key
)
328 Entries::iterator i
= entries
.begin();
329 while (i
!= entries
.end())
330 i
= (*i
)->name() == key
? entries
.erase(i
) : i
+1;
334 AppendTokens(NotePairs::Entries
&entries
, const SBuf
&key
, const SBuf
&val
, const CharacterSet
&delimiters
)
336 Parser::Tokenizer
tok(val
);
338 while (tok
.token(v
, delimiters
))
339 entries
.push_back(new NotePairs::Entry(key
, v
));
342 entries
.push_back(new NotePairs::Entry(key
, v
));
345 const NotePairs::Entries
&
346 NotePairs::expandListEntries(const CharacterSet
*delimiters
) const
349 static NotePairs::Entries expandedEntries
;
350 expandedEntries
.clear();
351 for (const auto &entry
: entries
)
352 AppendTokens(expandedEntries
, entry
->name(), entry
->value(), *delimiters
);
353 return expandedEntries
;
359 NotePairs::addStrList(const SBuf
&key
, const SBuf
&values
, const CharacterSet
&delimiters
)
361 AppendTokens(entries
, key
, values
, delimiters
);
365 NotePairs::hasPair(const SBuf
&key
, const SBuf
&value
) const
367 for (const auto &e
: entries
)
368 if (e
->name() == key
&& e
->value() == value
)
374 NotePairs::append(const NotePairs
*src
)
376 for (const auto &e
: src
->entries
)
377 entries
.push_back(new NotePairs::Entry(e
->name(), e
->value()));
381 NotePairs::appendNewOnly(const NotePairs
*src
)
383 for (const auto &e
: src
->entries
) {
384 if (!hasPair(e
->name(), e
->value()))
385 entries
.push_back(new NotePairs::Entry(e
->name(), e
->value()));
390 NotePairs::replaceOrAddOrAppend(const NotePairs
*src
, const NotePairs::Names
&appendables
)
392 for (const auto &e
: src
->entries
) {
393 if (std::find(appendables
.begin(), appendables
.end(), e
->name()) == appendables
.end())
400 NotePairs::replaceOrAdd(const NotePairs
*src
)
402 for (const auto &e
: src
->entries
)