This is notify.c in view mode; [Download] [Up]
/* @(#)src/notify.c 1.2 24 Oct 1990 05:23:50 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * notify.c: * keep track of errors and handle notification of the appropriate * users for errors. * * external functions: defer_delivery, fail_delivery, succeed_delivery, * error_delivery, process_msg_log, notify */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <pwd.h> #include <signal.h> #include <time.h> #include "defs.h" #include "smail.h" #include "dys.h" #include "addr.h" #include "hash.h" #include "main.h" #include "log.h" #include "direct.h" #include "transport.h" #include "child.h" #include "exitcodes.h" #ifndef DEPEND # include "extern.h" # include "debug.h" #endif /* exported variables */ int send_to_postmaster; /* set TRUE to mail to postmaster */ int return_to_sender; /* set TRUE to mail log to sender */ /* functions local to this file */ static char *decode_x_line(); static void put_defer_error(); static void classify_addr(); static void notify_new_message(); static struct addr *remove_addr(); /* variables local to this file */ static char *log_banner = "\ |------------------------- Message log follows: -------------------------|\n"; static char *addr_error_banner = "\ |------------------------- Failed addresses follow: ---------------------|\n"; static char *text_banner = "\ |------------------------- Message text follows: ------------------------|\n"; /* * fail_delivery - log that delivery to the set of addresses failed */ void fail_delivery(addr) struct addr *addr; /* list of failed addrs */ { register struct addr *cur; if (cur && exitvalue == EX_OK) { exitvalue = EX_UNAVAILABLE; } for (cur = addr; cur; cur = cur->succ) { write_log(LOG_SYS, "%s ... failed: (ERR_%03ld) %s", cur->in_addr, cur->error->info & ERR_MASK, cur->error->message); /* X entries in msg log will be picked up by queue run scans */ if (cur->parent) { write_log(LOG_MLOG, "Xfail: <%s> parent: <%s> reason: (ERR_%03ld) %s", cur->in_addr, cur->parent->in_addr, cur->error->info & ERR_MASK, cur->error->message); } else { write_log(LOG_MLOG, "Xfail: <%s> reason: (ERR_%03ld) %s", cur->in_addr, cur->error->info & ERR_MASK, cur->error->message); } } } /* * defer_delivery - log that delivery to the set of addresses is deferred * * don't write an error message, if it would be a repeat of the most * recent deferal message written to the message log file for the * address. */ void defer_delivery(addr, defer_errors) struct addr *addr; /* list of succeeded addrs */ struct defer_addr *defer_errors; /* previous deferals */ { struct addr *cur; struct defer_addr *link; if (cur && exitvalue == EX_OK) { exitvalue = EX_TEMPFAIL; } for (cur = addr; cur; cur = cur->succ) { for (link = defer_errors; link; link = link->succ) { if (EQ(link->address, cur->in_addr) || link->parent == NULL && cur->parent == NULL || link->parent != NULL && cur->parent != NULL && EQ(link->parent, cur->parent->in_addr)) { break; } } if (link) { if (link->error == (cur->error->info & ERR_MASK) && EQ(link->message, cur->error->message)) { break; } } write_log((cur->error->info & ERR_CONFERR)? (LOG_SYS|LOG_TTY): LOG_SYS, "%s ... deferred: (ERR_%03ld) %s", cur->in_addr, cur->error->info & ERR_MASK, cur->error->message); if (cur->parent) { write_log(LOG_MLOG, "Xdefer: <%s> parent: <%s> reason: (ERR_%03ld) %s", cur->in_addr, cur->parent->in_addr, cur->error->info & ERR_MASK, cur->error->message); } else { write_log(LOG_MLOG, "Xdefer: <%s> reason: (ERR_%03ld) %s", cur->in_addr, cur->error->info & ERR_MASK, cur->error->message); } } } /* * succeed_delivery - log that delivery to the set of addresses succeeded */ void succeed_delivery(addr) struct addr *addr; /* list of succeeded addrs */ { register struct addr *cur; for (cur = addr; cur; cur = cur->succ) { if (dont_deliver) { write_log(LOG_SYS, "%s ... not delivered, debugging", cur->in_addr); } else { write_log(LOG_SYS, "%s ... delivered", cur->in_addr); } /* X entries will be picked up by queue run scans */ if (cur->parent) { write_log(LOG_MLOG, "Xsucceed: <%s> parent: <%s>", cur->in_addr, cur->parent->in_addr); } else { write_log(LOG_MLOG, "Xsucceed: <%s>", cur->in_addr); } } } /* * error_delivery - log that an error was sent for this list of addrs * * detect mail sent to owners and log the original address. */ void error_delivery(addr) struct addr *addr; /* list of error addrs */ { register struct addr *cur; for (cur = addr; cur; cur = cur->succ) { register struct addr *log_addr; if (cur->true_addr) { log_addr = cur->true_addr; write_log(LOG_SYS, "%s ... error sent to %s", log_addr->in_addr, cur->in_addr); } else { if (cur->error->info & ERR_NSENDER) { write_log(LOG_SYS, "%s ... error returned to %s", cur->in_addr, sender); } if (cur->error->info & ERR_NPOSTMAST) { write_log(LOG_SYS, "%s ... error sent to postmaster", cur->in_addr); } } /* X entries will be picked up by queue run scans */ if (cur->parent) { write_log(LOG_MLOG, "Xsent_error: <%s> parent: <%s>", cur->in_addr, cur->parent->in_addr); } else { write_log(LOG_MLOG, "Xsent_error: <%s>", cur->in_addr); } } } /* * process_msg_log - process X lines in the per-message log * * Lines beginning with X in the per-message log contain information on what * mail processing was completed in a previous run on the input spool file. * This function takes, as input, the set of addresses which were produced * by resolve_addr_list() for delivery and returns a list which does not * contain addresses which have already been delivered. * * It also returns a pointer to information which should be passed to the * notify() routine after all the transports have been called. */ struct addr * process_msg_log(in, sent_errors, defer_errors) struct addr *in; /* input resolved addresses */ struct identify_addr **sent_errors; /* return errors that have been sent */ struct defer_addr **defer_errors; /* last defer message for addrs */ { register struct addr *ret; /* addr list to return */ char *s; /* line of text from scan_msg_log() */ char *m; /* defer message */ ret = in; /* scan through all of the lines marked X in the message log file */ for (s = scan_msg_log(TRUE); s; s = scan_msg_log(FALSE)) { char *address; /* the address to remove */ char *parent; /* parent of address to remove */ if (strncmp(s, "Xfail:", sizeof("Xfail:") - 1) == 0) { /* get the failed address and the parent address */ (void) decode_x_line(s + sizeof("Xfail:") - 1 , &address, &parent); /* remove any occurance from the input list */ if (address) { ret = remove_addr(ret, address, parent); } } else if (strncmp(s, "Xsucceed:", sizeof("Xsucceed:") - 1) == 0) { /* get the delivered address and the parent address */ (void) decode_x_line(s + sizeof("Xsucceed:") - 1, &address, &parent); /* remove any occurance from the input list */ if (address) { ret = remove_addr(ret, address, parent); } } else if (strncmp(s, "Xdefer:", sizeof("Xdefer:") - 1) == 0) { /* grab the message and error number of the defered address */ m = decode_x_line(s + sizeof("Xdefer:") - 1, &address, &parent); /* put defer message into output defer_error list */ if (m) { put_defer_error(defer_errors, m, address, parent); } } else if (strncmp(s, "Xsent_error:", sizeof("Xsent_error:")-1) == 0) { register struct identify_addr *new_sent; /* get the sent error address and the parent address */ (void) decode_x_line(s + sizeof("Xsent_error:") - 1, &address, &parent); if (address == NULL) { continue; } /* give this address to notify */ new_sent = (struct identify_addr *)xmalloc(sizeof(*new_sent)); new_sent->address = COPY_STRING(address); if (parent) { new_sent->parent = COPY_STRING(parent); } else { new_sent->parent = NULL; } new_sent->succ = *sent_errors; *sent_errors = new_sent; } } return ret; } /* * decode_x_line - decode an X-line from the per-message log file * * The input should be of the form: * * <address> parent: <parent_address> garbage... * or <address> garbage... * * this routine returns the address and the parent_address. It also * returns a pointer to the garbage. */ static char * decode_x_line(line, address, parent) char *line; /* input line */ char **address; /* return address here */ char **parent; /* return parent address here */ { int level; /* count < and > nesting */ register char *p = line; /* point to parts of the line */ /* skip initial white-space */ while (isspace(*p)) p++; /* ignore anything not of the proper form */ if (*p != '<') { *address = NULL; return NULL; } p++; /* skip the initial < */ /* * extract the address as the text up until before the balancing * > character, in <address>. */ *address = p; /* mark this spot as the address */ level = 1; /* start at nesting level 1 */ if (*p == '<') { level++; } while (level > 0) { p = address_token(p); if (p == NULL) { *address = NULL; /* bad input form */ return NULL; } if (*p == '<') { level++; } else if (*p == '>') { --level; } } *p++ = '\0'; /* the last > is the end of address */ /* scan for a possible parent address */ while (isspace(*p)) p++; /* skip up until parent: */ /* is there a parent address? */ if (strncmp(p, "parent:", sizeof("parent:") - 1) != 0) { /* no */ *parent = NULL; return p; } p += sizeof("parent:") - 1; /* skip initial white-space before actual parent address */ while (isspace(*p)) p++; /* ignore anything not of the proper form */ if (*p != '<') { *parent = NULL; return NULL; } p++; /* skip the initial < */ /* * extract the parent as the text up until before the balancing * > character, in <parent_addres> */ *parent = p; level = 1; if (*p == '<') { level++; } while (level > 0){ p = address_token(p); if (p == NULL) { *address = NULL; return; } if (*p == '<') { level++; } else if (*p == '>') { --level; } } *p++ = '\0'; while (isspace(*p)) p++; return p; } /* * put_defer_error - put a deferal message for an address in a list * * remove previous entries for this address from the list. */ static void put_defer_error(list, message, address, parent) struct defer_addr **list; char *message; char *address; char *parent; { int error; static char reason_pfx[] = "reason: (ERR_"; char *p; struct defer_addr *link, *next_link, *new_list; if (strncmp(message, reason_pfx, sizeof(reason_pfx) - 1) != 0) { /* doesn't match the correct format */ return; } message += sizeof(reason_pfx) - 1; p = message; while (isdigit(*message)) message++; if (*message != ')') { /* doesn't match the correct format */ return; } *message = 0; error = atoi(p); *message++ = ')'; while (isspace(*message)) message++; new_list = (struct defer_addr *)xmalloc(sizeof(*new_list)); new_list->succ = NULL; new_list->error = error; new_list->message = COPY_STRING(message); new_list->address = COPY_STRING(address); new_list->parent = parent? COPY_STRING(parent): NULL; for (link = *list; link; link = next_link) { next_link = link->succ; if (EQ(link->address, address) || link->parent == NULL && parent == NULL || link->parent != NULL && parent != NULL && EQ(link->parent, parent)) { continue; } link->succ = new_list; new_list = link; } *list = new_list; } /* * notify - notify all users who are to receive a note about errors * * also, note any configuration errors and set call_defer_message * if any are found. If any addrs exist in the defer list, set * some_deferred_addrs to prevent main from calling unlink_spool(). */ void notify(defer, fail, sent_errors) struct addr *defer; /* list of deferred addresses */ struct addr *fail; /* list of failed addresses */ struct identify_addr *sent_errors; /* addrs already handled */ { register struct addr *cur; /* current addr being processed */ struct addr *next; /* next addr to process */ struct addr *to_owner = NULL; /* addrs to send to addr owners */ struct addr *verify = NULL; /* list of owner addrs to verify */ struct addr *to_other = NULL; /* to sender and/or postmaster */ static char *mailargs[] = { NULL, #ifdef GLOTZNET "-G", NULL, #endif /* GLOTZNET */ "-bS", NULL, }; FILE *f; /* stdin to child process */ int pid; /* child process id */ DEBUG(DBG_NOTIFY_HI, "notify called\n"); /* set the exec name to the smail binary */ mailargs[0] = smail; #ifdef GLOTZNET mailargs[2] = glotzhost; #endif /* GLOTZNET */ /* remove any address which have already been taken care of */ while (sent_errors) { defer = remove_addr(defer, sent_errors->address, sent_errors->parent); fail = remove_addr(fail, sent_errors->address, sent_errors->parent); sent_errors = sent_errors->succ; } /* * for deferred addresses, don't return anything, though note config * errors and defer the message if any were found. */ for (cur = defer; cur; cur = next) { next = cur->succ; some_deferred_addrs = TRUE; /* main should not call unlink_spool */ if (cur->error->info & ERR_CONFERR) { call_defer_message = TRUE; /* config error, defer whole message */ } } /* * for failed addresses put them in two lists, one list going to * the sender or the postmaster, the other list going to addr owners. */ for (cur = fail; cur; cur = next) { next = cur->succ; classify_addr(cur, &to_other, &verify); } /* verify all owners. If some don't verify, retry classify_addr() */ while (verify) { struct addr *new_list = NULL; /* unverified addrs */ /* * treat defer and fail lists as identical, since we cannot * reasonably handle defer addresses here. */ verify_addr_list(verify, &to_owner, &new_list, &new_list); /* take any new addrs and send through classify_addr */ verify = NULL; for (cur = new_list; cur; cur = next) { next = cur->succ; classify_addr(cur, &to_other, &verify); } } /* sort the list of verified addresses */ if (to_owner) { to_owner = addr_sort(to_owner, OFFSET(addr, in_addr)); } if (return_to_sender) { /* * if return_to_sender is on but the error_processing flag * doesn't call for a return message, then turn the flag off. */ if (error_processing != MAIL_BACK && error_processing != WRITE_BACK) { return_to_sender = FALSE; } /* turn it off, as well, for low priority messages */ if (islower(msg_grade) && msg_grade >= 'n') { return_to_sender = FALSE; } } /* * lastly, if the sender was given as the special string <>, * then an error message failed. Rather than risk an infinite * loop here, defer the message rather than delivering a recursive * error message. * * error message loops can still exist if SMTP is not used or if * an intermediate site does not correctly handle the special * sender string <>. */ if (error_sender) { if (return_to_sender || send_to_postmaster || to_owner) { call_defer_message = TRUE; return; } } /* if nobody needs to be notified, then nothing needs to be done */ if (! return_to_sender && ! send_to_postmaster && to_owner == NULL) { return; } /* * open a channel to a new smail process, operating in bsmtp mode, * and send it messages for all of the recipients. */ pid = open_child(mailargs, (char **)NULL, &f, (FILE **)NULL, -1, CHILD_MINENV|CHILD_RETRY, prog_euid, prog_egid); if (pid < 0) { DEBUG(DBG_NOTIFY_LO, "notify: open_child failed, cannot notify\n"); /* if we can't notify, let humans interfere later */ call_defer_message = TRUE; return; } /* start up the session */ (void) fprintf(f, "HELO %s\n", primary_name); /* * if any owners are to receive a message, then send to each * owner in turn */ cur = to_owner; while (cur) { char *last_owner; /* initiate a new message */ notify_new_message(f, cur->in_addr, "sending to address owner"); send_log(f, FALSE, log_banner); /* don't include X lines from log */ (void) fputs(addr_error_banner, f); /* write out all of the address errors */ do { if (cur->true_addr->error) { (void) fprintf(f, " %s ... failed: %s\n", cur->true_addr->in_addr, cur->true_addr->error->message); } last_owner = cur->in_addr; cur = cur->succ; } while (cur && EQIC(last_owner, cur->in_addr)); (void) fputs(text_banner, f); write_header(f, (long)(PUT_RECEIVED|LOCAL_TPORT)); putc('\n', f); write_body(f, (long)PUT_DOTS); (void) fputs(".\n", f); /* finish the message */ } /* if the postmaster is to receive mail, then send it to him (her?) */ if (send_to_postmaster) { int wrote_addr_error_banner = FALSE; /* only write banner once */ notify_new_message(f, "postmaster", "sending to postmaster"); send_log(f, TRUE, log_banner); /* include X lines from log */ /* write messages in to_other destined to the postmaster */ for (cur = to_other; cur; cur = cur->succ) { if (cur->error->info & ERR_NPOSTMAST) { if (!wrote_addr_error_banner) { (void) fputs(addr_error_banner, f); wrote_addr_error_banner = TRUE; } (void) fprintf(f, " %s ... %s\n", cur->in_addr, cur->error->message); } } (void) fputs(text_banner, f); write_header(f, (long)(PUT_RECEIVED|LOCAL_TPORT)); putc('\n', f); write_body(f, (long)PUT_DOTS); (void) fputs(".\n", f); /* finish the message */ } /* if the sender is to receive mail, arrange for that */ if (return_to_sender) { int wrote_addr_error_banner = FALSE; /* only write banner once */ notify_new_message(f, sender, "returning to sender"); send_log(f, FALSE, log_banner); /* don't include X lines from log */ /* write messages in to_other destined to the sender */ for (cur = to_other; cur; cur = cur->succ) { if (cur->error->info & ERR_NSENDER) { if (!wrote_addr_error_banner) { (void) fputs(addr_error_banner, f); wrote_addr_error_banner = TRUE; } (void) fprintf(f, " %s ... %s\n", cur->in_addr, cur->error->message); } } (void) fputs(text_banner, f); /* * a message grade between a and z causes body not to be returned. * This is handy when notification of errors is desired, but * when the overhead of sending the entire message back is not * necessary. */ write_header(f, (long)(PUT_RECEIVED|LOCAL_TPORT)); putc('\n', f); if (islower(msg_grade)) { fputs("[low-priority message, body not included]\n", f); } else { write_body(f, (long)PUT_DOTS); } (void) fputs(".\n", f); /* finish the message */ } (void) fputs("QUIT\n", f); (void) close_child(f, (FILE *)NULL, pid); /* note the addresses for which error messages have been sent */ error_delivery(to_owner); error_delivery(to_other); } /* * classify_addr - determine who is to be sent an error message * * If postmaster or the sender is to receive an error, link into the * to_other list, setting send_to_postmaster and/or return_to_sender * appropriately. If a config error is found, set call_defer_message. * * If we need to send to an owner, determine if an owner string is * defined and link an addr structure for that owner into the to_owner * list. */ static void classify_addr(addr, to_other, to_owner) register struct addr *addr; /* input address */ struct addr **to_other; /* list to sender or postmaster */ struct addr **to_owner; /* list to address owner */ { int link_to_other = FALSE; /* TRUE to link into to_other list */ if (addr->error->info & (ERR_NSENDER|ERR_NPOSTMAST)) { /* send to either the sender or the postmaster */ link_to_other = TRUE; /* note which one */ if (addr->error->info & ERR_NSENDER) { return_to_sender = TRUE; } if (addr->error->info & ERR_NPOSTMAST) { send_to_postmaster = TRUE; } } if (addr->error->info & (ERR_NSOWNER|ERR_NPOWNER)) { /* send to an address owner, if one is defined and expandable */ register char *s = NULL; /* expanded owner string */ register struct addr *cur; /* addr for scanning parents */ char *work_addr; /* output from preparse_address() */ /* * find the closest parent associated with a director that * defines an owner string. */ for (cur = addr; cur; cur = cur->parent) { char *error; /* temp for storing parse error */ /* * must have a reasonable, expandable owner address */ if (cur->director && cur->director->owner && (s = expand_string(cur->director->owner, (struct addr *)NULL, cur->home, cur->remainder)) && (work_addr = preparse_address(s, &error))) { break; } } if (cur) { /* owner found for the address, build entry in to_owner list */ struct addr *new = alloc_addr(); new->succ = *to_owner; /* link */ *to_owner = new; new->in_addr = COPY_STRING(s); /* copy owner address */ /* * if this addr has already been seen by classify_addr() use * the true_addr assigned previously, otherwise this is the * first time through, so assign the original addr */ if (addr->true_addr) { new->true_addr = addr->true_addr; } else { new->true_addr = addr; /* keep track of true addr */ } new->work_addr = work_addr; /* use the preparsed address */ /* disable use of smartuser driver */ new->flags = ADDR_SMARTUSER; /* * if verify_addr_list() fails to verify this list look higher * up for a parent that has an associated owner. */ new->parent = cur->parent; } else { /* no owner for the address, send to sender and/or postmaster */ if (addr->true_addr) { int perhaps_link = FALSE; /* true to see link, if not linked */ struct error *true_error = addr->true_addr->error; if ((addr->error->info & ERR_NSOWNER) || (addr->error->info & ERR_NPOWNER)) { perhaps_link = TRUE; } /* only link if this has not been linked to to_other before */ if (perhaps_link) { if (! (true_error->info & (ERR_NSENDER|ERR_NPOSTMAST)) ) { /* link the true address, not this address */ addr = addr->true_addr; link_to_other = TRUE; } } } else { link_to_other = TRUE; } if (addr->error->info & ERR_NSOWNER) { addr->error->info |= ERR_NSENDER; return_to_sender = TRUE; } if (addr->error->info & ERR_NPOWNER) { addr->error->info |= ERR_NPOSTMAST; send_to_postmaster = TRUE; } } } /* if link_to_other was set anywhere above, link into to_other list */ if (link_to_other) { addr->succ = *to_other; *to_other = addr; } } static void notify_new_message(f, to, subject_to) FILE *f; char *to; /* recipient */ char *subject_to; /* message for inclusion in subject */ { /* initiate a new message */ (void) fprintf(f, "MAIL FROM:<>\n"); /* state the recipient */ (void) fprintf(f, "RCPT TO:<%s>\n", to); /* start the message text, with the complete header */ (void) fprintf(f, "DATA\n\From: <MAILER-DAEMON@%s>\n", primary_name); if (to[0] == '@') { /* watch out for route-addrs */ (void) fprintf(f, "To: <%s>\n", to); } else { (void) fprintf(f, "To: %s\n", to); } (void) fprintf(f, "Subject: mail failed, %s\nReference: <%s@%s>\n\n", subject_to, message_id, primary_name); } /* * remove_addr - remove any matched addresses from an input list * * given an address string and (perhaps) a parent address string and * an input address list, remove any occurance of an address in the * input list whose in_addr matches the specified address string and * whose parent in_addr string matches the specified parent string. * If parent is NULL then there must not be a parent address, otherwise * there must be a matching parent address. */ static struct addr * remove_addr(in, address, parent) struct addr *in; /* input addr list */ char *address; /* address to match against */ char *parent; /* parent of address to match */ { register struct addr *cur; /* current address to process */ struct addr *next; /* next address to process */ struct addr *out = NULL; /* output address list */ for (cur = in; cur; cur = next) { next = cur->succ; if (EQ(cur->in_addr, address)) { /* the address does matches */ if (parent) { /* a matching parent is required for a match */ if (cur->parent && EQ(parent, cur->parent->in_addr)) { /* match, don't put it on the output queue */ DEBUG1(DBG_NOTIFY_LO, "%s ... already delivered\n", cur->in_addr); continue; } } else if (cur->parent == NULL) { /* match, don't put it on the output queue */ DEBUG1(DBG_NOTIFY_LO, "%s ... already delivered\n", cur->in_addr); continue; } } /* no match, put the address on the output queue */ cur->succ = out; out = cur; } return out; /* return the new list */ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.