]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Notes.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / Notes.cc
CommitLineData
b3404bc5 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
b3404bc5 3 *
bbc27441
AJ
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.
b3404bc5
CT
7 */
8
9#include "squid.h"
f4f55a21 10#include "AccessLogEntry.h"
b3404bc5
CT
11#include "acl/FilledChecklist.h"
12#include "acl/Gadgets.h"
457857fe 13#include "client_side.h"
b3404bc5 14#include "ConfigParser.h"
602d9612 15#include "globals.h"
d3dddfb5 16#include "http/Stream.h"
b3404bc5 17#include "HttpReply.h"
602d9612 18#include "HttpRequest.h"
75d47340
CT
19#include "parser/Tokenizer.h"
20#include "sbuf/StringConvert.h"
b3404bc5
CT
21#include "SquidConfig.h"
22#include "Store.h"
cf9f0261 23#include "StrList.h"
b3404bc5
CT
24
25#include <algorithm>
26#include <string>
27
28Note::Value::~Value()
29{
30 aclDestroyAclList(&aclList);
b5db97e7 31 delete valueFormat;
b3404bc5
CT
32}
33
75d47340
CT
34Note::Value::Value(const char *aVal, const bool quoted, const char *descr, const Method m)
35 : aclList(nullptr), valueFormat(nullptr), theValue(aVal), theMethod(m)
b3404bc5 36{
75d47340
CT
37 if (quoted) {
38 valueFormat = new Format::Format(descr ? descr : "Notes");
b5db97e7
EB
39 if (!valueFormat->parse(theValue.c_str())) {
40 delete valueFormat;
41 SBuf exceptionMsg;
42 exceptionMsg.Printf("failed to parse annotation value %s", theValue.c_str());
43 throw TexcHere(exceptionMsg.c_str());
44 }
75d47340 45 }
b3404bc5
CT
46}
47
75d47340
CT
48const SBuf &
49Note::Value::format(const AccessLogEntryPointer &al)
b3404bc5 50{
75d47340
CT
51 if (al && valueFormat) {
52 static MemBuf mb;
53 mb.reset();
54 valueFormat->assemble(mb, al, 0);
55 theFormattedValue.assign(mb.content());
56 return theFormattedValue;
57 }
58 return theValue;
59}
b3404bc5 60
75d47340
CT
61Note::Value::Pointer
62Note::addValue(const char *value, const bool quoted, const char *descr, const Value::Method m)
63{
64 values.push_back(new Value(value, quoted, descr, m));
65 return values.back();
66}
67
68bool
69Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al, SBuf &matched)
70{
71 ACLFilledChecklist ch(nullptr, request, nullptr);
cb365059 72 ch.al = al;
b248c2a3 73 ch.reply = reply;
cb365059 74 ch.syncAle(request, nullptr);
b3404bc5 75 if (reply)
b248c2a3 76 HTTPMSGLOCK(ch.reply);
b3404bc5 77
75d47340
CT
78 for (auto v: values) {
79 assert(v->aclList);
06bf5384 80 const auto ret = ch.fastCheck(v->aclList);
75d47340
CT
81 debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
82 ", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
06bf5384 83 if (ret.allowed()) {
75d47340
CT
84 matched = v->format(al);
85 return true;
af0ded40 86 }
b3404bc5 87 }
75d47340
CT
88 matched.clear();
89 return false;
b3404bc5
CT
90}
91
75d47340
CT
92void
93Note::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
b3404bc5 94{
75d47340
CT
95 for (auto v: values) {
96 const SBuf &formatted = v->format(al);
97 if (!pairs->empty() && v->method() == Value::mhReplace)
98 pairs->remove(theKey);
99 if (delimiters)
100 pairs->addStrList(key(), formatted, *delimiters);
101 else
102 pairs->add(key(), formatted);
b3404bc5 103 }
75d47340 104}
b3404bc5 105
75d47340
CT
106void
107Note::dump(StoreEntry *entry, const char *k)
108{
109 for (auto v: values) {
110 storeAppendPrintf(entry, "%s %.*s %s",
111 k, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v->value())));
112 dump_acl_list(entry, v->aclList);
113 storeAppendPrintf(entry, "\n");
114 }
115}
116
117SBuf
118Note::toString(const char *sep) const
119{
120 SBuf result;
121 for (auto val: values)
122 result.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
123 val->value().length(), val->value().rawContent(), sep);
124 return result;
b3404bc5
CT
125}
126
127Note::Pointer
75d47340
CT
128Notes::add(const SBuf &noteKey)
129{
130 if (Note::Pointer p = find(noteKey))
131 return p;
132 notes.push_back(new Note(noteKey));
133 return notes.back();
134}
135
136Note::Pointer
137Notes::find(const SBuf &noteKey)
138{
139 for (auto n: notes)
140 if (n->key() == noteKey)
141 return n;
142 return nullptr;
143}
144
145void
146Notes::validateKey(const SBuf &key) const
b3404bc5 147{
b3404bc5 148 if (blacklisted) {
75d47340
CT
149 for (int i = 0; blacklisted[i] != nullptr; ++i) {
150 if (!key.cmp(blacklisted[i])) {
151 fatalf("%s:%d: meta key \"%.*s\" is a reserved %s name",
152 cfg_filename, config_lineno, key.length(), key.rawContent(),
b3404bc5
CT
153 descr ? descr : "");
154 }
155 }
156 }
75d47340
CT
157 // TODO: fix code duplication: the same set of specials is produced
158 // by isKeyNameChar().
159 static const CharacterSet allowedSpecials = CharacterSet::ALPHA +
160 CharacterSet::DIGIT + CharacterSet("specials", "-_");
161 const auto specialIndex = key.findFirstNotOf(allowedSpecials);
162 if (specialIndex != SBuf::npos) {
163 debugs(28, DBG_CRITICAL, "Warning: used special character '" <<
164 key[specialIndex] << "' within annotation name. " <<
165 "Future Squid versions will not support this.");
166 }
167}
b3404bc5 168
75d47340
CT
169Note::Pointer
170Notes::parse(ConfigParser &parser)
171{
172 const char *tok = ConfigParser::NextToken();
173 if (!tok)
174 fatalf("FATAL: Missing note key");
175 SBuf key(tok);
176 validateKey(key);
177 ConfigParser::EnableMacros();
178 const char *val = ConfigParser::NextQuotedToken();
179 if (!val)
180 fatalf("FATAL: Missing note value");
181 ConfigParser::DisableMacros();
182 Note::Pointer note = add(key);
183 Note::Value::Pointer noteValue = note->addValue(val, formattedValues && ConfigParser::LastTokenWasQuoted(), descr);
184 key.append('=');
185 key.append(val);
186 aclParseAclList(parser, &noteValue->aclList, key.c_str());
b3404bc5
CT
187 return note;
188}
189
190void
75d47340
CT
191Notes::parseKvPair() {
192 char *k, *v;
193 int parsedPairs = 0;
194 while (ConfigParser::NextKvPair(k, v)) {
195 int keyLen = strlen(k);
196 const Note::Value::Method method = (k[keyLen - 1] == '+') ? Note::Value::mhAppend : Note::Value::mhReplace;
197 if (method == Note::Value::mhAppend)
198 keyLen--;
199 else {
200 assert(method == Note::Value::mhReplace);
201 if (Note::Pointer oldNote = find(SBuf(k, keyLen)))
202 debugs(28, DBG_CRITICAL, "Warning: annotation configuration with key " << k <<
b8f75894 203 " already exists and will be overwritten");
b3404bc5 204 }
75d47340
CT
205 SBuf key(k, keyLen);
206 validateKey(key);
207 Note::Pointer note = add(key);
208 (void)note->addValue(v, formattedValues && ConfigParser::LastTokenWasQuoted(), descr, method);
209 parsedPairs++;
b3404bc5 210 }
75d47340
CT
211 if (!parsedPairs)
212 fatalf("FATAL: Missing annotation kv pair");
b3404bc5
CT
213}
214
215void
75d47340 216Notes::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntry::Pointer &al)
b3404bc5 217{
75d47340
CT
218 for (auto n: notes)
219 n->updateNotePairs(pairs, delimiters, al);
b3404bc5 220}
cf9f0261 221
75d47340
CT
222void
223Notes::dump(StoreEntry *entry, const char *key)
b670cb6a 224{
75d47340
CT
225 for (auto n: notes)
226 n->dump(entry, key);
b670cb6a
AR
227}
228
cf9f0261 229const char *
75d47340
CT
230Notes::toString(const char *sep) const
231{
232 static SBuf result;
233 result.clear();
234 for (auto note: notes)
235 result.append(note->toString(sep));
236 return result.isEmpty() ? nullptr : result.c_str();
237}
238
239bool
240NotePairs::find(SBuf &resultNote, const char *noteKey, const char *sep) const
cf9f0261 241{
75d47340
CT
242 resultNote.clear();
243 for (auto e: entries) {
244 if (!e->name().cmp(noteKey)) {
245 if (!resultNote.isEmpty())
246 resultNote.append(sep);
247 resultNote.append(e->value());
cf9f0261
CT
248 }
249 }
75d47340 250 return resultNote.length();
cf9f0261
CT
251}
252
253const char *
254NotePairs::toString(const char *sep) const
255{
75d47340
CT
256 static SBuf result;
257 result.clear();
258 for (auto e: entries)
259 result.appendf("%.*s: %.*s%s", e->name().length(), e->name().rawContent(),
260 e->value().length(), e->value().rawContent(), sep);
261 return result.isEmpty() ? nullptr : result.c_str();
cf9f0261
CT
262}
263
264const char *
265NotePairs::findFirst(const char *noteKey) const
266{
75d47340
CT
267 for (auto e: entries)
268 if (!e->name().cmp(noteKey))
269 return const_cast<SBuf &>(e->value()).c_str();
270 return nullptr;
cf9f0261
CT
271}
272
273void
274NotePairs::add(const char *key, const char *note)
275{
276 entries.push_back(new NotePairs::Entry(key, note));
277}
278
75d47340
CT
279void
280NotePairs::add(const SBuf &key, const SBuf &note)
281{
282 entries.push_back(new NotePairs::Entry(key, note));
283}
284
457857fe
CT
285void
286NotePairs::remove(const char *key)
287{
75d47340
CT
288 Entries::iterator i = entries.begin();
289 while (i != entries.end())
290 i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
457857fe
CT
291}
292
cf9f0261 293void
75d47340
CT
294NotePairs::remove(const SBuf &key)
295{
296 Entries::iterator i = entries.begin();
297 while (i != entries.end())
298 i = (*i)->name() == key ? entries.erase(i) : i+1;
299}
300
301static void
302AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
303{
304 Parser::Tokenizer tok(val);
305 SBuf v;
306 while (tok.token(v, delimiters))
307 entries.push_back(new NotePairs::Entry(key, v));
308 v = tok.remaining();
309 if (!v.isEmpty())
310 entries.push_back(new NotePairs::Entry(key, v));
311}
312
313const NotePairs::Entries &
314NotePairs::expandListEntries(const CharacterSet *delimiters) const
cf9f0261 315{
75d47340
CT
316 if (delimiters) {
317 static NotePairs::Entries expandedEntries;
318 expandedEntries.clear();
319 for(auto entry: entries)
320 AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
321 return expandedEntries;
cf9f0261 322 }
75d47340
CT
323 return entries;
324}
325
326void
327NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
328{
329 AppendTokens(entries, key, values, delimiters);
cf9f0261
CT
330}
331
332bool
75d47340 333NotePairs::hasPair(const SBuf &key, const SBuf &value) const
cf9f0261 334{
75d47340
CT
335 for (auto e: entries)
336 if (e->name() == key && e->value() == value)
cf9f0261 337 return true;
cf9f0261
CT
338 return false;
339}
340
341void
342NotePairs::append(const NotePairs *src)
343{
75d47340
CT
344 for (auto e: src->entries)
345 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
cf9f0261 346}
f4f55a21 347
71e7400c
AJ
348void
349NotePairs::appendNewOnly(const NotePairs *src)
350{
75d47340
CT
351 for (auto e: src->entries) {
352 if (!hasPair(e->name(), e->value()))
353 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
71e7400c
AJ
354 }
355}
356
d665de37
A
357void
358NotePairs::replaceOrAddOrAppend(const NotePairs *src, const NotePairs::Names &appendables)
359{
360 for (const auto e: src->entries) {
361 if (std::find(appendables.begin(), appendables.end(), e->name()) == appendables.end())
362 remove(e->name());
363 }
364 append(src);
365}
366
457857fe
CT
367void
368NotePairs::replaceOrAdd(const NotePairs *src)
369{
75d47340
CT
370 for (auto e: src->entries)
371 remove(e->name());
457857fe
CT
372 append(src);
373}
374