static char rcsid[] = "@(#)$Id: service_list.c,v 1.31 2006/04/09 07:37:08 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.31 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) *****************************************************************************/ #include "headers.h" #include "ss_imp.h" #include "connection_imp.h" #ifdef USE_DLOPEN #include "shared_imp.h" #endif #include "s_me.h" #include "s_elm.h" DEBUG_VAR(Debug,__FILE__,"net"); #ifdef REMOTE_MBX /* Seems that h_errno is macro on AIX */ #ifndef h_errno extern int h_errno; #endif #include #ifndef ANSI_C extern int errno; #endif struct service_entry * service_list = NULL; int service_count = 0; /* Hard coded */ static struct service_type { char * name; int flags; PORTS defport; } SERVICE_TYPES[] = { { "*", STFLAG_browser|STFLAG_mbox| STFLAG_is_imap|STFLAG_is_pop | STFLAG_smtp_like| STFLAG_is_submission|STFLAG_is_smtp, PORT_end }, { "imap", STFLAG_browser|STFLAG_mbox|STFLAG_is_imap, PORT_imap4 }, { "pop", STFLAG_mbox|STFLAG_is_pop, PORT_pop3 }, { "submission", STFLAG_smtp_like|STFLAG_is_submission, PORT_submission }, { "smtp", STFLAG_smtp_like|STFLAG_is_smtp, PORT_smtp }, { NULL, 0, PORT_end }, }; CONST struct service_type * IMAP_SERVICE = & SERVICE_TYPES[1]; CONST struct service_type * POP_SERVICE = & SERVICE_TYPES[2]; CONST struct service_type * SUBMISSION_SERVICE = & SERVICE_TYPES[3]; CONST struct service_type * SMTP_SERVICE = & SERVICE_TYPES[4]; static void zero_service_entry P_((struct service_entry *entry, const char *name, const struct service_type *st, int flags)); static void zero_service_entry(entry,name,st,flags) struct service_entry *entry; CONST char *name; CONST struct service_type *st; int flags; { bzero((void *)entry,sizeof (*entry)); entry->flags = flags; entry->official_name = safe_strdup(name); entry->aliases_list = 0; entry->aliases_count = 0; entry->addr_list = NULL; entry->addr_count = 0; entry->addr_name_list = NULL; entry->addr_name_count = 0; entry->service = st; /* ??? */ entry->port_list = NULL; entry->port_count = 0; entry->option_list = NULL; entry->option_count = 0; } static void add_addr_name_to_entry P_((struct service_entry *entry, char *name, int malloc_it)); static void add_addr_name_to_entry(entry,name,malloc_it) struct service_entry *entry; char *name; int malloc_it; { entry->addr_name_list = safe_realloc(entry->addr_name_list, (entry->addr_name_count+1) * sizeof (entry->addr_name_list[0])); entry->addr_name_list[entry->addr_name_count++] = malloc_it ? safe_strdup(name) : name; } static void add_alias_to_entry P_((struct service_entry *entry, const char *name)); static void add_alias_to_entry(entry,name) struct service_entry *entry; CONST char *name; { entry->aliases_list = safe_realloc(entry->aliases_list, (entry->aliases_count+1) * sizeof (entry->aliases_list[0])); entry->aliases_list[entry->aliases_count++] = safe_strdup(name); } static void add_addr_to_entry P_((struct service_entry *entry, SOCKADDR *result)); static void add_addr_to_entry(entry,result) struct service_entry *entry; SOCKADDR *result; { entry->addr_list = safe_realloc(entry->addr_list, (entry->addr_count+1) * sizeof (entry->addr_list[0])); bzero((void *)&(entry->addr_list[entry->addr_count]), sizeof (entry->addr_list[entry->addr_count])); entry->addr_list[entry->addr_count++] = *result; } static void add_port_to_entry P_((struct service_entry *entry, int port)); static void add_port_to_entry(entry,port) struct service_entry *entry; int port; { entry->port_list = safe_realloc(entry->port_list, (entry->port_count+2) * sizeof (entry->port_list[0])); entry->port_list[entry->port_count++] = port; entry->port_list[entry->port_count] = PORT_end; } static int add_option_type_to_entry P_((struct service_entry *entry, struct SE_option_type * Y, const char * prefix)); static int add_option_type_to_entry(entry,Y,prefix) struct service_entry *entry; struct SE_option_type * Y; CONST char * prefix; { entry->option_list = safe_realloc(entry->option_list, (entry->option_count+1) * sizeof (entry->option_list[0])); bzero((void *) &(entry->option_list[entry->option_count]), sizeof (entry->option_list[0])); entry->option_list[entry->option_count].type = Y; entry->option_list[entry->option_count].prefix = safe_strdup(prefix); entry->option_list[entry->option_count].value = NULL; Y->zero_options(& (entry->option_list[entry->option_count])); return entry->option_count++; } static struct service_entry *malloc_service_entry P_((const char *name, const struct service_type *st)); static struct service_entry *malloc_service_entry(name,st) CONST char * name; CONST struct service_type *st; { struct service_entry *ret = safe_malloc (sizeof (struct service_entry)); /* Free this */ zero_service_entry(ret,name,st,SE_temporary); return ret; } static void merge1_entries P_((struct service_entry *target, struct service_entry *value)); static void merge1_entries(target,value) struct service_entry *target; struct service_entry *value; { if (target->service == &(SERVICE_TYPES[0])) { target->service = value->service; if (target->service != &(SERVICE_TYPES[0])) { DPRINT(Debug,10,(&Debug, "... setting temporary entry for %s to be type %s\n", target->official_name, target->service->name)); } } else if (target->service != value->service && value->service != &(SERVICE_TYPES[0])) { DPRINT(Debug,1,(&Debug, "... conflicting service entry types. Type %s used, %s ignored\n", target->service->name, value->service->name )); return; } if (0 != istrcmp(target->official_name,value->official_name)) { int j; for (j = 0; j < target->aliases_count; j++) { if (0 == istrcmp(value->official_name,target->aliases_list[j])) break; } if (j >= target->aliases_count) add_alias_to_entry(target,value->official_name); } /* Add missing aliases */ if (value->aliases_count > 0) { int i; for (i = 0; i < value->aliases_count; i++) { int j; if (0 == istrcmp(target->official_name,value->aliases_list[i])) continue; for (j = 0; j < target->aliases_count; j++) { if (0 == istrcmp(value->aliases_list[i], target->aliases_list[j])) break; } if (j >= target->aliases_count) add_alias_to_entry(target,value->aliases_list[i]); } } if (value->addr_name_count > 0) { int i; target->flags |= SE_given_name_addr; for (i = 0; i < value->addr_name_count; i++) { int j; for (j = 0; j < target->addr_name_count; j++) { if (0 == istrcmp(value->addr_name_list[i], target->addr_name_list[j])) break; } if (j >= target->addr_name_count) add_addr_name_to_entry(target, value->addr_name_list[i], 1 /* strdup it */); } } if (value->addr_count > 0 && (value->flags & SE_given_addr)) { int i; if (! (target->flags & SE_given_addr)) target->addr_count = 0; /* Forget cached addresses */ target->flags |= SE_given_addr; for (i = 0; i < value->addr_count; i++) { int j; for (j = 0; j < target->addr_count; j++) { if (0 == memcmp(&(value->addr_list[i]), &(target->addr_list[j]), sizeof (value->addr_list[i]))) break; } if (j >= target->addr_count) add_addr_to_entry(target,& (value->addr_list[i])); } } if (value->port_count > 0 && (value->flags & SE_given_port)) { int i; if (! (target->flags & SE_given_port)) target->port_count = 0; /* Forget default port */ target->flags |= SE_given_port; for (i = 0; i < value->port_count; i++) { int j; for (j = 0; j < target->port_count; j++) { if (value->port_list[i] == target->port_list[j]) break; } if (j >= target->port_count) add_port_to_entry(target,value->port_list[i]); } } if (value->option_count > 0) { int i; for (i = 0; i < value->option_count; i++) { int j; int idx = -1; for (j = 0; j < target->option_count; j++) { if (0 == strcmp(value->option_list[i].prefix, target->option_list[j].prefix)) idx = j; } if (-1 == idx) { struct SE_option_type * Y = #ifdef USE_DLOPEN get_option_type(value->option_list[i].prefix) #else NULL #endif ; if (!Y) { panic("CONNECTION PANIC",__FILE__,__LINE__, "merge1_entries", "option type disappeared",0); continue; } idx = add_option_type_to_entry(target,Y, value->option_list[i].prefix); } if (SE_option_t_magic != target->option_list[idx].type->magic) panic("CONNECTION PANIC",__FILE__,__LINE__, "merge1_entries", "Bad option type magic on service list",0); if (target->option_list[idx].type != value->option_list[i].type) panic("CONNECTION PANIC",__FILE__,__LINE__, "merge1_entries", "option type mismatch",0); target->option_list[idx].type-> merge_options(& (target->option_list[idx]), & (value->option_list[i]) ); } } } static void merge_entries P_((struct service_entry **ret, struct service_entry *value)); static void merge_entries(ret,value) struct service_entry **ret; struct service_entry *value; { if (value == *ret) return; if (!*ret) *ret = value; else { if ((*ret)->flags & SE_temporary) { merge1_entries(*ret,value); } else { struct service_entry * A = malloc_service_entry((*ret)->official_name, (*ret)->service); merge1_entries(A,*ret); merge1_entries(A,value); *ret = A; DPRINT(Debug,10,(&Debug, "... making temporary entry for %s from permanent\n", (*ret)->official_name)); } } } static int name_ok P_((CONST char *name)); static int name_ok(name) CONST char *name; { if ('\0' == *name) return 0; return strlen(name) == strspn(name,"abcdefghijklmnopqrstuvxyz.-"); } static void dump_service_entry P_((FILE *f, struct service_entry *entry)); static void dump_service_entry(f,entry) FILE *f; struct service_entry *entry; { char * sep = "\t"; int idx; fputs(entry->official_name,f); putc('\t',f); fputs(entry->service->name,f); if (entry->flags & SE_given_alias) { int y; for (y = 0; y < entry->aliases_count; y++) { if (name_ok(entry->aliases_list[y])) elm_fprintf(f,FRM("%salias=%s"), sep,entry->aliases_list[y]); else elm_fprintf(f,FRM("%salias=%Q"), sep,entry->aliases_list[y]); sep = "; "; } } if (entry->flags & SE_given_name_addr) { int y; for (y = 0; y < entry->addr_name_count; y++) { if (name_ok(entry->addr_name_list[y])) elm_fprintf(f,FRM("%saddr=%s"), sep,entry->addr_name_list[y]); else elm_fprintf(f,FRM("%salias=%Q"), sep,entry->addr_name_list[y]); sep = "; "; } } if (entry->flags & SE_given_addr) { int y; for (y = 0; y < entry->addr_count; y++) { SOCKADDR X = entry->addr_list[y]; switch (X.sa.sa_family) { char * z; #ifdef I_NETINET_IN case AF_INET: z = inet_ntoa(X.sin.sin_addr); elm_fprintf(f,FRM("%salias=ip:%Q"), sep,z); if (ntohs(X.sin.sin_port) != PORT_end) elm_fprintf(f,FRM(":%d"), ntohs(X.sin.sin_port)); sep = "; "; break; #endif default: /* none */ break; } } } if (entry->flags & SE_given_port) { int y; for (y = 0; y < entry->port_count; y++) { elm_fprintf(f,FRM("%sport=%d"), sep,entry->port_list[y]); sep = "; "; } } for (idx = 0; idx < entry->option_count; idx++) { char * val; if (SE_option_t_magic != entry->option_list[idx].type->magic) panic("CONNECTION PANIC",__FILE__,__LINE__, "dump_service_entry", "Bad option type magic on service list",0); val = entry->option_list[idx].type-> give_values(& (entry->option_list[idx]), entry->option_list[idx].prefix); if (val) { fputs(sep,f); fputs(val,f); free(val); val = NULL; } } putc('\n',f); } static struct service_entry *scan_list P_((const char *hostname, int flag)); static struct service_entry *scan_list(hostname,flag) CONST char *hostname; int flag; { struct service_entry *ret = NULL; int i; for (i = service_count-1; i >= 0; i--) { if (0 != (flag & service_list[i].service->flags)) { int j; if (0 == istrcmp(hostname,service_list[i].official_name)) { #ifdef DEBUG if (Debug.active >= 8) { FILE *F; DPRINT(Debug,8,(&Debug,"\n-- found [%d] ",i)); F = debug_to_FILE(&Debug); if (F) { dump_service_entry(F,&(service_list[i])); fclose(F); } } #endif /* ret = &(service_list[i]); */ merge_entries(&ret,&(service_list[i])); } for (j = 0; j < service_list[i].aliases_count; j++) { if (0 == istrcmp(hostname,service_list[i].aliases_list[j])) { #ifdef DEBUG if (Debug.active >= 8) { FILE *F; DPRINT(Debug,8,(&Debug,"\n-- found [%d] ",i)); F = debug_to_FILE(&Debug); if (F) { dump_service_entry(F,&(service_list[i])); fclose(F); } } #endif /* ret = &(service_list[i]); */ merge_entries(&ret,&(service_list[i])); } } } } #ifdef DEBUG if (Debug.active >= 8 && ret) { FILE *F; if (ret->flags & SE_temporary) { DPRINT(Debug,8,(&Debug,"\nMERGED ",i)); } else { DPRINT(Debug,8,(&Debug,"\nRESULT ",i)); } F = debug_to_FILE(&Debug); if (F) { dump_service_entry(F,ret); fclose(F); } DPRINT(Debug,8,(&Debug,"\n")); } #endif return ret; } static struct hostent * lookup_name P_((const char *hostname,int silent)); static struct hostent * lookup_name(hostname,silent) CONST char *hostname; int silent; { struct hostent *he = NULL; lib_transient(CATGETS(elm_msg_cat, MeSet,MeLookingUp, "Looking up %s ..."), hostname); he = gethostbyname(hostname); if (!he) { DPRINT(Debug,9,(&Debug, "lookup_name: %s: h_errno=%d\n", hostname,h_errno)); /* We are silent about errors if cached data exists */ if (!silent) { switch(h_errno) { case HOST_NOT_FOUND: lib_error(CATGETS(elm_msg_cat, MeSet,MeNoRemoteHost, "No remote mailbox server host: %s"), hostname); break; case NO_ADDRESS: lib_error(CATGETS(elm_msg_cat, MeSet,MeNoAddress, "Name %s have not IP-address"), hostname); break; case TRY_AGAIN: lib_error(CATGETS(elm_msg_cat, MeSet,MeTemporary, "Address for %s is not yet found..."), hostname); break; default: lib_error(CATGETS(elm_msg_cat, MeSet,MeNameError, "Failed to get address for %s"), hostname); break; } } } else { lib_transient(CATGETS(elm_msg_cat, MeSet,MeLookingUpOK, "Looking up %s ... OK"), hostname); } return he; } static void fill_address P_((struct service_entry *ret, struct hostent *he)); static void fill_address(ret,he) struct service_entry *ret; struct hostent *he; { if (he->h_addr_list) { int x; for (x = 0; he->h_addr_list[x]; x++) { SOCKADDR X; /* Make compaarision easier */ bzero((void *)&X, sizeof X); switch(he->h_addrtype) { #ifdef I_NETINET_IN case AF_INET: X.sin.sin_family = he->h_addrtype; X.sin.sin_port = htons(PORT_end); /* No port given */ if (sizeof(X.sin.sin_addr) != he->h_length) { lib_error(FRM("%s: Bad IP-addr length %d (should be %d)"), he->h_name,he->h_length, sizeof(X.sin.sin_addr)); return; } memcpy(&(X.sin.sin_addr), he->h_addr_list[x], he->h_length); add_addr_to_entry(ret,&X); break; #endif default: lib_error(CATGETS(elm_msg_cat, MeSet,MeUnsupportedAddrType, "Name %s have odd type address"), he->h_name); /* NOTE: We do not know on what compoents arbitary sockect address is constructed so we can't fill it */ return; } } } } struct service_entry * give_service_entry(hostname,flag) CONST char *hostname; int flag; { struct service_entry *ret; int have_port = 0; /* set if addresses have port set */ DPRINT(Debug,9,(&Debug, "give_service_entry(name=\"%s\",flags=%d)\n", hostname,flag)); ret = scan_list(hostname,flag); /* If on service entry do not have ip addresses they needed to be retrieved. Notice that we retrieve new entries every time (alternatively we should keep track of expiry times). However all other data is cached (also addresses if them is generated from names given on configuration data) */ if (ret) hostname = ret->official_name; if (ret && (0 != (ret->flags & SE_given_name_addr) && ret->addr_count == 0 || 0 != (ret->flags & SE_rescan_name_addr))) { int x; /* Clear data on case of rescan */ if (ret->addr_list) { free(ret->addr_list); ret->addr_list = NULL; ret->addr_count = 0; } ret->flags &= ~SE_rescan_name_addr; for (x = 0; x < ret->addr_name_count; x++) { struct hostent *he = lookup_name(ret->addr_name_list[x],0); if (he) fill_address(ret,he); else ret->flags |= SE_rescan_name_addr; } } if (!ret || 0 == (ret->flags & SE_given_addr) && 0 == (ret->flags & SE_given_name_addr)) { struct hostent *he = lookup_name(hostname, /* Silent: */ ret && ret->addr_count > 0); if (he) { /* Now try found entry again ... */ if (!ret) { ret = scan_list(he->h_name,flag); if (!ret && he->h_aliases) { int j; for (j = 0; he->h_aliases[j] && !ret; j++) ret = scan_list(he->h_aliases[j],flag); } /* we need to add hostname to aliases because it was not on there (otherwise entry should have found earlier) */ if (ret) add_alias_to_entry(ret,hostname); } if (!ret) { struct service_type *st; for (st = &SERVICE_TYPES[0]; st->name; st++) { if (flag == (flag &st->flags)) break; } if (!st->name) panic("CONNECTION PANIC",__FILE__,__LINE__, "give_service_entry", "Service type do not found", 0); ret = malloc_service_entry(hostname,st); } if (he->h_aliases) { /* Add missing aliases */ int x; for (x = 0; he->h_aliases[x]; x++) { int j; for (j=0; j < ret->aliases_count; j++) if (0 == istrcmp(he->h_aliases[x], ret->aliases_list[j])) break; if (j >= ret->aliases_count) add_alias_to_entry(ret,he->h_aliases[x]); } } if (he->h_addr_list) { /* Clear old data */ if (ret->addr_list) { free(ret->addr_list); ret->addr_list = NULL; ret->addr_count = 0; } fill_address(ret,he); } } } if (ret && ret->addr_count > 0) { int x; for (x = 0; x < ret->addr_count; x++) { switch(ret->addr_list[x].sa.sa_family) { #ifdef I_NETINET_IN case AF_INET: if (ret->addr_list[x].sin.sin_port == htons(PORT_end)) have_port = 0; break; #endif default: have_port = 0; break; } } } if (ret && ret->port_count == 0 && !have_port) { PORTS r = PORT_end; /* builtin definations here */ if ( PORT_end == ret->service->defport) { /* Caller will define portlist */ if (ret->port_list) free(ret->port_list); ret->port_list = NULL; } else r = ret->service->defport; if (r != PORT_end) { ret->port_list = safe_realloc(ret->port_list, 2 * sizeof (ret->port_list[0])); ret->port_list[0] = r; ret->port_list[1] = PORT_end; ret->port_count = 1; } } if (ret) { DPRINT(Debug,9,(&Debug, "give_service_entry=%p: type=%p (%s)%s\n", ret,ret->service,ret->service->name, ret->flags & SE_temporary ? ", temporary" : "")); } else { DPRINT(Debug,9,(&Debug, "give_service_entry=NULL\n")); } return ret; } void free_temporary_service_entry(Y) struct service_entry **Y; { if (!(*Y)) return; if ((*Y)->flags & SE_temporary) { int x; DPRINT(Debug,12,(&Debug, "free_temporary_service_entry: Freeing entry %p\n", *Y)); if ((*Y)->official_name) free((*Y)->official_name); (*Y)->official_name = NULL; for (x = 0; x < (*Y)->aliases_count; x++) { if ((*Y)->aliases_list[x]) free((*Y)->aliases_list[x]); (*Y)->aliases_list[x] = NULL; } if ((*Y)->aliases_list) free((*Y)->aliases_list); (*Y)->aliases_list = NULL; (*Y)->aliases_count = 0; if ((*Y)->addr_list) free((*Y)->addr_list); (*Y)->addr_list = NULL; (*Y)->addr_count = 0; for (x = 0; x < (*Y)->addr_name_count; x++) { if ((*Y)->addr_name_list[x]) free((*Y)->addr_name_list[x]); (*Y)->addr_name_list[x] = NULL; } if ((*Y)->addr_name_list) free((*Y)->addr_name_list); (*Y)->addr_name_list = NULL; (*Y)->addr_name_count = 0; (*Y)->service = NULL; if ((*Y)->port_list) free((*Y)->port_list); (*Y)->port_list = NULL; (*Y)->port_count = 0; for (x = 0; x < (*Y)->option_count; x++) { if (SE_option_t_magic != (*Y)->option_list[x].type->magic) panic("CONNECTION PANIC",__FILE__,__LINE__, "free_temporary_service_entry", "Bad option type magic on service list",0); (*Y)->option_list[x].type-> free_options(& ((*Y)->option_list[x])); if ((*Y)->option_list[x].prefix) free((*Y)->option_list[x].prefix); (*Y)->option_list[x].prefix = NULL; } if ((*Y)->option_list) free((*Y)->option_list); (*Y)->option_list = NULL; (*Y)->option_count = 0; /* Temporary entry is malloced ... */ free(*Y); } /* If not temporary, we assume that this entry is on list and therefore that does not produce dangling pointer */ (*Y) = NULL; } int parse_service_entries (filename,system, errors) CONST char *filename; int system; int *errors; { /* hostname service options */ int max_count = 0; int count = 0; FILE * f; char buf[LONG_STRING]; int c, l1; if (!system) { int err = can_open(filename,"r"); if (err) { DPRINT(Debug,2,(&Debug, "parse_service_entries=0: %s: %s (can_open)\n", filename,error_description(err))); return 0; } } f = fopen(filename,"r"); if (!f) { int err = errno; DPRINT(Debug,2,(&Debug, "parse_service_entries=0: %s: %s\n", filename,error_description(err))); return 0; } while(EOF != (c = fgetc(f))) if ('\n' == c) max_count++; DPRINT(Debug,9,(&Debug, "parse_service_entries: %s, max_count=%d\n", filename,max_count)); if (!max_count) { fclose(f); return 0; } rewind(f); service_list = safe_realloc(service_list, (service_count + max_count) * sizeof (service_list[0])); while (count < max_count && (l1 = mail_gets(buf,sizeof buf, f)) > 0) { char *c,*c1, *options; struct service_type *st; if ('\n' == buf[l1 -1]) buf[l1 - 1] = '\0'; else { lib_error(CATGETS(elm_msg_cat, MeSet, MeTooLongLine, "%.30s: Too long line: %.30s..."), filename,buf); (*errors) ++; break; } l1--; while (l1-- > 0 && whitespace(buf[l1])) buf[l1] = '\0'; c = buf; while (*c && whitespace (*c)) /* skip leading whitespace */ c++; if ('#' == *c) continue; if (!*c) continue; c1 = strpbrk(c," \t"); if (!c1) { lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine, "%.30s: Bad line: %.30s..."), filename,buf); (*errors) ++; break; } *c1 = '\0'; c1++; while (*c1 && whitespace (*c1)) /* skip leading whitespace */ c1++; if (!*c1) { lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine, "%.30s: Bad line: %.30s..."), filename,buf); (*errors) ++; break; } options = strpbrk(c1," \t;"); if (options) { *options = '\0'; options++; } for (st = &SERVICE_TYPES[0]; st->name; st++) if (0 == istrcmp(c1,st->name)) break; if (!st->name) { lib_error(CATGETS(elm_msg_cat, MeSet, MeServiceType, "%.30s: Bad service type %s"), filename,c1); (*errors) ++; break; } zero_service_entry( & (service_list[service_count+count]), c /* hostname */, st /* service */, 0 /* This is NOT temporary entry */); if (system) service_list[service_count+count].flags |= SE_system; if (options) { char *opt; char * WALK = NULL; for (opt = mime_parse_content_opts(options, &WALK); opt; opt = mime_parse_content_opts(NULL, &WALK)) { char *val = NULL; /* malloced by dequote_opt */ char * q = strchr(opt,'='); char * zopt = NULL; DPRINT(Debug,65,(&Debug, "mime_parse_content_opts gives: %s\n", opt)); if (q) *q++ = '\0'; if (0 == strcmp(opt,"alias")) { if (!q || !q[0]) { lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue, "%.30s: Option %s requires value"), filename,opt); (*errors) ++; continue; } val = dequote_opt(q,strlen(q)); add_alias_to_entry(& (service_list[service_count+count]), val); free(val); /* add_alias_to_entry strdup's value */ service_list[service_count+count].flags |= SE_given_alias; } else if (0 == strcmp(opt,"addr")) { char *q1 = NULL; if (!q || !q[0]) { lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue, "%.30s: Option %s requires value"), filename,opt); (*errors) ++; continue; } if ('"' != q[0]) q1 = strchr(q,':'); if (!q1) { /* name given */ add_addr_name_to_entry(&(service_list[service_count+count]), /* Malloces result */ dequote_opt(q,strlen(q)), 0); service_list[service_count+count].flags |= SE_given_name_addr; } else { /* Addr? given */ SOCKADDR result; #ifndef USE_INET_ATON long tmp; #endif char * port; /* Make comparision easier */ bzero((void *)&result, sizeof result); *q1++ = '\0'; port = qstrpbrk(q1,":/"); if (port) *port++ = '\0'; val = dequote_opt(q1,strlen(q1)); /* inet_aton() is better but not available on some systems so we need use inet_addr() */ #ifdef I_NETINET_IN if (0 == strcmp(q,"ip") && #ifdef USE_INET_ATON inet_aton(q1,&result.sin.sin_addr) #else -1 != (tmp = inet_addr(q1)) #endif ) { int p = PORT_end; /* No port given */ #ifndef USE_INET_ATON memcpy(&(result.sin.sin_addr.s_addr), &tmp, sizeof (result.sin.sin_addr.s_addr)); #endif result.sin.sin_family = AF_INET; if (port) p = atoi(port); result.sin.sin_port = htons(p); } else #endif { lib_error(CATGETS(elm_msg_cat, MeSet, MeBadAddrSpec, "%.30s: Bad address specification %s:%s"), filename,q,q1); (*errors) ++; free(val); val= NULL; continue; } add_addr_to_entry(& (service_list[service_count+ count]), &result); free(val); val = NULL; service_list[service_count+count].flags |= SE_given_addr; } } else if (0 == strcmp(opt,"port")) { int port; if (!q || !q[0]) { lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue, "%.30s: Option %s requires value"), filename,opt); (*errors) ++; continue; } port = atoi(q); if (port == PORT_end) { lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionPortBad, "%.30s: Option port=%d unsupported"), filename, port); (*errors) ++; continue; } add_port_to_entry(& (service_list[service_count+count]), port); service_list[service_count+count].flags |= SE_given_port; #ifdef USE_DLOPEN } else if (NULL != (zopt = strchr(opt,':'))) { int idx = -1; int x; *zopt++ = '\0'; for (x = 0; x < service_list[service_count+count]. option_count; x++) { if (0 == strcmp(opt,service_list[service_count+count]. option_list[x].prefix)) idx = x; } if (-1 == idx) { struct SE_option_type * Y = get_option_type(opt); if (!Y) continue; idx = add_option_type_to_entry(& (service_list [service_count+count]), Y,opt); } if (SE_option_t_magic != service_list[service_count+count]. option_list[idx].type->magic) panic("CONNECTION PANIC",__FILE__,__LINE__, "parse_service_entries", "Bad option type on service list",0); if (!service_list[service_count+count]. option_list[idx].type -> parse_on_option(& (service_list[service_count+count]. option_list[idx]), zopt,q)) { (*errors) ++; continue; } #endif } else { lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionUnspported, "%.30s: Option %s unsupported"), filename,opt); (*errors) ++; continue; } } } count++; } service_count += count; DPRINT(Debug,9,(&Debug, "parse_service_entries: %s, %d parsed (%d total)\n", filename,count,service_count)); fclose(f); return 1; } void dump_service_entries(f,system) FILE *f; int system; { int x; for (x = 0; x < service_count; x++) { if (system && (service_list[x].flags & SE_system) == 0) continue; if (!system && (service_list[x].flags & SE_system) != 0) continue; dump_service_entry(f,&(service_list[x])); } fflush(f); } #endif /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */