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