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