1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9
10 #include "../exim.h"
11 #include "lf_functions.h"
12
13
14
15 /*************************************************
16 * Call SQL server(s) to run an actual query *
17 *************************************************/
18
19 /* All the SQL lookups are of the same form, with a list of servers to try
20 until one can be accessed. It is now also possible to provide the server data
21 as part of the query. This function manages server selection and looping; each
22 lookup has its own function for actually performing the lookup.
23
24 Arguments:
25 name the lookup name, e.g. "MySQL"
26 optionname the name of the servers option, e.g. "mysql_servers"
27 optserverlist the value of the servers option
28 query the query
29 result where to pass back the result
30 errmsg where to pass back an error message
31 do_cache to be set zero if data is changed
32 func the lookup function to call
33
34 Returns: the return from the lookup function, or DEFER
35 */
36
37 int
lf_sqlperform(const uschar * name,const uschar * optionname,const uschar * optserverlist,const uschar * query,uschar ** result,uschar ** errmsg,uint * do_cache,const uschar * opts,int (* fn)(const uschar *,uschar *,uschar **,uschar **,BOOL *,uint *,const uschar *))38 lf_sqlperform(const uschar *name, const uschar *optionname,
39 const uschar *optserverlist, const uschar *query,
40 uschar **result, uschar **errmsg, uint *do_cache, const uschar * opts,
41 int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *))
42 {
43 int rc;
44 uschar *server;
45 BOOL defer_break = FALSE;
46
47 DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts);
48
49 /* Handle queries that do have server information at the start. */
50
51 if (Ustrncmp(query, "servers", 7) == 0)
52 {
53 int qsep = 0;
54 const uschar *s, *ss;
55 const uschar *qserverlist;
56 uschar *qserver;
57
58 s = query + 7;
59 skip_whitespace(&s);
60 if (*s++ != '=')
61 {
62 *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
63 return DEFER;
64 }
65 skip_whitespace(&s);
66
67 ss = Ustrchr(s, ';');
68 if (!ss)
69 {
70 *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
71 name);
72 return DEFER;
73 }
74
75 if (ss == s)
76 {
77 *errmsg = string_sprintf("\"servers=\" defines no servers in \"%s\"",
78 query);
79 return DEFER;
80 }
81
82 qserverlist = string_sprintf("%.*s", (int)(ss - s), s);
83
84 while ((qserver = string_nextinlist(&qserverlist, &qsep, NULL, 0)))
85 {
86 if (Ustrchr(qserver, '/'))
87 server = qserver;
88 else
89 {
90 int len = Ustrlen(qserver);
91 const uschar * serverlist = optserverlist;
92
93 for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);)
94 if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/')
95 break;
96
97 if (!server)
98 {
99 *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
100 qserver, optionname);
101 return DEFER;
102 }
103 }
104
105 { uschar *m;
106 if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)))
107 {
108 *errmsg = m;
109 return DEFER;
110 }
111 }
112
113 rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts);
114 if (rc != DEFER || defer_break) return rc;
115 }
116 }
117
118 /* Handle queries that do not have server information at the start. */
119
120 else
121 {
122 const uschar * serverlist = NULL;
123
124 /* If options are present, scan for a server definition. Default to
125 the "optserverlist" srgument. */
126
127 if (opts)
128 {
129 uschar * ele;
130 for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
131 if (Ustrncmp(ele, "servers=", 8) == 0)
132 { serverlist = ele + 8; break; }
133 }
134
135 if (!serverlist)
136 serverlist = optserverlist;
137 if (!serverlist)
138 *errmsg = string_sprintf("no %s servers defined (%s option)", name,
139 optionname);
140 else
141 for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); )
142 {
143 /* If not a full spec assume from options; scan main list for matching
144 hostname */
145
146 if (!Ustrchr(server, '/'))
147 {
148 int len = Ustrlen(server);
149 const uschar * slist = optserverlist;
150 uschar * ele;
151 for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
152 if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
153 break;
154 if (!ele)
155 {
156 *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
157 server, optionname);
158 return DEFER;
159 }
160 server = ele;
161 }
162
163 { uschar *m;
164 if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)))
165 {
166 *errmsg = m;
167 return DEFER;
168 }
169 }
170
171 rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
172 if (rc != DEFER || defer_break) return rc;
173 }
174 }
175
176 return DEFER;
177 }
178
179 /* End of lf_sqlperform.c */
180