]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Notes.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / Notes.cc
CommitLineData
b3404bc5 1/*
5b74111a 2 * Copyright (C) 1996-2018 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);
b248c2a3 72 ch.reply = reply;
b3404bc5 73 if (reply)
b248c2a3 74 HTTPMSGLOCK(ch.reply);
b3404bc5 75
75d47340
CT
76 for (auto v: values) {
77 assert(v->aclList);
06bf5384 78 const auto ret = ch.fastCheck(v->aclList);
75d47340
CT
79 debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
80 ", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
06bf5384 81 if (ret.allowed()) {
75d47340
CT
82 matched = v->format(al);
83 return true;
af0ded40 84 }
b3404bc5 85 }
75d47340
CT
86 matched.clear();
87 return false;
b3404bc5
CT
88}
89
75d47340
CT
90void
91Note::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
b3404bc5 92{
75d47340
CT
93 for (auto v: values) {
94 const SBuf &formatted = v->format(al);
95 if (!pairs->empty() && v->method() == Value::mhReplace)
96 pairs->remove(theKey);
97 if (delimiters)
98 pairs->addStrList(key(), formatted, *delimiters);
99 else
100 pairs->add(key(), formatted);
b3404bc5 101 }
75d47340 102}
b3404bc5 103
75d47340
CT
104void
105Note::dump(StoreEntry *entry, const char *k)
106{
107 for (auto v: values) {
108 storeAppendPrintf(entry, "%s %.*s %s",
109 k, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v->value())));
110 dump_acl_list(entry, v->aclList);
111 storeAppendPrintf(entry, "\n");
112 }
113}
114
115SBuf
116Note::toString(const char *sep) const
117{
118 SBuf result;
119 for (auto val: values)
120 result.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
121 val->value().length(), val->value().rawContent(), sep);
122 return result;
b3404bc5
CT
123}
124
125Note::Pointer
75d47340
CT
126Notes::add(const SBuf &noteKey)
127{
128 if (Note::Pointer p = find(noteKey))
129 return p;
130 notes.push_back(new Note(noteKey));
131 return notes.back();
132}
133
134Note::Pointer
135Notes::find(const SBuf &noteKey)
136{
137 for (auto n: notes)
138 if (n->key() == noteKey)
139 return n;
140 return nullptr;
141}
142
143void
144Notes::validateKey(const SBuf &key) const
b3404bc5 145{
b3404bc5 146 if (blacklisted) {
75d47340
CT
147 for (int i = 0; blacklisted[i] != nullptr; ++i) {
148 if (!key.cmp(blacklisted[i])) {
149 fatalf("%s:%d: meta key \"%.*s\" is a reserved %s name",
150 cfg_filename, config_lineno, key.length(), key.rawContent(),
b3404bc5
CT
151 descr ? descr : "");
152 }
153 }
154 }
75d47340
CT
155 // TODO: fix code duplication: the same set of specials is produced
156 // by isKeyNameChar().
157 static const CharacterSet allowedSpecials = CharacterSet::ALPHA +
158 CharacterSet::DIGIT + CharacterSet("specials", "-_");
159 const auto specialIndex = key.findFirstNotOf(allowedSpecials);
160 if (specialIndex != SBuf::npos) {
161 debugs(28, DBG_CRITICAL, "Warning: used special character '" <<
162 key[specialIndex] << "' within annotation name. " <<
163 "Future Squid versions will not support this.");
164 }
165}
b3404bc5 166
75d47340
CT
167Note::Pointer
168Notes::parse(ConfigParser &parser)
169{
170 const char *tok = ConfigParser::NextToken();
171 if (!tok)
172 fatalf("FATAL: Missing note key");
173 SBuf key(tok);
174 validateKey(key);
175 ConfigParser::EnableMacros();
176 const char *val = ConfigParser::NextQuotedToken();
177 if (!val)
178 fatalf("FATAL: Missing note value");
179 ConfigParser::DisableMacros();
180 Note::Pointer note = add(key);
181 Note::Value::Pointer noteValue = note->addValue(val, formattedValues && ConfigParser::LastTokenWasQuoted(), descr);
182 key.append('=');
183 key.append(val);
184 aclParseAclList(parser, &noteValue->aclList, key.c_str());
b3404bc5
CT
185 return note;
186}
187
188void
75d47340
CT
189Notes::parseKvPair() {
190 char *k, *v;
191 int parsedPairs = 0;
192 while (ConfigParser::NextKvPair(k, v)) {
193 int keyLen = strlen(k);
194 const Note::Value::Method method = (k[keyLen - 1] == '+') ? Note::Value::mhAppend : Note::Value::mhReplace;
195 if (method == Note::Value::mhAppend)
196 keyLen--;
197 else {
198 assert(method == Note::Value::mhReplace);
199 if (Note::Pointer oldNote = find(SBuf(k, keyLen)))
200 debugs(28, DBG_CRITICAL, "Warning: annotation configuration with key " << k <<
b8f75894 201 " already exists and will be overwritten");
b3404bc5 202 }
75d47340
CT
203 SBuf key(k, keyLen);
204 validateKey(key);
205 Note::Pointer note = add(key);
206 (void)note->addValue(v, formattedValues && ConfigParser::LastTokenWasQuoted(), descr, method);
207 parsedPairs++;
b3404bc5 208 }
75d47340
CT
209 if (!parsedPairs)
210 fatalf("FATAL: Missing annotation kv pair");
b3404bc5
CT
211}
212
213void
75d47340 214Notes::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntry::Pointer &al)
b3404bc5 215{
75d47340
CT
216 for (auto n: notes)
217 n->updateNotePairs(pairs, delimiters, al);
b3404bc5 218}
cf9f0261 219
75d47340
CT
220void
221Notes::dump(StoreEntry *entry, const char *key)
b670cb6a 222{
75d47340
CT
223 for (auto n: notes)
224 n->dump(entry, key);
b670cb6a
AR
225}
226
cf9f0261 227const char *
75d47340
CT
228Notes::toString(const char *sep) const
229{
230 static SBuf result;
231 result.clear();
232 for (auto note: notes)
233 result.append(note->toString(sep));
234 return result.isEmpty() ? nullptr : result.c_str();
235}
236
237bool
238NotePairs::find(SBuf &resultNote, const char *noteKey, const char *sep) const
cf9f0261 239{
75d47340
CT
240 resultNote.clear();
241 for (auto e: entries) {
242 if (!e->name().cmp(noteKey)) {
243 if (!resultNote.isEmpty())
244 resultNote.append(sep);
245 resultNote.append(e->value());
cf9f0261
CT
246 }
247 }
75d47340 248 return resultNote.length();
cf9f0261
CT
249}
250
251const char *
252NotePairs::toString(const char *sep) const
253{
75d47340
CT
254 static SBuf result;
255 result.clear();
256 for (auto e: entries)
257 result.appendf("%.*s: %.*s%s", e->name().length(), e->name().rawContent(),
258 e->value().length(), e->value().rawContent(), sep);
259 return result.isEmpty() ? nullptr : result.c_str();
cf9f0261
CT
260}
261
262const char *
263NotePairs::findFirst(const char *noteKey) const
264{
75d47340
CT
265 for (auto e: entries)
266 if (!e->name().cmp(noteKey))
267 return const_cast<SBuf &>(e->value()).c_str();
268 return nullptr;
cf9f0261
CT
269}
270
271void
272NotePairs::add(const char *key, const char *note)
273{
274 entries.push_back(new NotePairs::Entry(key, note));
275}
276
75d47340
CT
277void
278NotePairs::add(const SBuf &key, const SBuf &note)
279{
280 entries.push_back(new NotePairs::Entry(key, note));
281}
282
457857fe
CT
283void
284NotePairs::remove(const char *key)
285{
75d47340
CT
286 Entries::iterator i = entries.begin();
287 while (i != entries.end())
288 i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
457857fe
CT
289}
290
cf9f0261 291void
75d47340
CT
292NotePairs::remove(const SBuf &key)
293{
294 Entries::iterator i = entries.begin();
295 while (i != entries.end())
296 i = (*i)->name() == key ? entries.erase(i) : i+1;
297}
298
299static void
300AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
301{
302 Parser::Tokenizer tok(val);
303 SBuf v;
304 while (tok.token(v, delimiters))
305 entries.push_back(new NotePairs::Entry(key, v));
306 v = tok.remaining();
307 if (!v.isEmpty())
308 entries.push_back(new NotePairs::Entry(key, v));
309}
310
311const NotePairs::Entries &
312NotePairs::expandListEntries(const CharacterSet *delimiters) const
cf9f0261 313{
75d47340
CT
314 if (delimiters) {
315 static NotePairs::Entries expandedEntries;
316 expandedEntries.clear();
317 for(auto entry: entries)
318 AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
319 return expandedEntries;
cf9f0261 320 }
75d47340
CT
321 return entries;
322}
323
324void
325NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
326{
327 AppendTokens(entries, key, values, delimiters);
cf9f0261
CT
328}
329
330bool
75d47340 331NotePairs::hasPair(const SBuf &key, const SBuf &value) const
cf9f0261 332{
75d47340
CT
333 for (auto e: entries)
334 if (e->name() == key && e->value() == value)
cf9f0261 335 return true;
cf9f0261
CT
336 return false;
337}
338
339void
340NotePairs::append(const NotePairs *src)
341{
75d47340
CT
342 for (auto e: src->entries)
343 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
cf9f0261 344}
f4f55a21 345
71e7400c
AJ
346void
347NotePairs::appendNewOnly(const NotePairs *src)
348{
75d47340
CT
349 for (auto e: src->entries) {
350 if (!hasPair(e->name(), e->value()))
351 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
71e7400c
AJ
352 }
353}
354
457857fe
CT
355void
356NotePairs::replaceOrAdd(const NotePairs *src)
357{
75d47340
CT
358 for (auto e: src->entries)
359 remove(e->name());
457857fe
CT
360 append(src);
361}
362