Copyright updates:
[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     if (is_tainted(server))
106       {
107       *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
108       return DEFER;
109       }
110
111     rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts);
112     if (rc != DEFER || defer_break) return rc;
113     }
114   }
115
116 /* Handle queries that do not have server information at the start. */
117
118 else
119   {
120   const uschar * serverlist = NULL;
121
122   /* If options are present, scan for a server definition.  Default to
123   the "optserverlist" srgument. */
124
125   if (opts)
126     {
127     uschar * ele;
128     for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
129       if (Ustrncmp(ele, "servers=", 8) == 0)
130         { serverlist = ele + 8; break; }
131     }
132
133   if (!serverlist)
134     serverlist = optserverlist;
135   if (!serverlist)
136     *errmsg = string_sprintf("no %s servers defined (%s option)", name,
137       optionname);
138   else
139     for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); )
140       {
141       /* If not a full spec assume from options; scan main list for matching
142       hostname */
143
144       if (!Ustrchr(server, '/'))
145         {
146         int len = Ustrlen(server);
147         const uschar * slist = optserverlist;
148         uschar * ele;
149         for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
150           if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
151             break;
152         if (!ele)
153           {
154           *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
155             server, optionname);
156           return DEFER;
157           }
158         server = ele;
159         }
160
161       if (is_tainted(server))
162         {
163         *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
164         return DEFER;
165         }
166
167       rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
168       if (rc != DEFER || defer_break) return rc;
169       }
170   }
171
172 return DEFER;
173 }
174
175 /* End of lf_sqlperform.c */