2 * Copyright (C) 1996-2017 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/StringConvert.h"
21 #include "SquidConfig.h"
30 aclDestroyAclList(&aclList
);
33 Note::Value::Value(const char *aVal
, const bool quoted
, const char *descr
, const Method m
)
34 : aclList(nullptr), valueFormat(nullptr), theValue(aVal
), theMethod(m
)
37 valueFormat
= new Format::Format(descr
? descr
: "Notes");
38 valueFormat
->parse(theValue
.c_str());
43 Note::Value::format(const AccessLogEntryPointer
&al
)
45 if (al
&& valueFormat
) {
48 valueFormat
->assemble(mb
, al
, 0);
49 theFormattedValue
.assign(mb
.content());
50 return theFormattedValue
;
56 Note::addValue(const char *value
, const bool quoted
, const char *descr
, const Value::Method m
)
58 values
.push_back(new Value(value
, quoted
, descr
, m
));
63 Note::match(HttpRequest
*request
, HttpReply
*reply
, const AccessLogEntry::Pointer
&al
, SBuf
&matched
)
65 ACLFilledChecklist
ch(nullptr, request
, nullptr);
68 HTTPMSGLOCK(ch
.reply
);
70 for (auto v
: values
) {
72 const int ret
= ch
.fastCheck(v
->aclList
);
73 debugs(93, 5, "Check for header name: " << theKey
<< ": " << v
->value() <<
74 ", HttpRequest: " << request
<< " HttpReply: " << reply
<< " matched: " << ret
);
75 if (ret
== ACCESS_ALLOWED
) {
76 matched
= v
->format(al
);
85 Note::updateNotePairs(NotePairs::Pointer pairs
, const CharacterSet
*delimiters
, const AccessLogEntryPointer
&al
)
87 for (auto v
: values
) {
88 const SBuf
&formatted
= v
->format(al
);
89 if (!pairs
->empty() && v
->method() == Value::mhReplace
)
90 pairs
->remove(theKey
);
92 pairs
->addStrList(key(), formatted
, *delimiters
);
94 pairs
->add(key(), formatted
);
99 Note::dump(StoreEntry
*entry
, const char *k
)
101 for (auto v
: values
) {
102 storeAppendPrintf(entry
, "%s %.*s %s",
103 k
, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v
->value())));
104 dump_acl_list(entry
, v
->aclList
);
105 storeAppendPrintf(entry
, "\n");
110 Note::toString(const char *sep
) const
113 for (auto val
: values
)
114 result
.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
115 val
->value().length(), val
->value().rawContent(), sep
);
120 Notes::add(const SBuf
¬eKey
)
122 if (Note::Pointer p
= find(noteKey
))
124 notes
.push_back(new Note(noteKey
));
129 Notes::find(const SBuf
¬eKey
)
132 if (n
->key() == noteKey
)
138 Notes::validateKey(const SBuf
&key
) const
141 for (int i
= 0; blacklisted
[i
] != nullptr; ++i
) {
142 if (!key
.cmp(blacklisted
[i
])) {
143 fatalf("%s:%d: meta key \"%.*s\" is a reserved %s name",
144 cfg_filename
, config_lineno
, key
.length(), key
.rawContent(),
149 // TODO: fix code duplication: the same set of specials is produced
150 // by isKeyNameChar().
151 static const CharacterSet allowedSpecials
= CharacterSet::ALPHA
+
152 CharacterSet::DIGIT
+ CharacterSet("specials", "-_");
153 const auto specialIndex
= key
.findFirstNotOf(allowedSpecials
);
154 if (specialIndex
!= SBuf::npos
) {
155 debugs(28, DBG_CRITICAL
, "Warning: used special character '" <<
156 key
[specialIndex
] << "' within annotation name. " <<
157 "Future Squid versions will not support this.");
162 Notes::parse(ConfigParser
&parser
)
164 const char *tok
= ConfigParser::NextToken();
166 fatalf("FATAL: Missing note key");
169 ConfigParser::EnableMacros();
170 const char *val
= ConfigParser::NextQuotedToken();
172 fatalf("FATAL: Missing note value");
173 ConfigParser::DisableMacros();
174 Note::Pointer note
= add(key
);
175 Note::Value::Pointer noteValue
= note
->addValue(val
, formattedValues
&& ConfigParser::LastTokenWasQuoted(), descr
);
178 aclParseAclList(parser
, ¬eValue
->aclList
, key
.c_str());
183 Notes::parseKvPair() {
186 while (ConfigParser::NextKvPair(k
, v
)) {
187 int keyLen
= strlen(k
);
188 const Note::Value::Method method
= (k
[keyLen
- 1] == '+') ? Note::Value::mhAppend
: Note::Value::mhReplace
;
189 if (method
== Note::Value::mhAppend
)
192 assert(method
== Note::Value::mhReplace
);
193 if (Note::Pointer oldNote
= find(SBuf(k
, keyLen
)))
194 debugs(28, DBG_CRITICAL
, "Warning: annotation configuration with key " << k
<<
195 " already exists and will be overwritten");
199 Note::Pointer note
= add(key
);
200 (void)note
->addValue(v
, formattedValues
&& ConfigParser::LastTokenWasQuoted(), descr
, method
);
204 fatalf("FATAL: Missing annotation kv pair");
208 Notes::updateNotePairs(NotePairs::Pointer pairs
, const CharacterSet
*delimiters
, const AccessLogEntry::Pointer
&al
)
211 n
->updateNotePairs(pairs
, delimiters
, al
);
215 Notes::dump(StoreEntry
*entry
, const char *key
)
222 Notes::toString(const char *sep
) const
226 for (auto note
: notes
)
227 result
.append(note
->toString(sep
));
228 return result
.isEmpty() ? nullptr : result
.c_str();
232 NotePairs::find(SBuf
&resultNote
, const char *noteKey
, const char *sep
) const
235 for (auto e
: entries
) {
236 if (!e
->name().cmp(noteKey
)) {
237 if (!resultNote
.isEmpty())
238 resultNote
.append(sep
);
239 resultNote
.append(e
->value());
242 return resultNote
.length();
246 NotePairs::toString(const char *sep
) const
250 for (auto e
: entries
)
251 result
.appendf("%.*s: %.*s%s", e
->name().length(), e
->name().rawContent(),
252 e
->value().length(), e
->value().rawContent(), sep
);
253 return result
.isEmpty() ? nullptr : result
.c_str();
257 NotePairs::findFirst(const char *noteKey
) const
259 for (auto e
: entries
)
260 if (!e
->name().cmp(noteKey
))
261 return const_cast<SBuf
&>(e
->value()).c_str();
266 NotePairs::add(const char *key
, const char *note
)
268 entries
.push_back(new NotePairs::Entry(key
, note
));
272 NotePairs::add(const SBuf
&key
, const SBuf
¬e
)
274 entries
.push_back(new NotePairs::Entry(key
, note
));
278 NotePairs::remove(const char *key
)
280 Entries::iterator i
= entries
.begin();
281 while (i
!= entries
.end())
282 i
= (*i
)->name().cmp(key
) ? i
+1 : entries
.erase(i
);
286 NotePairs::remove(const SBuf
&key
)
288 Entries::iterator i
= entries
.begin();
289 while (i
!= entries
.end())
290 i
= (*i
)->name() == key
? entries
.erase(i
) : i
+1;
294 AppendTokens(NotePairs::Entries
&entries
, const SBuf
&key
, const SBuf
&val
, const CharacterSet
&delimiters
)
296 Parser::Tokenizer
tok(val
);
298 while (tok
.token(v
, delimiters
))
299 entries
.push_back(new NotePairs::Entry(key
, v
));
302 entries
.push_back(new NotePairs::Entry(key
, v
));
305 const NotePairs::Entries
&
306 NotePairs::expandListEntries(const CharacterSet
*delimiters
) const
309 static NotePairs::Entries expandedEntries
;
310 expandedEntries
.clear();
311 for(auto entry
: entries
)
312 AppendTokens(expandedEntries
, entry
->name(), entry
->value(), *delimiters
);
313 return expandedEntries
;
319 NotePairs::addStrList(const SBuf
&key
, const SBuf
&values
, const CharacterSet
&delimiters
)
321 AppendTokens(entries
, key
, values
, delimiters
);
325 NotePairs::hasPair(const SBuf
&key
, const SBuf
&value
) const
327 for (auto e
: entries
)
328 if (e
->name() == key
&& e
->value() == value
)
334 NotePairs::append(const NotePairs
*src
)
336 for (auto e
: src
->entries
)
337 entries
.push_back(new NotePairs::Entry(e
->name(), e
->value()));
341 NotePairs::appendNewOnly(const NotePairs
*src
)
343 for (auto e
: src
->entries
) {
344 if (!hasPair(e
->name(), e
->value()))
345 entries
.push_back(new NotePairs::Entry(e
->name(), e
->value()));
350 NotePairs::replaceOrAdd(const NotePairs
*src
)
352 for (auto e
: src
->entries
)