| 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, |
| 40 | int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *)) |
| 41 | { |
| 42 | int sep, rc; |
| 43 | uschar *server; |
| 44 | const uschar *serverlist; |
| 45 | uschar buffer[512]; |
| 46 | BOOL defer_break = FALSE; |
| 47 | |
| 48 | DEBUG(D_lookup) debug_printf_indent("%s query: %s\n", name, query); |
| 49 | |
| 50 | /* Handle queries that do not have server information at the start. */ |
| 51 | |
| 52 | if (Ustrncmp(query, "servers", 7) != 0) |
| 53 | { |
| 54 | sep = 0; |
| 55 | serverlist = optserverlist; |
| 56 | while ((server = string_nextinlist(&serverlist, &sep, buffer, |
| 57 | sizeof(buffer))) != NULL) |
| 58 | { |
| 59 | rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache); |
| 60 | if (rc != DEFER || defer_break) return rc; |
| 61 | } |
| 62 | if (optserverlist == NULL) |
| 63 | *errmsg = string_sprintf("no %s servers defined (%s option)", name, |
| 64 | optionname); |
| 65 | } |
| 66 | |
| 67 | /* Handle queries that do have server information at the start. */ |
| 68 | |
| 69 | else |
| 70 | { |
| 71 | int qsep; |
| 72 | const uschar *s, *ss; |
| 73 | const uschar *qserverlist; |
| 74 | uschar *qserver; |
| 75 | uschar qbuffer[512]; |
| 76 | |
| 77 | s = query + 7; |
| 78 | while (isspace(*s)) s++; |
| 79 | if (*s++ != '=') |
| 80 | { |
| 81 | *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name); |
| 82 | return DEFER; |
| 83 | } |
| 84 | while (isspace(*s)) s++; |
| 85 | |
| 86 | ss = Ustrchr(s, ';'); |
| 87 | if (ss == NULL) |
| 88 | { |
| 89 | *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup", |
| 90 | name); |
| 91 | return DEFER; |
| 92 | } |
| 93 | |
| 94 | if (ss == s) |
| 95 | { |
| 96 | *errmsg = string_sprintf("\"servers=\" defines no servers in \"%s\"", |
| 97 | query); |
| 98 | return DEFER; |
| 99 | } |
| 100 | |
| 101 | qserverlist = string_sprintf("%.*s", (int)(ss - s), s); |
| 102 | qsep = 0; |
| 103 | |
| 104 | while ((qserver = string_nextinlist(&qserverlist, &qsep, qbuffer, |
| 105 | sizeof(qbuffer))) != NULL) |
| 106 | { |
| 107 | if (Ustrchr(qserver, '/') != NULL) |
| 108 | server = qserver; |
| 109 | else |
| 110 | { |
| 111 | int len = Ustrlen(qserver); |
| 112 | |
| 113 | sep = 0; |
| 114 | serverlist = optserverlist; |
| 115 | while ((server = string_nextinlist(&serverlist, &sep, buffer, |
| 116 | sizeof(buffer))) != NULL) |
| 117 | { |
| 118 | if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/') |
| 119 | break; |
| 120 | } |
| 121 | |
| 122 | if (server == NULL) |
| 123 | { |
| 124 | *errmsg = string_sprintf("%s server \"%s\" not found in %s", name, |
| 125 | qserver, optionname); |
| 126 | return DEFER; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache); |
| 131 | if (rc != DEFER || defer_break) return rc; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | return DEFER; |
| 136 | } |
| 137 | |
| 138 | /* End of lf_sqlperform.c */ |