38b7c2ad345c4bde4f6cbcadb74ae42994eb2f8a
[exim.git] / src / src / lookups / lf_sqlperform.c
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
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 */