d5bb8d2976c7b4d97b754708ab631dce3430617b
[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 - 2024 */
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-or-later */
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   log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obslete syntax used for lookup\n");
60
61   s = query + 7;
62   skip_whitespace(&s);
63   if (*s++ != '=')
64     {
65     *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
66     return DEFER;
67     }
68   skip_whitespace(&s);
69
70   ss = Ustrchr(s, ';');
71   if (!ss)
72     {
73     *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
74       name);
75     return DEFER;
76     }
77
78   if (ss == s)
79     {
80     *errmsg = string_sprintf("\"servers=\" defines no servers in \"%s\"",
81       query);
82     return DEFER;
83     }
84
85   qserverlist = string_sprintf("%.*s", (int)(ss - s), s);
86
87   while ((qserver = string_nextinlist(&qserverlist, &qsep, NULL, 0)))
88     {
89     if (Ustrchr(qserver, '/'))
90       server = qserver;
91     else
92       {
93       int len = Ustrlen(qserver);
94       const uschar * serverlist = optserverlist;
95
96       for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);)
97         if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/')
98           break;
99
100       if (!server)
101         {
102         *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
103           qserver, optionname);
104         return DEFER;
105         }
106       }
107
108     if (is_tainted(server))
109       {
110       *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
111       return DEFER;
112       }
113
114     rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts);
115     if (rc != DEFER || defer_break) return rc;
116     }
117   }
118
119 /* Handle queries that do not have server information at the start. */
120
121 else
122   {
123   const uschar * serverlist = NULL;
124
125   /* If options are present, scan for a server definition.  Default to
126   the "optserverlist" srgument. */
127
128   if (opts)
129     {
130     uschar * ele;
131     for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
132       if (Ustrncmp(ele, "servers=", 8) == 0)
133         { serverlist = ele + 8; break; }
134     }
135
136   if (!serverlist)
137     serverlist = optserverlist;
138   if (!serverlist)
139     *errmsg = string_sprintf("no %s servers defined (%s option)", name,
140       optionname);
141   else
142     for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); )
143       {
144       /* If not a full spec assume from options; scan main list for matching
145       hostname */
146
147       if (!Ustrchr(server, '/'))
148         {
149         int len = Ustrlen(server);
150         const uschar * slist = optserverlist;
151         uschar * ele;
152         for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
153           if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
154             break;
155         if (!ele)
156           {
157           *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
158             server, optionname);
159           return DEFER;
160           }
161         server = ele;
162         }
163
164       if (is_tainted(server))
165         {
166         *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
167         return DEFER;
168         }
169
170       rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
171       if (rc != DEFER || defer_break) return rc;
172       }
173   }
174
175 return DEFER;
176 }
177
178 /* End of lf_sqlperform.c */