1 /*
2  *
3  * Conky, a system monitor, based on torsmo
4  *
5  * Any original torsmo code is licensed under the BSD license
6  *
7  * All code written since the fork of torsmo is licensed under the GPL
8  *
9  * Please see COPYING for details
10  *
11  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
12  * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
13  *	(see AUTHORS)
14  * All rights reserved.
15  *
16  * This program is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 #include <cctype>
30 #include <cstdlib>
31 #include <string>
32 #include "conky.h"
33 #include "logging.h"
34 
35 namespace {
36 conky::simple_config_setting<std::string> _template[10] = {
37     {"template0", std::string(), true}, {"template1", std::string(), true},
38     {"template2", std::string(), true}, {"template3", std::string(), true},
39     {"template4", std::string(), true}, {"template5", std::string(), true},
40     {"template6", std::string(), true}, {"template7", std::string(), true},
41     {"template8", std::string(), true}, {"template9", std::string(), true}};
42 }  // namespace
43 
44 /* backslash_escape - do the actual substitution task for template objects
45  *
46  * The field templates is used for substituting the \N occurrences. Set it to
47  * nullptr to leave them as they are.
48  */
backslash_escape(const char * src,char ** templates,unsigned int template_count)49 static char *backslash_escape(const char *src, char **templates,
50                               unsigned int template_count) {
51   char *src_dup;
52   const char *p;
53   unsigned int dup_idx = 0, dup_len;
54 
55   dup_len = strlen(src) + 1;
56   src_dup = static_cast<char *>(malloc(dup_len * sizeof(char)));
57 
58   p = src;
59   while (*p != 0) {
60     switch (*p) {
61       case '\\':
62         if (*(p + 1) == 0) { break; }
63         if (*(p + 1) == '\\') {
64           src_dup[dup_idx++] = '\\';
65           p++;
66         } else if (*(p + 1) == ' ') {
67           src_dup[dup_idx++] = ' ';
68           p++;
69         } else if (*(p + 1) == 'n') {
70           src_dup[dup_idx++] = '\n';
71           p++;
72         } else if (templates != nullptr) {
73           unsigned int tmpl_num;
74           int digits;
75           if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) ||
76               (tmpl_num > template_count)) {
77             break;
78           }
79           if (tmpl_num == 0) {
80             CRIT_ERR(
81                 nullptr, nullptr,
82                 "invalid template argument \\0; arguments must start at \\1");
83           }
84           dup_len += strlen(templates[tmpl_num - 1]);
85           src_dup =
86               static_cast<char *>(realloc(src_dup, dup_len * sizeof(char)));
87           snprintf(src_dup + dup_idx, dup_len - dup_idx, "%s",
88                    templates[tmpl_num - 1]);
89           dup_idx += strlen(templates[tmpl_num - 1]);
90           p += digits;
91         }
92         break;
93       default:
94         src_dup[dup_idx++] = *p;
95         break;
96     }
97     p++;
98   }
99   src_dup[dup_idx++] = '\0';
100   src_dup = static_cast<char *>(realloc(src_dup, dup_idx * sizeof(char)));
101   return src_dup;
102 }
103 
104 /* handle_template_object - core logic of the template object
105  *
106  * use config variables like this:
107  * template1 = "$\1\2"
108  * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}"
109  *
110  * and use them like this:
111  * ${template1 node name}
112  * ${template2 root /}
113  * ${template2 cdrom /mnt/cdrom}
114  */
handle_template(const char * tmpl,const char * args)115 static char *handle_template(const char *tmpl, const char *args) {
116   char *args_dup = nullptr;
117   char *p, *p_old;
118   char **argsp = nullptr;
119   unsigned int argcnt = 0, template_idx, i;
120   char *eval_text;
121 
122   if ((sscanf(tmpl, "template%u", &template_idx) != 1) ||
123       (template_idx >= MAX_TEMPLATES)) {
124     return nullptr;
125   }
126 
127   if (args != nullptr) {
128     args_dup = strdup(args);
129     p = args_dup;
130     while (*p != 0) {
131       while ((*p != 0) && (*p == ' ' && (p == args_dup || *(p - 1) != '\\'))) {
132         p++;
133       }
134       if (p > args_dup && *(p - 1) == '\\') { p--; }
135       p_old = p;
136       while ((*p != 0) && (*p != ' ' || (p > args_dup && *(p - 1) == '\\'))) {
137         p++;
138       }
139       if (*p != 0) {
140         (*p) = '\0';
141         p++;
142       }
143       argsp = static_cast<char **>(realloc(argsp, ++argcnt * sizeof(char *)));
144       argsp[argcnt - 1] = p_old;
145     }
146     for (i = 0; i < argcnt; i++) {
147       char *tmp;
148       tmp = backslash_escape(argsp[i], nullptr, 0);
149       DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp);
150       argsp[i] = tmp;
151     }
152   }
153 
154   eval_text = backslash_escape(_template[template_idx].get(*state).c_str(),
155                                argsp, argcnt);
156   DBGP("substituted %s, output is '%s'", tmpl, eval_text);
157   free(args_dup);
158   for (i = 0; i < argcnt; i++) { free(argsp[i]); }
159   free(argsp);
160   return eval_text;
161 }
162 
163 /* Search inbuf and replace all found template object references
164  * with the substituted value. */
find_and_replace_templates(const char * inbuf)165 char *find_and_replace_templates(const char *inbuf) {
166   char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out;
167   int stack, outlen;
168 
169   outlen = strlen(inbuf) + 1;
170   o = outbuf = static_cast<char *>(calloc(outlen, sizeof(char)));
171   memset(outbuf, 0, outlen * sizeof(char));
172 
173   p = indup = strdup(inbuf);
174   while (*p != 0) {
175     while ((*p != 0) && *p != '$') { *(o++) = *(p++); }
176 
177     if ((*p) == 0) { break; }
178 
179     if ((static_cast<int>(strncmp(p, "$template", strlen("$template")) != 0) !=
180          0) &&
181         (strncmp(p, "${template", strlen("${template")) != 0)) {
182       *(o++) = *(p++);
183       continue;
184     }
185 
186     if (*(p + 1) == '{') {
187       p += 2;
188       templ = p;
189       while ((*p != 0) && (isspace(static_cast<unsigned char>(*p)) == 0) &&
190              *p != '{' && *p != '}') {
191         p++;
192       }
193       if (*p == '}') {
194         args = nullptr;
195       } else {
196         args = p;
197       }
198 
199       stack = 1;
200       while ((*p != 0) && stack > 0) {
201         if (*p == '{') {
202           stack++;
203         } else if (*p == '}') {
204           stack--;
205         }
206         p++;
207       }
208       if (stack == 0) {
209         // stack is empty. that means the previous char was }, so we zero it
210         *(p - 1) = '\0';
211       } else {
212         // we ran into the end of string without finding a closing }, bark
213         CRIT_ERR(nullptr, nullptr,
214                  "cannot find a closing '}' in template expansion");
215       }
216     } else {
217       templ = p + 1;
218       p += strlen("$template");
219       while ((*p != 0) && (isdigit(static_cast<unsigned char>(*p)) != 0)) {
220         p++;
221       }
222       args = nullptr;
223     }
224     tmpl_out = handle_template(templ, args);
225     if (tmpl_out != nullptr) {
226       int len = strlen(tmpl_out);
227       outlen += len;
228       *o = '\0';
229       outbuf = static_cast<char *>(realloc(outbuf, outlen * sizeof(char)));
230       strncat(outbuf, tmpl_out, len);
231       free(tmpl_out);
232       o = outbuf + strlen(outbuf);
233     } else {
234       NORM_ERR("failed to handle template '%s' with args '%s'", templ, args);
235     }
236   }
237   *o = '\0';
238   outbuf =
239       static_cast<char *>(realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char)));
240   free(indup);
241   return outbuf;
242 }
243 
244 /* check text for any template object references */
text_contains_templates(const char * text)245 int text_contains_templates(const char *text) {
246   if (strcasestr(text, "${template") != nullptr) { return 1; }
247   if (strcasestr(text, "$template") != nullptr) { return 1; }
248   return 0;
249 }
250