]> granicus.if.org Git - icinga2/blob - lib/icinga/macroprocessor.cpp
Change log message identifier for libicinga.
[icinga2] / lib / icinga / macroprocessor.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/macroprocessor.hpp"
21 #include "icinga/macroresolver.hpp"
22 #include "icinga/customvarobject.hpp"
23 #include "base/array.hpp"
24 #include "base/objectlock.hpp"
25 #include "base/logger_fwd.hpp"
26 #include "base/context.hpp"
27 #include "base/dynamicobject.hpp"
28 #include <boost/foreach.hpp>
29 #include <boost/algorithm/string/split.hpp>
30 #include <boost/algorithm/string/join.hpp>
31 #include <boost/algorithm/string/classification.hpp>
32
33 using namespace icinga;
34
35 Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers,
36     const CheckResult::Ptr& cr, String *missingMacro,
37     const MacroProcessor::EscapeCallback& escapeFn)
38 {
39         Value result;
40
41         if (str.IsEmpty())
42                 return Empty;
43
44         if (str.IsScalar()) {
45                 result = InternalResolveMacros(str, resolvers, cr, missingMacro, escapeFn);
46         } else if (str.IsObjectType<Array>()) {
47                 Array::Ptr resultArr = make_shared<Array>();
48                 Array::Ptr arr = str;
49
50                 ObjectLock olock(arr);
51
52                 BOOST_FOREACH(const Value& arg, arr) {
53                         /* Note: don't escape macros here. */
54                         resultArr->Add(InternalResolveMacros(arg, resolvers, cr, missingMacro, EscapeCallback()));
55                 }
56
57                 result = resultArr;
58         } else {
59                 BOOST_THROW_EXCEPTION(std::invalid_argument("Command is not a string or array."));
60         }
61
62         return result;
63 }
64
65 bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers,
66     const CheckResult::Ptr& cr, String *result, bool *recursive_macro)
67 {
68         CONTEXT("Resolving macro '" + macro + "'");
69
70         *recursive_macro = false;
71
72         std::vector<String> tokens;
73         boost::algorithm::split(tokens, macro, boost::is_any_of("."));
74
75         String objName;
76         if (tokens.size() > 1) {
77                 objName = tokens[0];
78                 tokens.erase(tokens.begin());
79         }
80
81         BOOST_FOREACH(const ResolverSpec& resolver, resolvers) {
82                 if (!objName.IsEmpty() && objName != resolver.first)
83                         continue;
84
85                 if (objName.IsEmpty()) {
86                         CustomVarObject::Ptr dobj = dynamic_pointer_cast<CustomVarObject>(resolver.second);
87
88                         if (dobj) {
89                                 Dictionary::Ptr vars = dobj->GetVars();
90
91                                 if (vars && vars->Contains(macro)) {
92                                         *result = vars->Get(macro);
93                                         *recursive_macro = true;
94                                         return true;
95                                 }
96                         }
97                 }
98
99                 MacroResolver::Ptr mresolver = dynamic_pointer_cast<MacroResolver>(resolver.second);
100
101                 if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
102                         return true;
103
104                 Value ref = resolver.second;
105                 bool valid = true;
106
107                 BOOST_FOREACH(const String& token, tokens) {
108                         if (ref.IsObjectType<Dictionary>()) {
109                                 Dictionary::Ptr dict = ref;
110                                 if (dict->Contains(token)) {
111                                         ref = dict->Get(token);
112                                         continue;
113                                 } else {
114                                         valid = false;
115                                         break;
116                                 }
117                         } else if (ref.IsObject()) {
118                                 Object::Ptr object = ref;
119
120                                 const Type *type = object->GetReflectionType();
121
122                                 if (!type) {
123                                         valid = false;
124                                         break;
125                                 }
126
127                                 int field = type->GetFieldId(token);
128
129                                 if (field == -1) {
130                                         valid = false;
131                                         break;
132                                 }
133
134                                 ref = object->GetField(field);
135                         }
136                 }
137
138                 if (valid) {
139                         if (tokens[0] == "vars" ||
140                             tokens[0] == "action_url" ||
141                             tokens[0] == "notes_url" ||
142                             tokens[0] == "notes")
143                                 *recursive_macro = true;
144
145                         *result = ref;
146                         return true;
147                 }
148         }
149
150         return false;
151 }
152
153 String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
154     const CheckResult::Ptr& cr, String *missingMacro,
155     const MacroProcessor::EscapeCallback& escapeFn, int recursionLevel)
156 {
157         CONTEXT("Resolving macros for string '" + str + "'");
158
159         if (recursionLevel > 15)
160                 BOOST_THROW_EXCEPTION(std::runtime_error("Infinite recursion detected while resolving macros"));
161
162         size_t offset, pos_first, pos_second;
163         offset = 0;
164
165         String result = str;
166         while ((pos_first = result.FindFirstOf("$", offset)) != String::NPos) {
167                 pos_second = result.FindFirstOf("$", pos_first + 1);
168
169                 if (pos_second == String::NPos)
170                         BOOST_THROW_EXCEPTION(std::runtime_error("Closing $ not found in macro format string."));
171
172                 String name = result.SubStr(pos_first + 1, pos_second - pos_first - 1);
173
174                 String resolved_macro;
175                 bool recursive_macro;
176                 bool found = ResolveMacro(name, resolvers, cr, &resolved_macro, &recursive_macro);
177
178                 /* $$ is an escape sequence for $. */
179                 if (name.IsEmpty()) {
180                         resolved_macro = "$";
181                         found = true;
182                 }
183
184                 if (!found) {
185                         if (!missingMacro)
186                                 Log(LogWarning, "MacroProcessor", "Macro '" + name + "' is not defined.");
187                         else
188                                 *missingMacro = name;
189                 }
190
191                 /* recursively resolve macros in the macro if it was a user macro */
192                 if (recursive_macro)
193                         resolved_macro = InternalResolveMacros(resolved_macro,
194                             resolvers, cr, missingMacro, EscapeCallback(), recursionLevel + 1);
195
196                 if (escapeFn)
197                         resolved_macro = escapeFn(resolved_macro);
198
199                 result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro);
200                 offset = pos_first + resolved_macro.GetLength() + 1;
201         }
202
203         return result;
204 }