Bugzilla 1217: Experimental Redis lookup
[exim.git] / src / src / drtables.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2012 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8
9 #include "exim.h"
10
11 #include <string.h>
12
13 /* This module contains tables that define the lookup methods and drivers
14 that are actually included in the binary. Its contents are controlled by
15 various macros in config.h that ultimately come from Local/Makefile. They are
16 all described in src/EDITME. */
17
18
19 lookup_info **lookup_list;
20 int lookup_list_count = 0;
21
22 static int lookup_list_init_done = 0;
23
24 /* Table of information about all possible authentication mechamisms. All
25 entries are always present if any mechanism is declared, but the functions are
26 set to NULL for those that are not compiled into the binary. */
27
28 #ifdef AUTH_CRAM_MD5
29 #include "auths/cram_md5.h"
30 #endif
31
32 #ifdef AUTH_CYRUS_SASL
33 #include "auths/cyrus_sasl.h"
34 #endif
35
36 #ifdef AUTH_DOVECOT
37 #include "auths/dovecot.h"
38 #endif
39
40 #ifdef AUTH_GSASL
41 #include "auths/gsasl_exim.h"
42 #endif
43
44 #ifdef AUTH_HEIMDAL_GSSAPI
45 #include "auths/heimdal_gssapi.h"
46 #endif
47
48 #ifdef AUTH_PLAINTEXT
49 #include "auths/plaintext.h"
50 #endif
51
52 #ifdef AUTH_SPA
53 #include "auths/spa.h"
54 #endif
55
56 auth_info auths_available[] = {
57
58 /* Checking by an expansion condition on plain text */
59
60 #ifdef AUTH_CRAM_MD5
61   {
62   US"cram_md5",                              /* lookup name */
63   auth_cram_md5_options,
64   &auth_cram_md5_options_count,
65   &auth_cram_md5_option_defaults,
66   sizeof(auth_cram_md5_options_block),
67   auth_cram_md5_init,                        /* init function */
68   auth_cram_md5_server,                      /* server function */
69   auth_cram_md5_client,                      /* client function */
70   NULL                                       /* diagnostic function */
71   },
72 #endif
73
74 #ifdef AUTH_CYRUS_SASL
75   {
76   US"cyrus_sasl",           /* lookup name */
77   auth_cyrus_sasl_options,
78   &auth_cyrus_sasl_options_count,
79   &auth_cyrus_sasl_option_defaults,
80   sizeof(auth_cyrus_sasl_options_block),
81   auth_cyrus_sasl_init,                      /* init function */
82   auth_cyrus_sasl_server,                    /* server function */
83   NULL,                                      /* client function */
84   auth_cyrus_sasl_version_report             /* diagnostic function */
85   },
86 #endif
87
88 #ifdef AUTH_DOVECOT
89   {
90   US"dovecot",                                /* lookup name */
91   auth_dovecot_options,
92   &auth_dovecot_options_count,
93   &auth_dovecot_option_defaults,
94   sizeof(auth_dovecot_options_block),
95   auth_dovecot_init,                          /* init function */
96   auth_dovecot_server,                        /* server function */
97   NULL,                                       /* client function */
98   NULL                                        /* diagnostic function */
99   },
100 #endif
101
102 #ifdef AUTH_GSASL
103   {
104   US"gsasl",                                  /* lookup name */
105   auth_gsasl_options,
106   &auth_gsasl_options_count,
107   &auth_gsasl_option_defaults,
108   sizeof(auth_gsasl_options_block),
109   auth_gsasl_init,                            /* init function */
110   auth_gsasl_server,                          /* server function */
111   NULL,                                       /* client function */
112   auth_gsasl_version_report                   /* diagnostic function */
113   },
114 #endif
115
116 #ifdef AUTH_HEIMDAL_GSSAPI
117   {
118   US"heimdal_gssapi",                         /* lookup name */
119   auth_heimdal_gssapi_options,
120   &auth_heimdal_gssapi_options_count,
121   &auth_heimdal_gssapi_option_defaults,
122   sizeof(auth_heimdal_gssapi_options_block),
123   auth_heimdal_gssapi_init,                   /* init function */
124   auth_heimdal_gssapi_server,                 /* server function */
125   NULL,                                       /* client function */
126   auth_heimdal_gssapi_version_report          /* diagnostic function */
127   },
128 #endif
129
130 #ifdef AUTH_PLAINTEXT
131   {
132   US"plaintext",                             /* lookup name */
133   auth_plaintext_options,
134   &auth_plaintext_options_count,
135   &auth_plaintext_option_defaults,
136   sizeof(auth_plaintext_options_block),
137   auth_plaintext_init,                       /* init function */
138   auth_plaintext_server,                     /* server function */
139   auth_plaintext_client,                     /* client function */
140   NULL                                       /* diagnostic function */
141   },
142 #endif
143
144 #ifdef AUTH_SPA
145   {
146   US"spa",                                   /* lookup name */
147   auth_spa_options,
148   &auth_spa_options_count,
149   &auth_spa_option_defaults,
150   sizeof(auth_spa_options_block),
151   auth_spa_init,                             /* init function */
152   auth_spa_server,                           /* server function */
153   auth_spa_client,                           /* client function */
154   NULL                                       /* diagnostic function */
155   },
156 #endif
157
158 { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL  }
159 };
160
161
162 /* Tables of information about which routers and transports are included in the
163 exim binary. */
164
165 /* Pull in the necessary header files */
166
167 #include "routers/rf_functions.h"
168
169 #ifdef ROUTER_ACCEPT
170 #include "routers/accept.h"
171 #endif
172
173 #ifdef ROUTER_DNSLOOKUP
174 #include "routers/dnslookup.h"
175 #endif
176
177 #ifdef ROUTER_MANUALROUTE
178 #include "routers/manualroute.h"
179 #endif
180
181 #ifdef ROUTER_IPLITERAL
182 #include "routers/ipliteral.h"
183 #endif
184
185 #ifdef ROUTER_IPLOOKUP
186 #include "routers/iplookup.h"
187 #endif
188
189 #ifdef ROUTER_QUERYPROGRAM
190 #include "routers/queryprogram.h"
191 #endif
192
193 #ifdef ROUTER_REDIRECT
194 #include "routers/redirect.h"
195 #endif
196
197 #ifdef TRANSPORT_APPENDFILE
198 #include "transports/appendfile.h"
199 #endif
200
201 #ifdef TRANSPORT_AUTOREPLY
202 #include "transports/autoreply.h"
203 #endif
204
205 #ifdef TRANSPORT_LMTP
206 #include "transports/lmtp.h"
207 #endif
208
209 #ifdef TRANSPORT_PIPE
210 #include "transports/pipe.h"
211 #endif
212
213 #ifdef TRANSPORT_SMTP
214 #include "transports/smtp.h"
215 #endif
216
217
218 /* Now set up the structures, terminated by an entry with a null name. */
219
220 router_info routers_available[] = {
221 #ifdef ROUTER_ACCEPT
222   {
223   US"accept",
224   accept_router_options,
225   &accept_router_options_count,
226   &accept_router_option_defaults,
227   sizeof(accept_router_options_block),
228   accept_router_init,
229   accept_router_entry,
230   NULL,     /* no tidyup entry */
231   ri_yestransport
232   },
233 #endif
234 #ifdef ROUTER_DNSLOOKUP
235   {
236   US"dnslookup",
237   dnslookup_router_options,
238   &dnslookup_router_options_count,
239   &dnslookup_router_option_defaults,
240   sizeof(dnslookup_router_options_block),
241   dnslookup_router_init,
242   dnslookup_router_entry,
243   NULL,     /* no tidyup entry */
244   ri_yestransport
245   },
246 #endif
247 #ifdef ROUTER_IPLITERAL
248   {
249   US"ipliteral",
250   ipliteral_router_options,
251   &ipliteral_router_options_count,
252   &ipliteral_router_option_defaults,
253   sizeof(ipliteral_router_options_block),
254   ipliteral_router_init,
255   ipliteral_router_entry,
256   NULL,     /* no tidyup entry */
257   ri_yestransport
258   },
259 #endif
260 #ifdef ROUTER_IPLOOKUP
261   {
262   US"iplookup",
263   iplookup_router_options,
264   &iplookup_router_options_count,
265   &iplookup_router_option_defaults,
266   sizeof(iplookup_router_options_block),
267   iplookup_router_init,
268   iplookup_router_entry,
269   NULL,     /* no tidyup entry */
270   ri_notransport
271   },
272 #endif
273 #ifdef ROUTER_MANUALROUTE
274   {
275   US"manualroute",
276   manualroute_router_options,
277   &manualroute_router_options_count,
278   &manualroute_router_option_defaults,
279   sizeof(manualroute_router_options_block),
280   manualroute_router_init,
281   manualroute_router_entry,
282   NULL,     /* no tidyup entry */
283   0
284   },
285 #endif
286 #ifdef ROUTER_QUERYPROGRAM
287   {
288   US"queryprogram",
289   queryprogram_router_options,
290   &queryprogram_router_options_count,
291   &queryprogram_router_option_defaults,
292   sizeof(queryprogram_router_options_block),
293   queryprogram_router_init,
294   queryprogram_router_entry,
295   NULL,     /* no tidyup entry */
296   0
297   },
298 #endif
299 #ifdef ROUTER_REDIRECT
300   {
301   US"redirect",
302   redirect_router_options,
303   &redirect_router_options_count,
304   &redirect_router_option_defaults,
305   sizeof(redirect_router_options_block),
306   redirect_router_init,
307   redirect_router_entry,
308   NULL,     /* no tidyup entry */
309   ri_notransport
310   },
311 #endif
312 { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, 0 }
313 };
314
315
316
317 transport_info transports_available[] = {
318 #ifdef TRANSPORT_APPENDFILE
319   {
320   US"appendfile",                              /* driver name */
321   appendfile_transport_options,                /* local options table */
322   &appendfile_transport_options_count,         /* number of entries */
323   &appendfile_transport_option_defaults,       /* private options defaults */
324   sizeof(appendfile_transport_options_block),  /* size of private block */
325   appendfile_transport_init,                   /* init entry point */
326   appendfile_transport_entry,                  /* main entry point */
327   NULL,                                        /* no tidyup entry */
328   NULL,                                        /* no closedown entry */
329   TRUE,                                        /* local flag */
330   },
331 #endif
332 #ifdef TRANSPORT_AUTOREPLY
333   {
334   US"autoreply",                               /* driver name */
335   autoreply_transport_options,                 /* local options table */
336   &autoreply_transport_options_count,          /* number of entries */
337   &autoreply_transport_option_defaults,        /* private options defaults */
338   sizeof(autoreply_transport_options_block),   /* size of private block */
339   autoreply_transport_init,                    /* init entry point */
340   autoreply_transport_entry,                   /* main entry point */
341   NULL,                                        /* no tidyup entry */
342   NULL,                                        /* no closedown entry */
343   TRUE                                         /* local flag */
344   },
345 #endif
346 #ifdef TRANSPORT_LMTP
347   {
348   US"lmtp",                                    /* driver name */
349   lmtp_transport_options,                      /* local options table */
350   &lmtp_transport_options_count,               /* number of entries */
351   &lmtp_transport_option_defaults,             /* private options defaults */
352   sizeof(lmtp_transport_options_block),        /* size of private block */
353   lmtp_transport_init,                         /* init entry point */
354   lmtp_transport_entry,                        /* main entry point */
355   NULL,                                        /* no tidyup entry */
356   NULL,                                        /* no closedown entry */
357   TRUE                                         /* local flag */
358   },
359 #endif
360 #ifdef TRANSPORT_PIPE
361   {
362   US"pipe",                                    /* driver name */
363   pipe_transport_options,                      /* local options table */
364   &pipe_transport_options_count,               /* number of entries */
365   &pipe_transport_option_defaults,             /* private options defaults */
366   sizeof(pipe_transport_options_block),        /* size of private block */
367   pipe_transport_init,                         /* init entry point */
368   pipe_transport_entry,                        /* main entry point */
369   NULL,                                        /* no tidyup entry */
370   NULL,                                        /* no closedown entry */
371   TRUE                                         /* local flag */
372   },
373 #endif
374 #ifdef TRANSPORT_SMTP
375   {
376   US"smtp",                                    /* driver name */
377   smtp_transport_options,                      /* local options table */
378   &smtp_transport_options_count,               /* number of entries */
379   &smtp_transport_option_defaults,             /* private options defaults */
380   sizeof(smtp_transport_options_block),        /* size of private block */
381   smtp_transport_init,                         /* init entry point */
382   smtp_transport_entry,                        /* main entry point */
383   NULL,                                        /* no tidyup entry */
384   smtp_transport_closedown,                    /* close down passed channel */
385   FALSE                                        /* local flag */
386   },
387 #endif
388 { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, FALSE }
389 };
390
391 struct lookupmodulestr
392 {
393   void *dl;
394   struct lookup_module_info *info;
395   struct lookupmodulestr *next;
396 };
397
398 static struct lookupmodulestr *lookupmodules = NULL;
399
400 static void addlookupmodule(void *dl, struct lookup_module_info *info)
401 {
402   struct lookupmodulestr *p = store_malloc(sizeof(struct lookupmodulestr));
403   p->dl = dl;
404   p->info = info;
405   p->next = lookupmodules;
406   lookupmodules = p;
407   lookup_list_count += info->lookupcount;
408 }
409
410 /* only valid after lookup_list and lookup_list_count are assigned */
411 static void add_lookup_to_list(lookup_info *info)
412 {
413   /* need to add the lookup to lookup_list, sorted */
414   int pos = 0;
415
416   /* strategy is to go through the list until we find
417    * either an empty spot or a name that is higher.
418    * this can't fail because we have enough space. */
419   while (lookup_list[pos]
420       && (Ustrcmp(lookup_list[pos]->name, info->name) <= 0)) {
421     pos++;
422   }
423   if (lookup_list[pos]) {
424     /* need to insert it, so move all the other items up
425      * (last slot is still empty, of course) */
426     memmove(&lookup_list[pos+1],
427             &lookup_list[pos],
428             sizeof(lookup_info **) * (lookup_list_count-pos-1));
429   }
430   lookup_list[pos] = info;
431 }
432
433
434 /* These need to be at file level for old versions of gcc (2.95.2 reported),
435  * which give parse errors on an extern in function scope.  Each entry needs
436  * to also be invoked in init_lookup_list() below  */
437
438 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
439 extern lookup_module_info whoson_lookup_module_info;
440 #endif
441 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
442 extern lookup_module_info testdb_lookup_module_info;
443 #endif
444 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
445 extern lookup_module_info sqlite_lookup_module_info;
446 #endif
447 #ifdef EXPERIMENTAL_SPF
448 extern lookup_module_info spf_lookup_module_info;
449 #endif
450 #ifdef EXPERIMENTAL_REDIS
451 extern lookup_module_info redis_lookup_module_info;
452 #endif
453 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
454 extern lookup_module_info pgsql_lookup_module_info;
455 #endif
456 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
457 extern lookup_module_info passwd_lookup_module_info;
458 #endif
459 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
460 extern lookup_module_info oracle_lookup_module_info;
461 #endif
462 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
463 extern lookup_module_info nisplus_lookup_module_info;
464 #endif
465 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
466 extern lookup_module_info nis_lookup_module_info;
467 #endif
468 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
469 extern lookup_module_info mysql_lookup_module_info;
470 #endif
471 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
472 extern lookup_module_info lsearch_lookup_module_info;
473 #endif
474 #ifdef LOOKUP_LDAP
475 extern lookup_module_info ldap_lookup_module_info;
476 #endif
477 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
478 extern lookup_module_info ibase_lookup_module_info;
479 #endif
480 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
481 extern lookup_module_info dsearch_lookup_module_info;
482 #endif
483 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
484 extern lookup_module_info dnsdb_lookup_module_info;
485 #endif
486 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
487 extern lookup_module_info dbmdb_lookup_module_info;
488 #endif
489 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
490 extern lookup_module_info cdb_lookup_module_info;
491 #endif
492
493 void init_lookup_list(void)
494 {
495 #ifdef LOOKUP_MODULE_DIR
496   DIR *dd;
497   struct dirent *ent;
498   int countmodules = 0;
499   int moduleerrors = 0;
500 #endif
501   struct lookupmodulestr *p;
502   const pcre *regex_islookupmod = regex_must_compile(
503       US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
504
505   if (lookup_list_init_done)
506     return;
507   lookup_list_init_done = 1;
508
509 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
510   addlookupmodule(NULL, &cdb_lookup_module_info);
511 #endif
512
513 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
514   addlookupmodule(NULL, &dbmdb_lookup_module_info);
515 #endif
516
517 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
518   addlookupmodule(NULL, &dnsdb_lookup_module_info);
519 #endif
520
521 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
522   addlookupmodule(NULL, &dsearch_lookup_module_info);
523 #endif
524
525 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
526   addlookupmodule(NULL, &ibase_lookup_module_info);
527 #endif
528
529 #ifdef LOOKUP_LDAP
530   addlookupmodule(NULL, &ldap_lookup_module_info);
531 #endif
532
533 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
534   addlookupmodule(NULL, &lsearch_lookup_module_info);
535 #endif
536
537 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
538   addlookupmodule(NULL, &mysql_lookup_module_info);
539 #endif
540
541 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
542   addlookupmodule(NULL, &nis_lookup_module_info);
543 #endif
544
545 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
546   addlookupmodule(NULL, &nisplus_lookup_module_info);
547 #endif
548
549 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
550   addlookupmodule(NULL, &oracle_lookup_module_info);
551 #endif
552
553 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
554   addlookupmodule(NULL, &passwd_lookup_module_info);
555 #endif
556
557 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
558   addlookupmodule(NULL, &pgsql_lookup_module_info);
559 #endif
560
561 #ifdef EXPERIMENTAL_REDIS
562   addlookupmodule(NULL, &redis_lookup_module_info);
563 #endif
564
565 #ifdef EXPERIMENTAL_SPF
566   addlookupmodule(NULL, &spf_lookup_module_info);
567 #endif
568
569 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
570   addlookupmodule(NULL, &sqlite_lookup_module_info);
571 #endif
572
573 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
574   addlookupmodule(NULL, &testdb_lookup_module_info);
575 #endif
576
577 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
578   addlookupmodule(NULL, &whoson_lookup_module_info);
579 #endif
580
581 #ifdef LOOKUP_MODULE_DIR
582   dd = opendir(LOOKUP_MODULE_DIR);
583   if (dd == NULL) {
584     DEBUG(D_lookup) debug_printf("Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
585     log_write(0, LOG_MAIN, "Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
586   }
587   else {
588     DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
589     while ((ent = readdir(dd)) != NULL) {
590       char *name = ent->d_name;
591       int len = (int)strlen(name);
592       if (pcre_exec(regex_islookupmod, NULL, name, len, 0, PCRE_EOPT, NULL, 0) >= 0) {
593         int pathnamelen = len + (int)strlen(LOOKUP_MODULE_DIR) + 2;
594         void *dl;
595         struct lookup_module_info *info;
596         const char *errormsg;
597
598         /* SRH: am I being paranoid here or what? */
599         if (pathnamelen > big_buffer_size) {
600           fprintf(stderr, "Loading lookup modules: %s/%s: name too long\n", LOOKUP_MODULE_DIR, name);
601           log_write(0, LOG_MAIN|LOG_PANIC, "%s/%s: name too long\n", LOOKUP_MODULE_DIR, name);
602           continue;
603         }
604
605         /* SRH: snprintf here? */
606         sprintf(CS big_buffer, "%s/%s", LOOKUP_MODULE_DIR, name);
607
608         dl = dlopen(CS big_buffer, RTLD_NOW);// TJ was LAZY
609         if (dl == NULL) {
610           fprintf(stderr, "Error loading %s: %s\n", name, dlerror());
611           moduleerrors++;
612           log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, dlerror());
613           continue;
614         }
615
616         /* FreeBSD nsdispatch() can trigger dlerror() errors about
617          * _nss_cache_cycle_prevention_function; we need to clear the dlerror()
618          * state before calling dlsym(), so that any error afterwards only
619          * comes from dlsym().
620          */
621         errormsg = dlerror();
622
623         info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info");
624         if ((errormsg = dlerror()) != NULL) {
625           fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
626           dlclose(dl);
627           moduleerrors++;
628           log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
629           continue;
630         }
631         if (info->magic != LOOKUP_MODULE_INFO_MAGIC) {
632           fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name);
633           dlclose(dl);
634           moduleerrors++;
635           log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name);
636           continue;
637         }
638
639         addlookupmodule(dl, info);
640         DEBUG(D_lookup) debug_printf("Loaded \"%s\" (%d lookup types)\n", name, info->lookupcount);
641         countmodules++;
642       }
643     }
644     closedir(dd);
645   }
646
647   DEBUG(D_lookup) debug_printf("Loaded %d lookup modules\n", countmodules);
648 #endif
649
650   store_free((void*)regex_islookupmod);
651
652   DEBUG(D_lookup) debug_printf("Total %d lookups\n", lookup_list_count);
653
654   lookup_list = store_malloc(sizeof(lookup_info *) * lookup_list_count);
655   memset(lookup_list, 0, sizeof(lookup_info *) * lookup_list_count);
656
657   /* now add all lookups to the real list */
658   p = lookupmodules;
659   while (p) {
660     int j;
661     struct lookupmodulestr *pnext;
662
663     for (j = 0; j < p->info->lookupcount; j++)
664       add_lookup_to_list(p->info->lookups[j]);
665
666     pnext = p->next;
667     store_free(p);
668     p = pnext;
669   }
670   /* just to be sure */
671   lookupmodules = NULL;
672 }
673
674 /* End of drtables.c */