1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
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. */
14 /* This module contains tables that define the lookup methods and drivers
15 that are actually included in the binary. Its contents are controlled by
16 various macros in config.h that ultimately come from Local/Makefile. They are
17 all described in src/EDITME. */
20 lookup_info **lookup_list;
21 int lookup_list_count = 0;
23 /* Table of information about all possible authentication mechanisms. All
24 entries are always present if any mechanism is declared, but the functions are
25 set to NULL for those that are not compiled into the binary. */
28 #include "auths/cram_md5.h"
31 #ifdef AUTH_CYRUS_SASL
32 #include "auths/cyrus_sasl.h"
36 #include "auths/dovecot.h"
40 #include "auths/external.h"
44 #include "auths/gsasl_exim.h"
47 #ifdef AUTH_HEIMDAL_GSSAPI
48 #include "auths/heimdal_gssapi.h"
52 #include "auths/plaintext.h"
56 #include "auths/spa.h"
60 #include "auths/tls.h"
63 auth_info auths_available[] = {
65 /* Checking by an expansion condition on plain text */
69 .driver_name = US"cram_md5", /* lookup name */
70 .options = auth_cram_md5_options,
71 .options_count = &auth_cram_md5_options_count,
72 .options_block = &auth_cram_md5_option_defaults,
73 .options_len = sizeof(auth_cram_md5_options_block),
74 .init = auth_cram_md5_init,
75 .servercode = auth_cram_md5_server,
76 .clientcode = auth_cram_md5_client,
77 .version_report = NULL,
78 .macros_create = NULL,
82 #ifdef AUTH_CYRUS_SASL
84 .driver_name = US"cyrus_sasl",
85 .options = auth_cyrus_sasl_options,
86 .options_count = &auth_cyrus_sasl_options_count,
87 .options_block = &auth_cyrus_sasl_option_defaults,
88 .options_len = sizeof(auth_cyrus_sasl_options_block),
89 .init = auth_cyrus_sasl_init,
90 .servercode = auth_cyrus_sasl_server,
92 .version_report = auth_cyrus_sasl_version_report,
93 .macros_create = NULL,
99 .driver_name = US"dovecot",
100 .options = auth_dovecot_options,
101 .options_count = &auth_dovecot_options_count,
102 .options_block = &auth_dovecot_option_defaults,
103 .options_len = sizeof(auth_dovecot_options_block),
104 .init = auth_dovecot_init,
105 .servercode = auth_dovecot_server,
107 .version_report = NULL,
108 .macros_create = NULL,
114 .driver_name = US"external",
115 .options = auth_external_options,
116 .options_count = &auth_external_options_count,
117 .options_block = &auth_external_option_defaults,
118 .options_len = sizeof(auth_external_options_block),
119 .init = auth_external_init,
120 .servercode = auth_external_server,
121 .clientcode = auth_external_client,
122 .version_report = NULL,
123 .macros_create = NULL,
129 .driver_name = US"gsasl",
130 .options = auth_gsasl_options,
131 .options_count = &auth_gsasl_options_count,
132 .options_block = &auth_gsasl_option_defaults,
133 .options_len = sizeof(auth_gsasl_options_block),
134 .init = auth_gsasl_init,
135 .servercode = auth_gsasl_server,
136 .clientcode = auth_gsasl_client,
137 .version_report = auth_gsasl_version_report,
138 .macros_create = auth_gsasl_macros,
142 #ifdef AUTH_HEIMDAL_GSSAPI
144 .driver_name = US"heimdal_gssapi",
145 .options = auth_heimdal_gssapi_options,
146 .options_count = &auth_heimdal_gssapi_options_count,
147 .options_block = &auth_heimdal_gssapi_option_defaults,
148 .options_len = sizeof(auth_heimdal_gssapi_options_block),
149 .init = auth_heimdal_gssapi_init,
150 .servercode = auth_heimdal_gssapi_server,
152 .version_report = auth_heimdal_gssapi_version_report,
153 .macros_create = NULL,
157 #ifdef AUTH_PLAINTEXT
159 .driver_name = US"plaintext",
160 .options = auth_plaintext_options,
161 .options_count = &auth_plaintext_options_count,
162 .options_block = &auth_plaintext_option_defaults,
163 .options_len = sizeof(auth_plaintext_options_block),
164 .init = auth_plaintext_init,
165 .servercode = auth_plaintext_server,
166 .clientcode = auth_plaintext_client,
167 .version_report = NULL,
168 .macros_create = NULL,
174 .driver_name = US"spa",
175 .options = auth_spa_options,
176 .options_count = &auth_spa_options_count,
177 .options_block = &auth_spa_option_defaults,
178 .options_len = sizeof(auth_spa_options_block),
179 .init = auth_spa_init,
180 .servercode = auth_spa_server,
181 .clientcode = auth_spa_client,
182 .version_report = NULL,
183 .macros_create = NULL,
189 .driver_name = US"tls",
190 .options = auth_tls_options,
191 .options_count = &auth_tls_options_count,
192 .options_block = &auth_tls_option_defaults,
193 .options_len = sizeof(auth_tls_options_block),
194 .init = auth_tls_init,
195 .servercode = auth_tls_server,
197 .version_report = NULL,
198 .macros_create = NULL,
202 { .driver_name = US"" } /* end marker */
206 /* Tables of information about which routers and transports are included in the
209 /* Pull in the necessary header files */
211 #include "routers/rf_functions.h"
214 #include "routers/accept.h"
217 #ifdef ROUTER_DNSLOOKUP
218 #include "routers/dnslookup.h"
221 #ifdef ROUTER_MANUALROUTE
222 #include "routers/manualroute.h"
225 #ifdef ROUTER_IPLITERAL
226 #include "routers/ipliteral.h"
229 #ifdef ROUTER_IPLOOKUP
230 #include "routers/iplookup.h"
233 #ifdef ROUTER_QUERYPROGRAM
234 #include "routers/queryprogram.h"
237 #ifdef ROUTER_REDIRECT
238 #include "routers/redirect.h"
241 #ifdef TRANSPORT_APPENDFILE
242 #include "transports/appendfile.h"
245 #ifdef TRANSPORT_AUTOREPLY
246 #include "transports/autoreply.h"
249 #ifdef TRANSPORT_LMTP
250 #include "transports/lmtp.h"
253 #ifdef TRANSPORT_PIPE
254 #include "transports/pipe.h"
257 #ifdef EXPERIMENTAL_QUEUEFILE
258 #include "transports/queuefile.h"
261 #ifdef TRANSPORT_SMTP
262 #include "transports/smtp.h"
266 /* Now set up the structures, terminated by an entry with a null name. */
268 router_info routers_available[] = {
271 .driver_name = US"accept",
272 .options = accept_router_options,
273 .options_count = &accept_router_options_count,
274 .options_block = &accept_router_option_defaults,
275 .options_len = sizeof(accept_router_options_block),
276 .init = accept_router_init,
277 .code = accept_router_entry,
278 .tidyup = NULL, /* no tidyup entry */
279 .ri_flags = ri_yestransport
282 #ifdef ROUTER_DNSLOOKUP
284 .driver_name = US"dnslookup",
285 .options = dnslookup_router_options,
286 .options_count = &dnslookup_router_options_count,
287 .options_block = &dnslookup_router_option_defaults,
288 .options_len = sizeof(dnslookup_router_options_block),
289 .init = dnslookup_router_init,
290 .code = dnslookup_router_entry,
291 .tidyup = NULL, /* no tidyup entry */
292 .ri_flags = ri_yestransport
295 #ifdef ROUTER_IPLITERAL
297 .driver_name = US"ipliteral",
298 .options = ipliteral_router_options,
299 .options_count = &ipliteral_router_options_count,
300 .options_block = &ipliteral_router_option_defaults,
301 .options_len = sizeof(ipliteral_router_options_block),
302 .init = ipliteral_router_init,
303 .code = ipliteral_router_entry,
304 .tidyup = NULL, /* no tidyup entry */
305 .ri_flags = ri_yestransport
308 #ifdef ROUTER_IPLOOKUP
310 .driver_name = US"iplookup",
311 .options = iplookup_router_options,
312 .options_count = &iplookup_router_options_count,
313 .options_block = &iplookup_router_option_defaults,
314 .options_len = sizeof(iplookup_router_options_block),
315 .init = iplookup_router_init,
316 .code = iplookup_router_entry,
317 .tidyup = NULL, /* no tidyup entry */
318 .ri_flags = ri_notransport
321 #ifdef ROUTER_MANUALROUTE
323 .driver_name = US"manualroute",
324 .options = manualroute_router_options,
325 .options_count = &manualroute_router_options_count,
326 .options_block = &manualroute_router_option_defaults,
327 .options_len = sizeof(manualroute_router_options_block),
328 .init = manualroute_router_init,
329 .code = manualroute_router_entry,
330 .tidyup = NULL, /* no tidyup entry */
334 #ifdef ROUTER_QUERYPROGRAM
336 .driver_name = US"queryprogram",
337 .options = queryprogram_router_options,
338 .options_count = &queryprogram_router_options_count,
339 .options_block = &queryprogram_router_option_defaults,
340 .options_len = sizeof(queryprogram_router_options_block),
341 .init = queryprogram_router_init,
342 .code = queryprogram_router_entry,
343 .tidyup = NULL, /* no tidyup entry */
347 #ifdef ROUTER_REDIRECT
349 .driver_name = US"redirect",
350 .options = redirect_router_options,
351 .options_count = &redirect_router_options_count,
352 .options_block = &redirect_router_option_defaults,
353 .options_len = sizeof(redirect_router_options_block),
354 .init = redirect_router_init,
355 .code = redirect_router_entry,
356 .tidyup = NULL, /* no tidyup entry */
357 .ri_flags = ri_notransport
365 transport_info transports_available[] = {
366 #ifdef TRANSPORT_APPENDFILE
368 .driver_name = US"appendfile",
369 .options = appendfile_transport_options,
370 .options_count = &appendfile_transport_options_count,
371 .options_block = &appendfile_transport_option_defaults, /* private options defaults */
372 .options_len = sizeof(appendfile_transport_options_block),
373 .init = appendfile_transport_init,
374 .code = appendfile_transport_entry,
380 #ifdef TRANSPORT_AUTOREPLY
382 .driver_name = US"autoreply",
383 .options = autoreply_transport_options,
384 .options_count = &autoreply_transport_options_count,
385 .options_block = &autoreply_transport_option_defaults,
386 .options_len = sizeof(autoreply_transport_options_block),
387 .init = autoreply_transport_init,
388 .code = autoreply_transport_entry,
394 #ifdef TRANSPORT_LMTP
396 .driver_name = US"lmtp",
397 .options = lmtp_transport_options,
398 .options_count = &lmtp_transport_options_count,
399 .options_block = &lmtp_transport_option_defaults,
400 .options_len = sizeof(lmtp_transport_options_block),
401 .init = lmtp_transport_init,
402 .code = lmtp_transport_entry,
408 #ifdef TRANSPORT_PIPE
410 .driver_name = US"pipe",
411 .options = pipe_transport_options,
412 .options_count = &pipe_transport_options_count,
413 .options_block = &pipe_transport_option_defaults,
414 .options_len = sizeof(pipe_transport_options_block),
415 .init = pipe_transport_init,
416 .code = pipe_transport_entry,
422 #ifdef EXPERIMENTAL_QUEUEFILE
424 .driver_name = US"queuefile",
425 .options = queuefile_transport_options,
426 .options_count = &queuefile_transport_options_count,
427 .options_block = &queuefile_transport_option_defaults,
428 .options_len = sizeof(queuefile_transport_options_block),
429 .init = queuefile_transport_init,
430 .code = queuefile_transport_entry,
436 #ifdef TRANSPORT_SMTP
438 .driver_name = US"smtp",
439 .options = smtp_transport_options,
440 .options_count = &smtp_transport_options_count,
441 .options_block = &smtp_transport_option_defaults,
442 .options_len = sizeof(smtp_transport_options_block),
443 .init = smtp_transport_init,
444 .code = smtp_transport_entry,
446 .closedown = smtp_transport_closedown,
456 auth_show_supported(gstring * g)
458 g = string_cat(g, US"Authenticators:");
459 for (auth_info * ai = auths_available; ai->driver_name[0]; ai++)
460 g = string_fmt_append(g, " %s", ai->driver_name);
461 return string_cat(g, US"\n");
465 route_show_supported(gstring * g)
467 g = string_cat(g, US"Routers:");
468 for (router_info * rr = routers_available; rr->driver_name[0]; rr++)
469 g = string_fmt_append(g, " %s", rr->driver_name);
470 return string_cat(g, US"\n");
474 transport_show_supported(gstring * g)
476 g = string_cat(g, US"Transports:");
477 #ifdef TRANSPORT_APPENDFILE
478 g = string_cat(g, US" appendfile");
479 #ifdef SUPPORT_MAILDIR
480 g = string_cat(g, US"/maildir"); /* damn these subclasses */
482 #ifdef SUPPORT_MAILSTORE
483 g = string_cat(g, US"/mailstore");
486 g = string_cat(g, US"/mbx");
489 #ifdef TRANSPORT_AUTOREPLY
490 g = string_cat(g, US" autoreply");
492 #ifdef TRANSPORT_LMTP
493 g = string_cat(g, US" lmtp");
495 #ifdef TRANSPORT_PIPE
496 g = string_cat(g, US" pipe");
498 #ifdef EXPERIMENTAL_QUEUEFILE
499 g = string_cat(g, US" queuefile");
501 #ifdef TRANSPORT_SMTP
502 g = string_cat(g, US" smtp");
504 return string_cat(g, US"\n");
509 struct lookupmodulestr
512 struct lookup_module_info *info;
513 struct lookupmodulestr *next;
516 static struct lookupmodulestr *lookupmodules = NULL;
519 addlookupmodule(void *dl, struct lookup_module_info *info)
521 struct lookupmodulestr *p = store_get(sizeof(struct lookupmodulestr), FALSE);
525 p->next = lookupmodules;
527 lookup_list_count += info->lookupcount;
530 /* only valid after lookup_list and lookup_list_count are assigned */
532 add_lookup_to_list(lookup_info *info)
534 /* need to add the lookup to lookup_list, sorted */
537 /* strategy is to go through the list until we find
538 either an empty spot or a name that is higher.
539 this can't fail because we have enough space. */
541 while (lookup_list[pos] && (Ustrcmp(lookup_list[pos]->name, info->name) <= 0))
544 if (lookup_list[pos])
546 /* need to insert it, so move all the other items up
547 (last slot is still empty, of course) */
549 memmove(&lookup_list[pos+1], &lookup_list[pos],
550 sizeof(lookup_info *) * (lookup_list_count-pos-1));
552 lookup_list[pos] = info;
556 /* These need to be at file level for old versions of gcc (2.95.2 reported),
557 * which give parse errors on an extern in function scope. Each entry needs
558 * to also be invoked in init_lookup_list() below */
560 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
561 extern lookup_module_info cdb_lookup_module_info;
563 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
564 extern lookup_module_info dbmdb_lookup_module_info;
566 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
567 extern lookup_module_info dnsdb_lookup_module_info;
569 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
570 extern lookup_module_info dsearch_lookup_module_info;
572 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
573 extern lookup_module_info ibase_lookup_module_info;
575 #if defined(LOOKUP_JSON)
576 extern lookup_module_info json_lookup_module_info;
578 #if defined(LOOKUP_LDAP)
579 extern lookup_module_info ldap_lookup_module_info;
581 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
582 extern lookup_module_info lsearch_lookup_module_info;
584 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
585 extern lookup_module_info mysql_lookup_module_info;
587 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
588 extern lookup_module_info nis_lookup_module_info;
590 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
591 extern lookup_module_info nisplus_lookup_module_info;
593 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
594 extern lookup_module_info oracle_lookup_module_info;
596 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
597 extern lookup_module_info passwd_lookup_module_info;
599 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
600 extern lookup_module_info pgsql_lookup_module_info;
602 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
603 extern lookup_module_info redis_lookup_module_info;
605 #if defined(LOOKUP_LMDB)
606 extern lookup_module_info lmdb_lookup_module_info;
608 #if defined(SUPPORT_SPF)
609 extern lookup_module_info spf_lookup_module_info;
611 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
612 extern lookup_module_info sqlite_lookup_module_info;
614 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
615 extern lookup_module_info testdb_lookup_module_info;
617 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
618 extern lookup_module_info whoson_lookup_module_info;
621 extern lookup_module_info readsock_lookup_module_info;
625 init_lookup_list(void)
627 #ifdef LOOKUP_MODULE_DIR
630 int countmodules = 0;
631 int moduleerrors = 0;
633 static BOOL lookup_list_init_done = FALSE;
636 if (lookup_list_init_done)
638 reset_point = store_mark();
639 lookup_list_init_done = TRUE;
641 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
642 addlookupmodule(NULL, &cdb_lookup_module_info);
645 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
646 addlookupmodule(NULL, &dbmdb_lookup_module_info);
649 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
650 addlookupmodule(NULL, &dnsdb_lookup_module_info);
653 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
654 addlookupmodule(NULL, &dsearch_lookup_module_info);
657 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
658 addlookupmodule(NULL, &ibase_lookup_module_info);
662 addlookupmodule(NULL, &ldap_lookup_module_info);
666 addlookupmodule(NULL, &json_lookup_module_info);
669 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
670 addlookupmodule(NULL, &lsearch_lookup_module_info);
673 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
674 addlookupmodule(NULL, &mysql_lookup_module_info);
677 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
678 addlookupmodule(NULL, &nis_lookup_module_info);
681 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
682 addlookupmodule(NULL, &nisplus_lookup_module_info);
685 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
686 addlookupmodule(NULL, &oracle_lookup_module_info);
689 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
690 addlookupmodule(NULL, &passwd_lookup_module_info);
693 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
694 addlookupmodule(NULL, &pgsql_lookup_module_info);
697 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
698 addlookupmodule(NULL, &redis_lookup_module_info);
702 addlookupmodule(NULL, &lmdb_lookup_module_info);
706 addlookupmodule(NULL, &spf_lookup_module_info);
709 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
710 addlookupmodule(NULL, &sqlite_lookup_module_info);
713 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
714 addlookupmodule(NULL, &testdb_lookup_module_info);
717 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
718 addlookupmodule(NULL, &whoson_lookup_module_info);
721 addlookupmodule(NULL, &readsock_lookup_module_info);
723 #ifdef LOOKUP_MODULE_DIR
724 if (!(dd = exim_opendir(LOOKUP_MODULE_DIR)))
726 DEBUG(D_lookup) debug_printf("Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
727 log_write(0, LOG_MAIN, "Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
731 const pcre *regex_islookupmod = regex_must_compile(
732 US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
734 DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
735 while ((ent = readdir(dd)))
737 char *name = ent->d_name;
738 int len = (int)strlen(name);
739 if (pcre_exec(regex_islookupmod, NULL, name, len, 0, PCRE_EOPT, NULL, 0) >= 0)
741 int pathnamelen = len + (int)strlen(LOOKUP_MODULE_DIR) + 2;
743 struct lookup_module_info *info;
744 const char *errormsg;
746 /* SRH: am I being paranoid here or what? */
747 if (pathnamelen > big_buffer_size)
749 fprintf(stderr, "Loading lookup modules: %s/%s: name too long\n", LOOKUP_MODULE_DIR, name);
750 log_write(0, LOG_MAIN|LOG_PANIC, "%s/%s: name too long\n", LOOKUP_MODULE_DIR, name);
754 /* SRH: snprintf here? */
755 sprintf(CS big_buffer, "%s/%s", LOOKUP_MODULE_DIR, name);
757 if (!(dl = dlopen(CS big_buffer, RTLD_NOW)))
759 errormsg = dlerror();
760 fprintf(stderr, "Error loading %s: %s\n", name, errormsg);
761 log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, errormsg);
766 /* FreeBSD nsdispatch() can trigger dlerror() errors about
767 * _nss_cache_cycle_prevention_function; we need to clear the dlerror()
768 * state before calling dlsym(), so that any error afterwards only
769 * comes from dlsym().
771 errormsg = dlerror();
773 info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info");
774 if ((errormsg = dlerror()))
776 fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
777 log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
782 if (info->magic != LOOKUP_MODULE_INFO_MAGIC)
784 fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name);
785 log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name);
791 addlookupmodule(dl, info);
792 DEBUG(D_lookup) debug_printf("Loaded \"%s\" (%d lookup types)\n", name, info->lookupcount);
796 store_free((void*)regex_islookupmod);
800 DEBUG(D_lookup) debug_printf("Loaded %d lookup modules\n", countmodules);
803 DEBUG(D_lookup) debug_printf("Total %d lookups\n", lookup_list_count);
805 lookup_list = store_malloc(sizeof(lookup_info *) * lookup_list_count);
806 memset(lookup_list, 0, sizeof(lookup_info *) * lookup_list_count);
808 /* now add all lookups to the real list */
809 for (struct lookupmodulestr * p = lookupmodules; p; p = p->next)
810 for (int j = 0; j < p->info->lookupcount; j++)
811 add_lookup_to_list(p->info->lookups[j]);
812 store_reset(reset_point);
813 /* just to be sure */
814 lookupmodules = NULL;
817 #endif /*!MACRO_PREDEF*/
818 /* End of drtables.c */