Add dynamic lookup support
[exim.git] / src / src / lookups / oracle.c
CommitLineData
0a49a7a4 1/* $Cambridge: exim/src/src/lookups/oracle.c,v 1.6 2009/11/16 19:50:38 nm4 Exp $ */
0756eb3c
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
0a49a7a4 7/* Copyright (c) University of Cambridge 1995 - 2009 */
0756eb3c
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10/* Interface to an Oracle database. This code was originally supplied by
11Paul Kelly, but I have hacked it around for various reasons, and tried to add
12some comments from my position of Oracle ignorance. */
13
14
15#include "../exim.h"
16
17
0756eb3c
PH
18/* The Oracle system headers */
19
20#include <oratypes.h>
21#include <ocidfn.h>
22#include <ocikpr.h>
23
0756eb3c
PH
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
dee5a20a 30understand why. The Oracle manual just asks for 256 bytes.
0756eb3c 31
dee5a20a
PH
32That was years ago. Jin Choi suggested (March 2007) that this change should
33be made in the source, as at worst it wastes 256 bytes, and it saves people
34having to discover about this for themselves as more and more systems are
3564-bit. So I have changed 256 to 512. */
36
37#define HDA_SIZE 512
0756eb3c
PH
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
53typedef 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
64typedef 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
74typedef struct oracle_connection {
75 struct oracle_connection *next;
76 uschar *server;
77 struct cda_def *handle;
78 void *hda_mem;
79} oracle_connection;
80
81static 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
93Arguments:
94 oracle_handle the handle of the connection
95 rc the return code
96 msg local text message
97*/
98
99static uschar *
100oracle_error(struct cda_def *oracle_handle, int rc, uschar *msg)
101{
102uschar tmp[1024];
103oerhms(oracle_handle, rc, tmp, sizeof(tmp));
104return 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
115Arguments:
116 cda the connection
117 def
118 desc descriptions put here
119
120Returns: number of fields
121*/
122
123static sword
124describe_define(Cda_Def *cda, Ora_Define *def, Ora_Describe *desc)
125{
126sword col, deflen, deftyp;
127static ub1 *defptr;
128static sword numwidth = 8;
129
130/* Describe the select-list items. */
131
132for (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
189return col;
190}
191
192
193
194/*************************************************
195* Open entry point *
196*************************************************/
197
198/* See local README for interface description. */
199
e6d225ae 200static void *
0756eb3c
PH
201oracle_open(uschar *filename, uschar **errmsg)
202{
203return (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
e6d225ae 214static void
0756eb3c
PH
215oracle_tidy(void)
216{
217oracle_connection *cn;
218while ((cn = oracle_connections) != NULL)
219 {
220 oracle_connections = cn->next;
221 DEBUG(D_lookup) debug_printf("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
233single server.
234
235Arguments:
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
242The server string is of the form "host/dbname/user/password", for compatibility
243with MySQL and pgsql, but at present, the dbname is not used. This string is in
244a nextinlist temporary buffer, so can be overwritten.
245
246Returns: OK, FAIL, or DEFER
247*/
248
249static int
250perform_oracle_search(uschar *query, uschar *server, uschar **resultptr,
251 uschar **errmsg, BOOL *defer_break)
252{
253Cda_Def *cda = NULL;
254struct cda_def *oracle_handle = NULL;
255Ora_Describe *desc = NULL;
256Ora_Define *def = NULL;
257void *hda = NULL;
258
259int i;
260int ssize = 0;
261int offset = 0;
262int yield = DEFER;
263unsigned int num_fields = 0;
264uschar *result = NULL;
265oracle_connection *cn = NULL;
266uschar *server_copy = NULL;
267uschar *sdata[4];
268uschar tmp[1024];
269
270/* Disaggregate the parameters from the server argument. The order is host,
271database, user, password. We can write to the string, since it is in a
272nextinlist temporary buffer. The copy of the string that is used for caching
273has the password removed. This copy is also used for debugging output. */
274
275for (i = 3; i > 0; i--)
276 {
277 uschar *pp = Ustrrchr(server, '/');
278 if (pp == NULL)
279 {
280 *errmsg = string_sprintf("incomplete ORACLE server data: %s", server);
281 *defer_break = TRUE;
282 return DEFER;
283 }
284 *pp++ = 0;
285 sdata[i] = pp;
286 if (i == 3) server_copy = string_copy(server); /* sans password */
287 }
288sdata[0] = server; /* What's left at the start */
289
290/* If the database is the empty string, set it NULL - the query must then
291define it. */
292
293if (sdata[1][0] == 0) sdata[1] = NULL;
294
295/* See if we have a cached connection to the server */
296
297for (cn = oracle_connections; cn != NULL; cn = cn->next)
298 {
299 if (strcmp(cn->server, server_copy) == 0)
300 {
301 oracle_handle = cn->handle;
302 hda = cn->hda_mem;
303 break;
304 }
305 }
306
307/* If no cached connection, we must set one up */
308
309if (cn == NULL)
310 {
311 DEBUG(D_lookup) debug_printf("ORACLE new connection: host=%s database=%s "
312 "user=%s\n", sdata[0], sdata[1], sdata[2]);
313
314 /* Get store for a new connection, initialize it, and connect to the server */
315
316 oracle_handle = store_get(sizeof(struct cda_def));
317 hda = store_get(HDA_SIZE);
318 memset(hda,'\0',HDA_SIZE);
319
320 /*
321 * Perform a default (blocking) login
322 *
323 * sdata[0] = tnsname (service name - typically host name)
324 * sdata[1] = dbname - not used at present
325 * sdata[2] = username
326 * sdata[3] = passwd
327 */
328
329 if(olog(oracle_handle, hda, sdata[2], -1, sdata[3], -1, sdata[0], -1,
330 (ub4)OCI_LM_DEF) != 0)
331 {
332 *errmsg = oracle_error(oracle_handle, oracle_handle->rc,
333 US"connection failed");
334 *defer_break = FALSE;
335 goto ORACLE_EXIT_NO_VALS;
336 }
337
338 /* Add the connection to the cache */
339
340 cn = store_get(sizeof(oracle_connection));
341 cn->server = server_copy;
342 cn->handle = oracle_handle;
343 cn->next = oracle_connections;
344 cn->hda_mem = hda;
345 oracle_connections = cn;
346 }
347
348/* Else use a previously cached connection - we can write to the server string
349to obliterate the password because it is in a nextinlist temporary buffer. */
350
351else
352 {
353 DEBUG(D_lookup)
354 debug_printf("ORACLE using cached connection for %s\n", server_copy);
355 }
356
357/* We have a connection. Open a cursor and run the query */
358
359cda = store_get(sizeof(Cda_Def));
360
361if (oopen(cda, oracle_handle, (text *)0, -1, -1, (text *)0, -1) != 0)
362 {
363 *errmsg = oracle_error(oracle_handle, cda->rc, "failed to open cursor");
364 *defer_break = FALSE;
365 goto ORACLE_EXIT_NO_VALS;
366 }
367
368if (oparse(cda, (text *)query, (sb4) -1,
369 (sword)PARSE_NO_DEFER, (ub4)PARSE_V7_LNG) != 0)
370 {
371 *errmsg = oracle_error(oracle_handle, cda->rc, "query failed");
372 *defer_break = FALSE;
373 oclose(cda);
374 goto ORACLE_EXIT_NO_VALS;
375 }
376
377/* Find the number of fields returned and sort out their types. If the number
378is one, we don't add field names to the data. Otherwise we do. */
379
380def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE);
381desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE);
382
383if ((num_fields = describe_define(cda,def,desc)) == -1)
384 {
385 *errmsg = oracle_error(oracle_handle, cda->rc, "describe_define failed");
386 *defer_break = FALSE;
387 goto ORACLE_EXIT;
388 }
389
390if (oexec(cda)!=0)
391 {
392 *errmsg = oracle_error(oracle_handle, cda->rc, "oexec failed");
393 *defer_break = FALSE;
394 goto ORACLE_EXIT;
395 }
396
397/* Get the fields and construct the result string. If there is more than one
398row, we insert '\n' between them. */
399
400while (cda->rc != NO_DATA_FOUND) /* Loop for each row */
401 {
402 ofetch(cda);
403 if(cda->rc == NO_DATA_FOUND) break;
404
405 if (result != NULL) result = string_cat(result, &ssize, &offset, "\n", 1);
406
407 /* Single field - just add on the data */
408
409 if (num_fields == 1)
410 result = string_cat(result, &ssize, &offset, def[0].buf, def[0].col_retlen);
411
412 /* Multiple fields - precede by file name, removing {lead,trail}ing WS */
413
414 else for (i = 0; i < num_fields; i++)
415 {
416 int slen;
417 uschar *s = US desc[i].buf;
418
419 while (*s != 0 && isspace(*s)) s++;
420 slen = Ustrlen(s);
421 while (slen > 0 && isspace(s[slen-1])) slen--;
422 result = string_cat(result, &ssize, &offset, s, slen);
423 result = string_cat(result, &ssize, &offset, US"=", 1);
424
425 /* int and float type wont ever need escaping. Otherwise, quote the value
426 if it contains spaces or is empty. */
427
428 if (desc[i].dbtype != INT_TYPE && desc[i].dbtype != FLOAT_TYPE &&
429 (def[i].buf[0] == 0 || strchr(def[i].buf, ' ') != NULL))
430 {
431 int j;
432 result = string_cat(result, &ssize, &offset, "\"", 1);
433 for (j = 0; j < def[i].col_retlen; j++)
434 {
435 if (def[i].buf[j] == '\"' || def[i].buf[j] == '\\')
436 result = string_cat(result, &ssize, &offset, "\\", 1);
437 result = string_cat(result, &ssize, &offset, def[i].buf+j, 1);
438 }
439 result = string_cat(result, &ssize, &offset, "\"", 1);
440 }
441
442 else switch(desc[i].dbtype)
443 {
444 case INT_TYPE:
445 sprintf(CS tmp, "%d", def[i].int_buf);
446 result = string_cat(result, &ssize, &offset, tmp, Ustrlen(tmp));
447 break;
448
449 case FLOAT_TYPE:
450 sprintf(CS tmp, "%f", def[i].flt_buf);
451 result = string_cat(result, &ssize, &offset, tmp, Ustrlen(tmp));
452 break;
453
454 case STRING_TYPE:
455 result = string_cat(result, &ssize, &offset, def[i].buf,
456 def[i].col_retlen);
457 break;
458
459 default:
460 *errmsg = string_sprintf("ORACLE: unknown field type %d", desc[i].dbtype);
461 *defer_break = FALSE;
462 result = NULL;
463 goto ORACLE_EXIT;
464 }
465
466 result = string_cat(result, &ssize, &offset, " ", 1);
467 }
468 }
469
470/* If result is NULL then no data has been found and so we return FAIL.
471Otherwise, we must terminate the string which has been built; string_cat()
472always leaves enough room for a terminating zero. */
473
474if (result == NULL)
475 {
476 yield = FAIL;
477 *errmsg = "ORACLE: no data found";
478 }
479else
480 {
481 result[offset] = 0;
482 store_reset(result + offset + 1);
483 }
484
485/* Get here by goto from various error checks. */
486
487ORACLE_EXIT:
488
489/* Close the cursor; don't close the connection, as it is cached. */
490
491oclose(cda);
492
493ORACLE_EXIT_NO_VALS:
494
495/* Non-NULL result indicates a sucessful result */
496
497if (result != NULL)
498 {
499 *resultptr = result;
500 return OK;
501 }
502else
503 {
504 DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
505 return yield; /* FAIL or DEFER */
506 }
507}
508
509
510
511
512/*************************************************
513* Find entry point *
514*************************************************/
515
516/* See local README for interface description. The handle and filename
517arguments are not used. Loop through a list of servers while the query is
518deferred with a retryable error. */
519
e6d225ae 520static int
0756eb3c
PH
521oracle_find(void *handle, uschar *filename, uschar *query, int length,
522 uschar **result, uschar **errmsg, BOOL *do_cache)
523{
524int sep = 0;
525uschar *server;
526uschar *list = oracle_servers;
527uschar buffer[512];
528
529do_cache = do_cache; /* Placate picky compilers */
530
531DEBUG(D_lookup) debug_printf("ORACLE query: %s\n", query);
532
533while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
534 {
535 BOOL defer_break;
536 int rc = perform_oracle_search(query, server, result, errmsg, &defer_break);
537 if (rc != DEFER || defer_break) return rc;
538 }
539
540if (oracle_servers == NULL)
541 *errmsg = "no ORACLE servers defined (oracle_servers option)";
542
543return DEFER;
544}
545
546
547
548/*************************************************
549* Quote entry point *
550*************************************************/
551
552/* The only characters that need to be quoted (with backslash) are newline,
553tab, carriage return, backspace, backslash itself, and the quote characters.
554Percent and underscore are not escaped. They are only special in contexts where
555they can be wild cards, and this isn't usually the case for data inserted from
556messages, since that isn't likely to be treated as a pattern of any kind.
557
558Arguments:
559 s the string to be quoted
560 opt additional option text or NULL if none
561
562Returns: the processed string or NULL for a bad option
563*/
564
e6d225ae 565static uschar *
0756eb3c
PH
566oracle_quote(uschar *s, uschar *opt)
567{
568register int c;
569int count = 0;
570uschar *t = s;
571uschar *quoted;
572
573if (opt != NULL) return NULL; /* No options are recognized */
574
575while ((c = *t++) != 0)
576 if (strchr("\n\t\r\b\'\"\\", c) != NULL) count++;
577
578if (count == 0) return s;
579t = quoted = store_get((int)strlen(s) + count + 1);
580
581while ((c = *s++) != 0)
582 {
583 if (strchr("\n\t\r\b\'\"\\", c) != NULL)
584 {
585 *t++ = '\\';
586 switch(c)
587 {
588 case '\n': *t++ = 'n';
589 break;
590 case '\t': *t++ = 't';
591 break;
592 case '\r': *t++ = 'r';
593 break;
594 case '\b': *t++ = 'b';
595 break;
596 default: *t++ = c;
597 break;
598 }
599 }
600 else *t++ = c;
601 }
602
603*t = 0;
604return quoted;
605}
606
e6d225ae
DW
607static lookup_info _lookup_info = {
608 US"oracle", /* lookup name */
609 lookup_querystyle, /* query-style lookup */
610 oracle_open, /* open function */
611 NULL, /* check function */
612 oracle_find, /* find function */
613 NULL, /* no close function */
614 oracle_tidy, /* tidy function */
615 oracle_quote /* quoting function */
616};
617
618#ifdef DYNLOOKUP
619#define oracle_lookup_module_info _lookup_module_info
620#endif
621
622static lookup_info *_lookup_list[] = { &_lookup_info };
623lookup_module_info oracle_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
0756eb3c
PH
624
625/* End of lookups/oracle.c */