]> git.ipfire.org Git - thirdparty/squid.git/blob - src/Notes.cc
author: Eduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
[thirdparty/squid.git] / src / Notes.cc
1 /*
2 * Copyright (C) 1996-2017 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 "client_side.h"
14 #include "ConfigParser.h"
15 #include "globals.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"
22 #include "Store.h"
23 #include "StrList.h"
24
25 #include <algorithm>
26 #include <string>
27
28 Note::Value::~Value()
29 {
30 aclDestroyAclList(&aclList);
31 }
32
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)
35 {
36 if (quoted) {
37 valueFormat = new Format::Format(descr ? descr : "Notes");
38 valueFormat->parse(theValue.c_str());
39 }
40 }
41
42 const SBuf &
43 Note::Value::format(const AccessLogEntryPointer &al)
44 {
45 if (al && valueFormat) {
46 static MemBuf mb;
47 mb.reset();
48 valueFormat->assemble(mb, al, 0);
49 theFormattedValue.assign(mb.content());
50 return theFormattedValue;
51 }
52 return theValue;
53 }
54
55 Note::Value::Pointer
56 Note::addValue(const char *value, const bool quoted, const char *descr, const Value::Method m)
57 {
58 values.push_back(new Value(value, quoted, descr, m));
59 return values.back();
60 }
61
62 bool
63 Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al, SBuf &matched)
64 {
65 ACLFilledChecklist ch(nullptr, request, nullptr);
66 ch.reply = reply;
67 if (reply)
68 HTTPMSGLOCK(ch.reply);
69
70 for (auto v: values) {
71 assert(v->aclList);
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);
77 return true;
78 }
79 }
80 matched.clear();
81 return false;
82 }
83
84 void
85 Note::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
86 {
87 for (auto v: values) {
88 const SBuf &formatted = v->format(al);
89 if (!pairs->empty() && v->method() == Value::mhReplace)
90 pairs->remove(theKey);
91 if (delimiters)
92 pairs->addStrList(key(), formatted, *delimiters);
93 else
94 pairs->add(key(), formatted);
95 }
96 }
97
98 void
99 Note::dump(StoreEntry *entry, const char *k)
100 {
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");
106 }
107 }
108
109 SBuf
110 Note::toString(const char *sep) const
111 {
112 SBuf result;
113 for (auto val: values)
114 result.appendf("%.*s: %.*s%s", key().length(), key().rawContent(),
115 val->value().length(), val->value().rawContent(), sep);
116 return result;
117 }
118
119 Note::Pointer
120 Notes::add(const SBuf &noteKey)
121 {
122 if (Note::Pointer p = find(noteKey))
123 return p;
124 notes.push_back(new Note(noteKey));
125 return notes.back();
126 }
127
128 Note::Pointer
129 Notes::find(const SBuf &noteKey)
130 {
131 for (auto n: notes)
132 if (n->key() == noteKey)
133 return n;
134 return nullptr;
135 }
136
137 void
138 Notes::validateKey(const SBuf &key) const
139 {
140 if (blacklisted) {
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(),
145 descr ? descr : "");
146 }
147 }
148 }
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.");
158 }
159 }
160
161 Note::Pointer
162 Notes::parse(ConfigParser &parser)
163 {
164 const char *tok = ConfigParser::NextToken();
165 if (!tok)
166 fatalf("FATAL: Missing note key");
167 SBuf key(tok);
168 validateKey(key);
169 ConfigParser::EnableMacros();
170 const char *val = ConfigParser::NextQuotedToken();
171 if (!val)
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);
176 key.append('=');
177 key.append(val);
178 aclParseAclList(parser, &noteValue->aclList, key.c_str());
179 return note;
180 }
181
182 void
183 Notes::parseKvPair() {
184 char *k, *v;
185 int parsedPairs = 0;
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)
190 keyLen--;
191 else {
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");
196 }
197 SBuf key(k, keyLen);
198 validateKey(key);
199 Note::Pointer note = add(key);
200 (void)note->addValue(v, formattedValues && ConfigParser::LastTokenWasQuoted(), descr, method);
201 parsedPairs++;
202 }
203 if (!parsedPairs)
204 fatalf("FATAL: Missing annotation kv pair");
205 }
206
207 void
208 Notes::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntry::Pointer &al)
209 {
210 for (auto n: notes)
211 n->updateNotePairs(pairs, delimiters, al);
212 }
213
214 void
215 Notes::dump(StoreEntry *entry, const char *key)
216 {
217 for (auto n: notes)
218 n->dump(entry, key);
219 }
220
221 const char *
222 Notes::toString(const char *sep) const
223 {
224 static SBuf result;
225 result.clear();
226 for (auto note: notes)
227 result.append(note->toString(sep));
228 return result.isEmpty() ? nullptr : result.c_str();
229 }
230
231 bool
232 NotePairs::find(SBuf &resultNote, const char *noteKey, const char *sep) const
233 {
234 resultNote.clear();
235 for (auto e: entries) {
236 if (!e->name().cmp(noteKey)) {
237 if (!resultNote.isEmpty())
238 resultNote.append(sep);
239 resultNote.append(e->value());
240 }
241 }
242 return resultNote.length();
243 }
244
245 const char *
246 NotePairs::toString(const char *sep) const
247 {
248 static SBuf result;
249 result.clear();
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();
254 }
255
256 const char *
257 NotePairs::findFirst(const char *noteKey) const
258 {
259 for (auto e: entries)
260 if (!e->name().cmp(noteKey))
261 return const_cast<SBuf &>(e->value()).c_str();
262 return nullptr;
263 }
264
265 void
266 NotePairs::add(const char *key, const char *note)
267 {
268 entries.push_back(new NotePairs::Entry(key, note));
269 }
270
271 void
272 NotePairs::add(const SBuf &key, const SBuf &note)
273 {
274 entries.push_back(new NotePairs::Entry(key, note));
275 }
276
277 void
278 NotePairs::remove(const char *key)
279 {
280 Entries::iterator i = entries.begin();
281 while (i != entries.end())
282 i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
283 }
284
285 void
286 NotePairs::remove(const SBuf &key)
287 {
288 Entries::iterator i = entries.begin();
289 while (i != entries.end())
290 i = (*i)->name() == key ? entries.erase(i) : i+1;
291 }
292
293 static void
294 AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
295 {
296 Parser::Tokenizer tok(val);
297 SBuf v;
298 while (tok.token(v, delimiters))
299 entries.push_back(new NotePairs::Entry(key, v));
300 v = tok.remaining();
301 if (!v.isEmpty())
302 entries.push_back(new NotePairs::Entry(key, v));
303 }
304
305 const NotePairs::Entries &
306 NotePairs::expandListEntries(const CharacterSet *delimiters) const
307 {
308 if (delimiters) {
309 static NotePairs::Entries expandedEntries;
310 expandedEntries.clear();
311 for(auto entry: entries)
312 AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
313 return expandedEntries;
314 }
315 return entries;
316 }
317
318 void
319 NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
320 {
321 AppendTokens(entries, key, values, delimiters);
322 }
323
324 bool
325 NotePairs::hasPair(const SBuf &key, const SBuf &value) const
326 {
327 for (auto e: entries)
328 if (e->name() == key && e->value() == value)
329 return true;
330 return false;
331 }
332
333 void
334 NotePairs::append(const NotePairs *src)
335 {
336 for (auto e: src->entries)
337 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
338 }
339
340 void
341 NotePairs::appendNewOnly(const NotePairs *src)
342 {
343 for (auto e: src->entries) {
344 if (!hasPair(e->name(), e->value()))
345 entries.push_back(new NotePairs::Entry(e->name(), e->value()));
346 }
347 }
348
349 void
350 NotePairs::replaceOrAdd(const NotePairs *src)
351 {
352 for (auto e: src->entries)
353 remove(e->name());
354 append(src);
355 }
356