9eb936b25da466350210a72eea713af62387538f
[exim.git] / src / src / lookups / oracle.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2015 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10 /* Interface to an Oracle database. This code was originally supplied by
11 Paul Kelly, but I have hacked it around for various reasons, and tried to add
12 some comments from my position of Oracle ignorance. */
13
14
15 #include "../exim.h"
16
17
18 /* The Oracle system headers */
19
20 #include <oratypes.h>
21 #include <ocidfn.h>
22 #include <ocikpr.h>
23
24 #define PARSE_NO_DEFER           0     /* parse straight away */
25 #define PARSE_V7_LNG             2
26 #define MAX_ITEM_BUFFER_SIZE  1024     /* largest size of a cell of data */
27 #define MAX_SELECT_LIST_SIZE    32     /* maximum number of columns (not rows!) */
28
29 /* Paul's comment on this was "change this to 512 for 64bit cpu", but I don't
30 understand why. The Oracle manual just asks for 256 bytes.
31
32 That was years ago. Jin Choi suggested (March 2007) that this change should
33 be made in the source, as at worst it wastes 256 bytes, and it saves people
34 having to discover about this for themselves as more and more systems are
35 64-bit. So I have changed 256 to 512. */
36
37 #define HDA_SIZE 512
38
39 /* Internal/external datatype codes */
40
41 #define NUMBER_TYPE              2
42 #define INT_TYPE                 3
43 #define FLOAT_TYPE               4
44 #define STRING_TYPE              5
45 #define ROWID_TYPE              11
46 #define DATE_TYPE               12
47
48 /*  ORACLE error codes used in demonstration programs */
49
50 #define VAR_NOT_IN_LIST       1007
51 #define NO_DATA_FOUND         1403
52
53 typedef struct Ora_Describe {
54   sb4   dbsize;
55   sb2   dbtype;
56   sb1   buf[MAX_ITEM_BUFFER_SIZE];
57   sb4   buflen;
58   sb4   dsize;
59   sb2   precision;
60   sb2   scale;
61   sb2   nullok;
62 } Ora_Describe;
63
64 typedef struct Ora_Define {
65   ub1   buf[MAX_ITEM_BUFFER_SIZE];
66   float flt_buf;
67   sword int_buf;
68   sb2   indp;
69   ub2   col_retlen, col_retcode;
70 } Ora_Define;
71
72 /* Structure and anchor for caching connections. */
73
74 typedef struct oracle_connection {
75   struct oracle_connection *next;
76   uschar *server;
77   struct cda_def *handle;
78   void   *hda_mem;
79 } oracle_connection;
80
81 static oracle_connection *oracle_connections = NULL;
82
83
84
85
86
87 /*************************************************
88 *        Set up message after error              *
89 *************************************************/
90
91 /* Sets up a message from a local string plus whatever Oracle gives.
92
93 Arguments:
94   oracle_handle   the handle of the connection
95   rc              the return code
96   msg             local text message
97 */
98
99 static uschar *
100 oracle_error(struct cda_def *oracle_handle, int rc, uschar *msg)
101 {
102 uschar tmp[1024];
103 oerhms(oracle_handle, rc, tmp, sizeof(tmp));
104 return string_sprintf("ORACLE %s: %s", msg, tmp);
105 }
106
107
108
109 /*************************************************
110 *     Describe and define the select list items  *
111 *************************************************/
112
113 /* Figures out sizes, types, and numbers.
114
115 Arguments:
116   cda        the connection
117   def
118   desc       descriptions put here
119
120 Returns:     number of fields
121 */
122
123 static sword
124 describe_define(Cda_Def *cda, Ora_Define *def, Ora_Describe *desc)
125 {
126 sword col, deflen, deftyp;
127 static ub1 *defptr;
128 static sword numwidth = 8;
129
130 /* Describe the select-list items. */
131
132 for (col = 0; col < MAX_SELECT_LIST_SIZE; col++)
133   {
134   desc[col].buflen = MAX_ITEM_BUFFER_SIZE;
135
136   if (odescr(cda, col + 1, &desc[col].dbsize,
137              &desc[col].dbtype, &desc[col].buf[0],
138              &desc[col].buflen, &desc[col].dsize,
139              &desc[col].precision, &desc[col].scale,
140              &desc[col].nullok) != 0)
141     {
142     /* Break on end of select list. */
143     if (cda->rc == VAR_NOT_IN_LIST) break; else return -1;
144     }
145
146   /* Adjust sizes and types for display, handling NUMBER with scale as float. */
147
148   if (desc[col].dbtype == NUMBER_TYPE)
149     {
150     desc[col].dbsize = numwidth;
151     if (desc[col].scale != 0)
152       {
153       defptr = (ub1 *)&def[col].flt_buf;
154       deflen = (sword) sizeof(float);
155       deftyp = FLOAT_TYPE;
156       desc[col].dbtype = FLOAT_TYPE;
157       }
158     else
159       {
160       defptr = (ub1 *)&def[col].int_buf;
161       deflen = (sword) sizeof(sword);
162       deftyp = INT_TYPE;
163       desc[col].dbtype = INT_TYPE;
164       }
165     }
166   else
167     {
168     if (desc[col].dbtype == DATE_TYPE)
169         desc[col].dbsize = 9;
170     if (desc[col].dbtype == ROWID_TYPE)
171         desc[col].dbsize = 18;
172     defptr = def[col].buf;
173     deflen = desc[col].dbsize > MAX_ITEM_BUFFER_SIZE ?
174       MAX_ITEM_BUFFER_SIZE : desc[col].dbsize + 1;
175     deftyp = STRING_TYPE;
176     desc[col].dbtype = STRING_TYPE;
177     }
178
179   /* Define an output variable */
180
181   if (odefin(cda, col + 1,
182              defptr, deflen, deftyp,
183              -1, &def[col].indp, (text *) 0, -1, -1,
184              &def[col].col_retlen,
185              &def[col].col_retcode) != 0)
186     return -1;
187   }  /* Loop for each column */
188
189 return col;
190 }
191
192
193
194 /*************************************************
195 *              Open entry point                  *
196 *************************************************/
197
198 /* See local README for interface description. */
199
200 static void *
201 oracle_open(const uschar * filename, uschar ** errmsg)
202 {
203 return (void *)(1);    /* Just return something non-null */
204 }
205
206
207
208 /*************************************************
209 *               Tidy entry point                 *
210 *************************************************/
211
212 /* See local README for interface description. */
213
214 static void
215 oracle_tidy(void)
216 {
217 oracle_connection *cn;
218 while ((cn = oracle_connections) != NULL)
219   {
220   oracle_connections = cn->next;
221   DEBUG(D_lookup) debug_printf_indent("close ORACLE connection: %s\n", cn->server);
222   ologof(cn->handle);
223   }
224 }
225
226
227
228 /*************************************************
229 *        Internal search function                *
230 *************************************************/
231
232 /* This function is called from the find entry point to do the search for a
233 single server.
234
235 Arguments:
236   query        the query string
237   server       the server string
238   resultptr    where to store the result
239   errmsg       where to point an error message
240   defer_break  TRUE if no more servers are to be tried after DEFER
241
242 The server string is of the form "host/dbname/user/password", for compatibility
243 with MySQL and pgsql, but at present, the dbname is not used. This string is in
244 a nextinlist temporary buffer, so can be overwritten.
245
246 Returns:       OK, FAIL, or DEFER
247 */
248
249 static int
250 perform_oracle_search(uschar *query, uschar *server, uschar **resultptr,
251   uschar **errmsg, BOOL *defer_break)
252 {
253 Cda_Def *cda = NULL;
254 struct cda_def *oracle_handle = NULL;
255 Ora_Describe *desc = NULL;
256 Ora_Define *def = NULL;
257 void *hda = NULL;
258
259 int yield = DEFER;
260 unsigned int num_fields = 0;
261 gstring * result = NULL;
262 oracle_connection *cn = NULL;
263 uschar *server_copy = NULL;
264 uschar *sdata[4];
265
266 /* Disaggregate the parameters from the server argument. The order is host,
267 database, user, password. We can write to the string, since it is in a
268 nextinlist temporary buffer. The copy of the string that is used for caching
269 has the password removed. This copy is also used for debugging output. */
270
271 for (int i = 3; i > 0; i--)
272   {
273   uschar *pp = Ustrrchr(server, '/');
274   if (pp == NULL)
275     {
276     *errmsg = string_sprintf("incomplete ORACLE server data: %s", server);
277     *defer_break = TRUE;
278     return DEFER;
279     }
280   *pp++ = 0;
281   sdata[i] = pp;
282   if (i == 3) server_copy = string_copy(server);  /* sans password */
283   }
284 sdata[0] = server;   /* What's left at the start */
285
286 /* If the database is the empty string, set it NULL - the query must then
287 define it. */
288
289 if (sdata[1][0] == 0) sdata[1] = NULL;
290
291 /* See if we have a cached connection to the server */
292
293 for (cn = oracle_connections; cn; cn = cn->next)
294   if (strcmp(cn->server, server_copy) == 0)
295     {
296     oracle_handle = cn->handle;
297     hda = cn->hda_mem;
298     break;
299     }
300
301 /* If no cached connection, we must set one up */
302
303 if (!cn)
304   {
305   DEBUG(D_lookup) debug_printf_indent("ORACLE new connection: host=%s database=%s "
306     "user=%s\n", sdata[0], sdata[1], sdata[2]);
307
308   /* Get store for a new connection, initialize it, and connect to the server */
309
310    oracle_handle = store_get(sizeof(struct cda_def), GET_UNTAINTED);
311    hda = store_get(HDA_SIZE, GET_UNTAINTED);
312    memset(hda,'\0',HDA_SIZE);
313
314   /*
315    * Perform a default (blocking) login
316    *
317    * sdata[0] = tnsname (service name - typically host name)
318    * sdata[1] = dbname - not used at present
319    * sdata[2] = username
320    * sdata[3] = passwd
321    */
322
323   if(olog(oracle_handle, hda, sdata[2], -1, sdata[3], -1, sdata[0], -1,
324          (ub4)OCI_LM_DEF) != 0)
325     {
326     *errmsg = oracle_error(oracle_handle, oracle_handle->rc,
327       US"connection failed");
328     *defer_break = FALSE;
329     goto ORACLE_EXIT_NO_VALS;
330     }
331
332   /* Add the connection to the cache */
333
334   cn = store_get(sizeof(oracle_connection), GET_UNTAINTED);
335   cn->server = server_copy;
336   cn->handle = oracle_handle;
337   cn->next = oracle_connections;
338   cn->hda_mem = hda;
339   oracle_connections = cn;
340   }
341
342 /* Else use a previously cached connection - we can write to the server string
343 to obliterate the password because it is in a nextinlist temporary buffer. */
344
345 else
346   {
347   DEBUG(D_lookup)
348     debug_printf_indent("ORACLE using cached connection for %s\n", server_copy);
349   }
350
351 /* We have a connection. Open a cursor and run the query */
352
353 cda = store_get(sizeof(Cda_Def), GET_UNTAINTED);
354
355 if (oopen(cda, oracle_handle, (text *)0, -1, -1, (text *)0, -1) != 0)
356   {
357   *errmsg = oracle_error(oracle_handle, cda->rc, "failed to open cursor");
358   *defer_break = FALSE;
359   goto ORACLE_EXIT_NO_VALS;
360   }
361
362 if (oparse(cda, (text *)query, (sb4) -1,
363       (sword)PARSE_NO_DEFER, (ub4)PARSE_V7_LNG) != 0)
364   {
365   *errmsg = oracle_error(oracle_handle, cda->rc, "query failed");
366   *defer_break = FALSE;
367   oclose(cda);
368   goto ORACLE_EXIT_NO_VALS;
369   }
370
371 /* Find the number of fields returned and sort out their types. If the number
372 is one, we don't add field names to the data. Otherwise we do. */
373
374 def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE, GET_UNTAINTED);
375 desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE, GET_UNTAINTED);
376
377 if ((num_fields = describe_define(cda,def,desc)) == -1)
378   {
379   *errmsg = oracle_error(oracle_handle, cda->rc, "describe_define failed");
380   *defer_break = FALSE;
381   goto ORACLE_EXIT;
382   }
383
384 if (oexec(cda)!=0)
385   {
386   *errmsg = oracle_error(oracle_handle, cda->rc, "oexec failed");
387   *defer_break = FALSE;
388   goto ORACLE_EXIT;
389   }
390
391 /* Get the fields and construct the result string. If there is more than one
392 row, we insert '\n' between them. */
393
394 while (cda->rc != NO_DATA_FOUND)  /* Loop for each row */
395   {
396   ofetch(cda);
397   if(cda->rc == NO_DATA_FOUND) break;
398
399   if (result) result = string_catn(result, "\n", 1);
400
401   /* Single field - just add on the data */
402
403   if (num_fields == 1)
404     result = string_catn(result, def[0].buf, def[0].col_retlen);
405
406   /* Multiple fields - precede by file name, removing {lead,trail}ing WS */
407
408   else for (int i = 0; i < num_fields; i++)
409     {
410     int slen;
411     uschar * s = US desc[i].buf;
412
413     Uskip_whitespace(&s);
414     slen = Ustrlen(s);
415     while (slen > 0 && isspace(s[slen-1])) slen--;
416     result = string_catn(result, s, slen);
417     result = string_catn(result, US"=", 1);
418
419     /* int and float type won't ever need escaping. Otherwise, quote the value
420     if it contains spaces or is empty. */
421
422     if (desc[i].dbtype != INT_TYPE && desc[i].dbtype != FLOAT_TYPE &&
423        (def[i].buf[0] == 0 || strchr(def[i].buf, ' ') != NULL))
424       {
425       result = string_catn(result, "\"", 1);
426       for (int j = 0; j < def[i].col_retlen; j++)
427         {
428         if (def[i].buf[j] == '\"' || def[i].buf[j] == '\\')
429           result = string_catn(result, "\\", 1);
430         result = string_catn(result, def[i].buf+j, 1);
431         }
432       result = string_catn(result, "\"", 1);
433       }
434
435     else switch(desc[i].dbtype)
436       {
437       case INT_TYPE:
438         result = string_cat(result, string_sprintf("%d", def[i].int_buf));
439         break;
440
441       case FLOAT_TYPE:
442         result = string_cat(result, string_sprintf("%f", def[i].flt_buf));
443         break;
444
445       case STRING_TYPE:
446         result = string_catn(result, def[i].buf, def[i].col_retlen);
447         break;
448
449       default:
450         *errmsg = string_sprintf("ORACLE: unknown field type %d", desc[i].dbtype);
451         *defer_break = FALSE;
452         result = NULL;
453         goto ORACLE_EXIT;
454       }
455
456     result = string_catn(result, " ", 1);
457     }
458   }
459
460 /* If result is NULL then no data has been found and so we return FAIL.
461 Otherwise, we must terminate the string which has been built; string_cat()
462 always leaves enough room for a terminating zero. */
463
464 if (!result)
465   {
466   yield = FAIL;
467   *errmsg = "ORACLE: no data found";
468   }
469 else
470   gstring_release_unused(result);
471
472 /* Get here by goto from various error checks. */
473
474 ORACLE_EXIT:
475
476 /* Close the cursor; don't close the connection, as it is cached. */
477
478 oclose(cda);
479
480 ORACLE_EXIT_NO_VALS:
481
482 /* Non-NULL result indicates a successful result */
483
484 if (result)
485   {
486   *resultptr = string_from_gstring(result);
487   return OK;
488   }
489 else
490   {
491   DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
492   return yield;      /* FAIL or DEFER */
493   }
494 }
495
496
497
498
499 /*************************************************
500 *               Find entry point                 *
501 *************************************************/
502
503 /* See local README for interface description. The handle and filename
504 arguments are not used. Loop through a list of servers while the query is
505 deferred with a retryable error. */
506
507 static int
508 oracle_find(void * handle, const uschar * filename, uschar * query, int length,
509   uschar ** result, uschar ** errmsg, uint * do_cache, const uschar * opts)
510 {
511 int sep = 0;
512 uschar *server;
513 uschar *list = oracle_servers;
514
515 do_cache = do_cache;   /* Placate picky compilers */
516
517 DEBUG(D_lookup) debug_printf_indent("ORACLE query: %s\n", query);
518
519 while ((server = string_nextinlist(&list, &sep, NULL, 0)))
520   {
521   BOOL defer_break;
522   int rc = perform_oracle_search(query, server, result, errmsg, &defer_break);
523   if (rc != DEFER || defer_break) return rc;
524   }
525
526 if (!oracle_servers)
527   *errmsg = "no ORACLE servers defined (oracle_servers option)";
528
529 return DEFER;
530 }
531
532
533
534 /*************************************************
535 *               Quote entry point                *
536 *************************************************/
537
538 /* The only characters that need to be quoted (with backslash) are newline,
539 tab, carriage return, backspace, backslash itself, and the quote characters.
540 Percent and underscore are not escaped. They are only special in contexts where
541 they can be wild cards, and this isn't usually the case for data inserted from
542 messages, since that isn't likely to be treated as a pattern of any kind.
543
544 Arguments:
545   s          the string to be quoted
546   opt        additional option text or NULL if none
547   idx        lookup type index
548
549 Returns:     the processed string or NULL for a bad option
550 */
551
552 static uschar *
553 oracle_quote(uschar * s, uschar * opt, unsigned idx)
554 {
555 int c, count = 0;
556 uschar * t = s, * quoted;
557
558 if (opt) return NULL;    /* No options are recognized */
559
560 while ((c = *t++))
561   if (strchr("\n\t\r\b\'\"\\", c) != NULL) count++;
562
563 t = quoted = store_get_quoted((int)Ustrlen(s) + count + 1, s, idx);
564
565 while ((c = *s++))
566   {
567   if (strchr("\n\t\r\b\'\"\\", c) != NULL)
568     {
569     *t++ = '\\';
570     switch(c)
571       {
572       case '\n': *t++ = 'n';
573       break;
574       case '\t': *t++ = 't';
575       break;
576       case '\r': *t++ = 'r';
577       break;
578       case '\b': *t++ = 'b';
579       break;
580       default:   *t++ = c;
581       break;
582       }
583     }
584   else *t++ = c;
585   }
586
587 *t = 0;
588 return quoted;
589 }
590
591
592 /*************************************************
593 *         Version reporting entry point          *
594 *************************************************/
595
596 /* See local README for interface description. */
597
598 #include "../version.h"
599
600 gstring *
601 oracle_version_report(gstring * g)
602 {
603 #ifdef DYNLOOKUP
604 g = string_fmt_append(g, "Library version: Oracle: Exim version %s\n", EXIM_VERSION_STR);
605 #endif
606 return g;
607 }
608
609
610 static lookup_info _lookup_info = {
611   .name = US"oracle",                   /* lookup name */
612   .type = lookup_querystyle,            /* query-style lookup */
613   .open = oracle_open,                  /* open function */
614   .check = NULL,                        /* check function */
615   .find = oracle_find,                  /* find function */
616   .close = NULL,                        /* no close function */
617   .tidy = oracle_tidy,                  /* tidy function */
618   .quote = oracle_quote,                /* quoting function */
619   .version_report = oracle_version_report          /* version reporting */
620 };
621
622 #ifdef DYNLOOKUP
623 #define oracle_lookup_module_info _lookup_module_info
624 #endif
625
626 static lookup_info *_lookup_list[] = { &_lookup_info };
627 lookup_module_info oracle_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
628
629 /* End of lookups/oracle.c */