]> granicus.if.org Git - icinga2/blob - lib/icinga/macroprocessor.cpp
Implement typeof() function
[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.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                                         Value value = vars->Get(macro);
93
94                                         if (value.IsObjectType<Array>())
95                                                 value = Utility::Join(value, ';');
96
97                                         *result = value;
98                                         *recursive_macro = true;
99                                         return true;
100                                 }
101                         }
102                 }
103
104                 MacroResolver::Ptr mresolver = dynamic_pointer_cast<MacroResolver>(resolver.second);
105
106                 if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
107                         return true;
108
109                 Value ref = resolver.second;
110                 bool valid = true;
111
112                 BOOST_FOREACH(const String& token, tokens) {
113                         if (ref.IsObjectType<Dictionary>()) {
114                                 Dictionary::Ptr dict = ref;
115                                 if (dict->Contains(token)) {
116                                         ref = dict->Get(token);
117                                         continue;
118                                 } else {
119                                         valid = false;
120                                         break;
121                                 }
122                         } else if (ref.IsObject()) {
123                                 Object::Ptr object = ref;
124
125                                 Type::Ptr type = object->GetReflectionType();
126
127                                 if (!type) {
128                                         valid = false;
129                                         break;
130                                 }
131
132                                 int field = type->GetFieldId(token);
133
134                                 if (field == -1) {
135                                         valid = false;
136                                         break;
137                                 }
138
139                                 ref = object->GetField(field);
140                         }
141                 }
142
143                 if (valid) {
144                         if (tokens[0] == "vars" ||
145                             tokens[0] == "action_url" ||
146                             tokens[0] == "notes_url" ||
147                             tokens[0] == "notes")
148                                 *recursive_macro = true;
149
150                         if (ref.IsObjectType<Array>())
151                                 ref = Utility::Join(ref, ';');
152
153                         *result = ref;
154                         return true;
155                 }
156         }
157
158         return false;
159 }
160
161 String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
162     const CheckResult::Ptr& cr, String *missingMacro,
163     const MacroProcessor::EscapeCallback& escapeFn, int recursionLevel)
164 {
165         CONTEXT("Resolving macros for string '" + str + "'");
166
167         if (recursionLevel > 15)
168                 BOOST_THROW_EXCEPTION(std::runtime_error("Infinite recursion detected while resolving macros"));
169
170         size_t offset, pos_first, pos_second;
171         offset = 0;
172
173         String result = str;
174         while ((pos_first = result.FindFirstOf("$", offset)) != String::NPos) {
175                 pos_second = result.FindFirstOf("$", pos_first + 1);
176
177                 if (pos_second == String::NPos)
178                         BOOST_THROW_EXCEPTION(std::runtime_error("Closing $ not found in macro format string."));
179
180                 String name = result.SubStr(pos_first + 1, pos_second - pos_first - 1);
181
182                 String resolved_macro;
183                 bool recursive_macro;
184                 bool found = ResolveMacro(name, resolvers, cr, &resolved_macro, &recursive_macro);
185
186                 /* $$ is an escape sequence for $. */
187                 if (name.IsEmpty()) {
188                         resolved_macro = "$";
189                         found = true;
190                 }
191
192                 if (!found) {
193                         if (!missingMacro)
194                                 Log(LogWarning, "MacroProcessor")
195                                     << "Macro '" << name << "' is not defined.";
196                         else
197                                 *missingMacro = name;
198                 }
199
200                 /* recursively resolve macros in the macro if it was a user macro */
201                 if (recursive_macro)
202                         resolved_macro = InternalResolveMacros(resolved_macro,
203                             resolvers, cr, missingMacro, EscapeCallback(), recursionLevel + 1);
204
205                 if (escapeFn)
206                         resolved_macro = escapeFn(resolved_macro);
207
208                 result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro);
209                 offset = pos_first + resolved_macro.GetLength() + 1;
210         }
211
212         return result;
213 }