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
lf_sqlperform(const uschar * name,const uschar * optionname,const uschar * optserverlist,const uschar * query,uschar ** result,uschar ** errmsg,uint * do_cache,const uschar * opts,int (* fn)(const uschar *,uschar *,uschar **,uschar **,BOOL *,uint *,const uschar *))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 */
180