Esempio n. 1
0
File: sln.c Progetto: rsenn/dirlist
int
sln(const char* path) {
  stralloc s, d;
  char* to;
  ssize_t i;
  stralloc_init(&s);
  stralloc_copys(&s, path);

  stralloc_init(&d);
  stralloc_copy(&d, &s);

  while(reduce(&d)) {

    buffer_puts(buffer_2, "'");
    buffer_putsa(buffer_2, &d);
    buffer_puts(buffer_2, "' -> '");
    buffer_putsa(buffer_2, &s);
    buffer_puts(buffer_2, "'\n");
    buffer_flush(buffer_2);

    stralloc_nul(&s);
    stralloc_nul(&d);

    if(mklink_sa(&s, &d) == -1) {
      errmsg_warnsys("symlink failed", NULL);
      exit(2);
    }
    stralloc_copy(&s, &d);
  }

  return 0;
}
Esempio n. 2
0
int
qldap_get_dotmode(qldap *q, stralloc *dm)
{
	int	r;

	/* get and check the status of the account */
	r = qldap_get_attr(q, LDAP_DOTMODE, &ldap_attr, SINGLE_VALUE);
	if (r == NOSUCH) {
		if (!stralloc_copy(dm, &dotmode)) return ERRNO;
		return OK;
	}
	if (r != OK)
		return r;

	if (!case_diffs(DOTMODE_LDAPONLY, ldap_attr.s)) {
		if (!stralloc_copys(dm, DOTMODE_LDAPONLY)) return ERRNO;
	} else if (!str_diff(DOTMODE_LDAPWITHPROG, ldap_attr.s)) {
		if (!stralloc_copys(dm, DOTMODE_LDAPWITHPROG)) return ERRNO;
	} else if (!str_diff(DOTMODE_DOTONLY, ldap_attr.s)) {
		if (!stralloc_copys(dm, DOTMODE_DOTONLY)) return ERRNO;
	} else if (!str_diff(DOTMODE_BOTH, ldap_attr.s)) {
		if (!stralloc_copys(dm, DOTMODE_BOTH)) return ERRNO;
	} else if (!str_diff(DOTMODE_NONE, ldap_attr.s)) {
		if (!stralloc_copys(dm, DOTMODE_NONE)) return ERRNO;
	} else {
		return ILLVAL;
	}
	if (!stralloc_0(dm)) return ERRNO;
	return OK;
}
Esempio n. 3
0
int dns_ip6_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
{
  unsigned int i;
  unsigned int j;
  unsigned int plus;
  unsigned int fqdnlen;

  if (!stralloc_copy(fqdn,in)) return -1;

  for (j = i = 0;j < rules->len;++j)
    if (!rules->s[j]) {
      if (!doit(fqdn,rules->s + i)) return -1;
      i = j + 1;
    }

  fqdnlen = fqdn->len;
  plus = byte_chr(fqdn->s,fqdnlen,'+');
  if (plus >= fqdnlen)
    return dns_ip6(out,fqdn);

  i = plus + 1;
  for (;;) {
    j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
    byte_copy(fqdn->s + plus,j,fqdn->s + i);
    fqdn->len = plus + j;
    if (dns_ip6(out,fqdn) == -1) return -1;
    if (out->len) return 0;
    i += j;
    if (i >= fqdnlen) return 0;
    ++i;
  }
}
void to_owner(void)
{
	stralloc_copy(&owner,&outlocal);
	stralloc_cats(&owner,"-owner@");
	stralloc_cat(&owner,&outhost);
	stralloc_0(&owner);
	qmail_to(&qq,owner.s);
}
Esempio n. 5
0
int control_rldef(stralloc *sa,char *fn,int flagme,char *def)
{
  int r;
  r = control_readline(sa,fn);
  if (r) return r;
  if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1;
  if (def) return stralloc_copys(sa,def) ? 1 : -1;
  return r;
}
Esempio n. 6
0
static const char *makepath(const char *fn)
{
  if (!stralloc_copy(&path,&basedir)) die_nomem();
  if (fn) {
    if (!stralloc_append(&path,"/")) die_nomem();
    if (!stralloc_cats(&path,fn)) die_nomem();
  }
  if (!stralloc_0(&path)) die_nomem();
  return path.s;
}
int geton(const char *action)
{
  const char *fl;
  int r;
  unsigned int i;
  unsigned char ch;

  fl = get_from(target.s,action);		/* try to match up */
  r = subscribe(workdir,target.s,1,fl,(*action == ACTION_RC[0]) ? "+mod" : "+",-1);
  if (flagdig == FLD_DENY || flagdig == FLD_ALLOW)
    strerr_die2x(0,INFO,MSG1(ERR_EXTRA_SUB,target.s));
  switch (r) {
    case 1:
	    qmail_puts(&qq,"List-Unsubscribe: <mailto:");	/*rfc2369 */
	    qmail_put(&qq,outlocal.s,outlocal.len);
	    qmail_puts(&qq,"-unsubscribe-");
		/* url-encode since verptarget is controlled by sender */
		/* note &verptarget ends in '\0', hence len - 1! */
	    for (i = 0; i < verptarget.len - 1; i++) {
	      ch = verptarget.s[i];
	      if (str_chr("\"?;<>&/:%+#",ch) < 10 ||
			 (ch <= ' ') || (ch & 0x80)) {
		urlstr[1] = hex[ch / 16];
	        urlstr[2] = hex[ch & 0xf];
		qmail_put(&qq,urlstr,3);
	      } else {
		qmail_put(&qq,verptarget.s + i, 1);
	      }
	    }
	    qmail_puts(&qq,"@");
	    qmail_put(&qq,outhost.s,outhost.len);	/* safe */
	    qmail_puts(&qq,">\n");
	    hdr_subject(MSG(SUB_WELCOME));
            hdr_ctboundary();
	    stralloc_copy(&confirm,&outlocal);
	    stralloc_cats(&confirm,"-unsubscribe-");
	    stralloc_cats(&confirm,verptarget.s);
	    stralloc_append(&confirm,'@');
	    stralloc_cat(&confirm,&outhost);
	    stralloc_0(&confirm);
	    set_cpconfirm(confirm.s,outlocal.len);	/* for !R in copy */
            copy(&qq,"text/top",flagcd);
            copy_act("text/sub-ok");
            break;
    default:
            if (str_start(action,ACTION_TC))
              strerr_die2x(0,INFO,MSG(ERR_SUB_NOP));
	    hdr_subject(MSG(SUB_SUBSCRIBE_NOP));
            hdr_ctboundary();
            copy(&qq,"text/top",flagcd);
            copy_act("text/sub-nop");
            break;
  }
  return r;
}
Esempio n. 8
0
void linkdotdir(const char *dash,const char *slash)
{
  stralloc_copy(&dotplus,&dot);
  stralloc_cats(&dotplus,dash);
  stralloc_0(&dotplus);
  dirplusmake(slash);
  if (flags['e' - 'a'])
    if (unlink(dotplus.s) == -1)
      if (errno != error_noent)
        strerr_die2sys(111,FATAL,MSG1(ERR_DELETE,dotplus.s));
  if (symlink(dirplus.s,dotplus.s) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_CREATE,dotplus.s));
  keyaddtime();
}
Esempio n. 9
0
int sub_sql_issub(struct subdbinfo *info,
		  const char *table,
		  const char *userhost,
		  stralloc *recorded)
{
  unsigned int j;
  void *result;
  int ret;

  /* SELECT address FROM list WHERE address = 'userhost' AND hash */
  /* BETWEEN 0 AND 52. Without the hash restriction, we'd make it */
  /* even easier to defeat. Just faking sender to the list name would*/
  /* work. Since sender checks for posts are bogus anyway, I don't */
  /* know if it's worth the cost of the "WHERE ...". */

  make_name(info,table?"_":0,table,0);

  /* Lower-case the domain portion */
  stralloc_copys(&addr,userhost);
  j = byte_rchr(addr.s,addr.len,'@');
  if (j == addr.len)
    return 0;
  case_lowerb(addr.s + j + 1,addr.len - j - 1);

  stralloc_copys(&query,"SELECT address FROM ");
  stralloc_cat(&query,&name);
  stralloc_cats(&query," WHERE ");
  stralloc_cats(&query,sql_issub_where_defn);

  result = sql_select(info,&query,1,&addr);

  if (!sql_fetch_row(info,result,1,&addr))
    ret = 0;
  else {
    /* we need to return the actual address as other dbs may accept
     * user-*@host, but we still want to make sure to send to e.g the
     * correct moderator address. */
    if (recorded != 0) {
      stralloc_copy(recorded,&addr);
      stralloc_0(recorded);
    }
    ret = 1;
  }
  sql_free_result(info,result);
  return ret;
}
Esempio n. 10
0
void load_config(const char *dir)
{
  load_flags(dir);

  switch(slurp("key",&key,512)) {
    case -1:
      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
    case 0:
      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
  }

  /* There are problems with using getconf_line to fetch the ezmlmrc
   * pointer, since the alt location for "ezmlmrc" turns out to be the
   * whole ezmlmrc file itself. */
  switch (slurp("ezmlmrc",&ezmlmrc,64)) {
  case -1:
    strerr_die4sys(111,FATAL,ERR_READ,dir,"/ezmlmrc: ");
  case 0:
    ezmlmrc.len = 0;
  }
  ezmlmrc.len = byte_chr(ezmlmrc.s,ezmlmrc.len,'\n');

  getconf_line(&outhost,"outhost",1,dir);
  getconf_line(&outlocal,"outlocal",1,dir);
  if (!stralloc_copy(&local,&outlocal)) die_nomem();

  getconf_line(&listid,"listid",0,dir);
  if (getconf_line(&charset,"charset",0,dir)) {
    if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
      if (charset.s[charset.len - 1] == 'B' ||
		 charset.s[charset.len - 1] == 'Q') {
        flagcd = charset.s[charset.len - 1];
        charset.s[charset.len - 2] = '\0';
      }
    }
  } else
    if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
  if (!stralloc_0(&charset)) die_nomem();

  // FIXME: need to handle escapes in mailinglist
  getconf_line(&mailinglist,"mailinglist",1,dir);
}
Esempio n. 11
0
int dns_resolvconfrewrite(stralloc *out)
{
  struct taia now;

  taia_now(&now);
  if (taia_less(&deadline,&now)) ok = 0;
  if (!uses) ok = 0;

  if (!ok) {
    if (init(&rules) == -1) return -1;
    taia_uint(&deadline,600);
    taia_add(&deadline,&now,&deadline);
    uses = 10000;
    ok = 1;
  }

  --uses;
  if (!stralloc_copy(out,&rules)) return -1;
  return 0;
}
Esempio n. 12
0
int control_readfile(stralloc *sa,char *fn,int flagme)
{
  buffer b;
  int fd;
  int match;

  if (!stralloc_copys(sa,"")) return -1;

  fd = open_read(fn);
  if (fd == -1)
  {
    if (errno == ENOENT)
    {
      if (flagme && meok)
      {
        if (!stralloc_copy(sa,&me)) return -1;
        if (!stralloc_0(sa)) return -1;
        return 1;
      }
      return 0;
    }
    return -1;
  }

  buffer_init(&b,read,fd,inbuf,sizeof(inbuf));

  for (;;)
  {
    if (getln(&b,&line,&match,'\n') == -1) break;
    if (!match && !line.len) { close(fd); return 1; }
    striptrailingwhitespace(&line);
    if (!stralloc_0(&line)) break;
    if (line.s[0])
      if (line.s[0] != '#')
        if (!stralloc_cat(sa,&line)) break;
    if (!match) { close(fd); return 1; }
  }
  close(fd);
  return -1;
}
Esempio n. 13
0
static void load_config(void)
{
  load_flags();

  key.len = 0;
  switch(slurp("key",&key,512)) {
    case -1:
      strerr_die2sys(111,FATAL,MSG1(ERR_READ,"key"));
    case 0:
      strerr_die4x(100,FATAL,listdir,"/key",MSG(ERR_NOEXIST));
  }

  /* There are problems with using getconf_line to fetch the ezmlmrc
   * pointer, since the alt location for "ezmlmrc" turns out to be the
   * whole ezmlmrc file itself. */
  switch (slurp("ezmlmrc",&ezmlmrc,64)) {
  case -1:
    strerr_die2sys(111,FATAL,MSG1(ERR_READ,"ezmlmrc"));
  case 0:
    ezmlmrc.len = 0;
  }
  ezmlmrc.len = byte_chr(ezmlmrc.s,ezmlmrc.len,'\n');

  getconf_line(&outhost,"outhost",1);
  getconf_line(&outlocal,"outlocal",1);
  if (!stralloc_copy(&local,&outlocal)) die_nomem();

  getconf_line(&listid,"listid",0);
  if (getconf_line(&charset,"charset",0)) {
    if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
      if (charset.s[charset.len - 1] == 'B' ||
		 charset.s[charset.len - 1] == 'Q') {
        flagcd = charset.s[charset.len - 1];
        charset.s[charset.len - 2] = '\0';
      }
    }
  } else
    if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
  if (!stralloc_0(&charset)) die_nomem();
}
Esempio n. 14
0
static int openone(unsigned long outnum)
{
  static stralloc fnadir = {0};
  char strnum[FMT_ULONG];
  int fd;
  stralloc_copys(&fnadir,"archive/");
  stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,outnum / 100));
  stralloc_copy(&fnaf,&fnadir);
  stralloc_cats(&fnaf,"/");
  stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int)(outnum % 100),2));
  stralloc_0(&fnadir);
  stralloc_0(&fnaf);

  if (mkdir(fnadir.s,0755) == -1)
    if (errno != error_exist)
      strerr_die2sys(111,FATAL,MSG1(ERR_CREATE,fnadir.s));
  if ((fd = open_trunc(fnaf.s)) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fnaf.s));

  substdio_fdbuf(&ssarchive,write,fd,archivebuf,sizeof archivebuf);
  return fd;
}
Esempio n. 15
0
int
xml_read_function(xmlreader* reader, xmlnodeid id, stralloc* name, stralloc* value, HMAP_DB** attrs) {
  switch(id) {
    case XML_TEXT: {
      buffer_putsa(buffer_1, value);
      break;
    }
    case XML_ELEMENT: {
      int closing = reader->closing || reader->self_closing;

      if(reader->closing)
        --depth;

      if(!(reader->closing && !prev_closing && stralloc_equal(&prev_element, name)) && stralloc_length(&prev_element)) {
        buffer_puts(buffer_1, "\n");
        buffer_putnspace(buffer_1, depth * 2);
      }

      buffer_putm_3(buffer_1, "<", reader->closing ? "/" : "", name->s);

      if(attrs && *attrs && (*attrs)->list_tuple) {
        buffer_putspace(buffer_1);
        xml_print_attributes(*attrs, buffer_1, " ", "=", "\"");
      }

      buffer_puts(buffer_1, reader->self_closing ? (name->s[0] == '?' ? "?>" : "/>") : ">");

      stralloc_copy(&prev_element, name);
      prev_closing = closing;

      if(!reader->closing && !reader->self_closing)
        ++depth;
      break;
    }
    default: break;
  }
  return 1;
}
Esempio n. 16
0
int openone(unsigned long outnum)
{
  int fd;
  if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
  if (!stralloc_catb(&fnadir,strnum,
		     fmt_ulong(strnum,outnum / 100))) die_nomem();
  if (!stralloc_copy(&fnaf,&fnadir)) die_nomem();
  if (!stralloc_cats(&fnaf,"/")) die_nomem();
  if (!stralloc_catb(&fnaf,strnum,
		     fmt_uint0(strnum,(unsigned int)(outnum % 100),2)))
    die_nomem();
  if (!stralloc_0(&fnadir)) die_nomem();
  if (!stralloc_0(&fnaf)) die_nomem();

  if (mkdir(fnadir.s,0755) == -1)
    if (errno != error_exist)
      strerr_die4sys(111,FATAL,ERR_CREATE,fnadir.s,": ");
  if ((fd = open_trunc(fnaf.s)) == -1)
    strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": ");

  substdio_fdbuf(&ssarchive,write,fd,archivebuf,sizeof archivebuf);
  return fd;
}
Esempio n. 17
0
void doconfirm(const char *act)
/* This should only be called with valid act for sub/unsub confirms. If act */
/* is not ACTION_[RST]C, it is assumed to be an unsubscribe conf.*/
/* "act" is the first letter of desired confirm request only as STRING! */
{
  strnum[fmt_ulong(strnum,when)] = 0;
  cookie(hash,key.s,key.len-flagdig,strnum,target.s,act);
  stralloc_copy(&confirm,&outlocal);
  stralloc_append(&confirm,'-');
  stralloc_catb(&confirm,act,1);
  stralloc_cats(&confirm,"c.");
  stralloc_cats(&confirm,strnum);
  stralloc_append(&confirm,'.');
  stralloc_catb(&confirm,hash,COOKIE);
  stralloc_append(&confirm,'-');
  stralloc_cats(&confirm,verptarget.s);
  stralloc_append(&confirm,'@');
  stralloc_cat(&confirm,&outhost);
  stralloc_0(&confirm);
  set_cpconfirm(confirm.s,outlocal.len);		/* for copy */
  set_cpaction(act);
  set_cphash(hash);
  set_cpwhen(when);

  qmail_puts(&qq,"Reply-To: ");
  quote2(&quoted,confirm.s);
  qmail_put(&qq,quoted.s,quoted.len);
  qmail_puts(&qq,"\n");

  hdr_subject((*act == ACTION_SC[0]) ? MSG(SUB_USR_SUBSCRIBE)
	      : (*act == ACTION_UC[0]) ? MSG(SUB_USR_UNSUBSCRIBE)
	      : (*act == ACTION_TC[0] || *act == ACTION_RC[0])
	      ? MSG(SUB_MOD_SUBSCRIBE)
	      : MSG(SUB_MOD_UNSUBSCRIBE));
  hdr_ctboundary();
    copy(&qq,"text/top",flagcd);
}
Esempio n. 18
0
/* convert a query to a filename mainly by adding slashes instead of 
   dots (this is an abstraction) and removing anything that is not
   a valid in DNS, which means A-Z and '-' */
int query2filename(char *q, stralloc *s)
{
  int i;
  char *p;
  stralloc tmp = { 0 };

  p = q;

  /* XXX: handling of uppercase/lowercase is missing */
  while(*p)
    {
      stralloc_copy(&tmp, s);
      stralloc_copyb(s, "/", 1);
      stralloc_catb(s, p+1, (int) *p);
      /* Clean up string by changing all characters not allowed in
	 dnsnames to '+'. We need to do tis to keep strange meta
	 characters from slipping into syscalls when opening
	 files changing directorys */
      for(i = 1; i < s->len; i++)
	{
	  if(!(((s->s[i] >= 'a') && (s->s[i] <= 'z'))
	       || ((s->s[i] >= 'A') && (s->s[i] <= 'Z'))
	       || ((s->s[i] >= '0') && (s->s[i] <= '9'))
	       || s->s[i] == '-'))
	    {
	      s->s[i] = '+';
	    }
	} 
      stralloc_cat(s, &tmp);
      p += ((int) *p) + 1;
    }

  stralloc_0(s);

  return s->len;
}
Esempio n. 19
0
main(int argc,char **argv)
{
  int fakev4=0;
  unsigned long u;
  int opt;
  char *x;
  int j;
  int s;
  int cloop;

  dns_random_init(seed);

  close(6);
  close(7);
  sig_ignore(sig_pipe);
 
  while ((opt = getopt(argc,argv,"46dDvqQhHrRi:p:t:T:l:I:")) != opteof)
    switch(opt) {
      case '4': noipv6 = 1; break;
      case '6': forcev6 = 1; break;
      case 'd': flagdelay = 1; break;
      case 'D': flagdelay = 0; break;
      case 'v': verbosity = 2; break;
      case 'q': verbosity = 0; break;
      case 'Q': verbosity = 1; break;
      case 'l': forcelocal = optarg; break;
      case 'H': flagremotehost = 0; break;
      case 'h': flagremotehost = 1; break;
      case 'R': flagremoteinfo = 0; break;
      case 'r': flagremoteinfo = 1; break;
      case 't': scan_ulong(optarg,&itimeout); break;
      case 'T': j = scan_ulong(optarg,&ctimeout[0]);
		if (optarg[j] == '+') ++j;
		scan_ulong(optarg + j,&ctimeout[1]);
		break;
      case 'i': if (!scan_ip6(optarg,iplocal)) usage(); break;
      case 'I': netif=socket_getifidx(optarg); break;
      case 'p': scan_ulong(optarg,&u); portlocal = u; break;
      default: usage();
    }
  argv += optind;

  if (!verbosity)
    buffer_2->fd = -1;

  hostname = *argv;
  if (!hostname) usage();
  if (!hostname[0] || str_equal(hostname,"0"))
    hostname = (noipv6?"127.0.0.1":"::1");

  x = *++argv;
  if (!x) usage();
  if (!x[scan_ulong(x,&u)])
    portremote = u;
  else {
    struct servent *se;
    se = getservbyname(x,"tcp");
    if (!se)
      strerr_die3x(111,FATAL,"unable to figure out port number for ",x);
    portremote = ntohs(se->s_port);
    /* i continue to be amazed at the stupidity of the s_port interface */
  }

  if (!*++argv) usage();

  if (!stralloc_copys(&tmp,hostname)) nomem();
  if (dns_ip6_qualify(&addresses,&fqdn,&tmp) == -1)
    strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": ");
  if (addresses.len < 16)
    strerr_die3x(111,FATAL,"no IP address for ",hostname);

  if (addresses.len == 16) {
    ctimeout[0] += ctimeout[1];
    ctimeout[1] = 0;
  }

  for (cloop = 0;cloop < 2;++cloop) {
    if (!stralloc_copys(&moreaddresses,"")) nomem();
    for (j = 0;j + 16 <= addresses.len;j += 4) {
      s = socket_tcp6();
      if (s == -1)
        strerr_die2sys(111,FATAL,"unable to create socket: ");
      if (socket_bind6(s,iplocal,portlocal,netif) == -1)
        strerr_die2sys(111,FATAL,"unable to bind socket: ");
      if (timeoutconn6(s,addresses.s + j,portremote,ctimeout[cloop],netif) == 0)
        goto CONNECTED;
      close(s);
      if (!cloop && ctimeout[1] && (errno == error_timeout)) {
	if (!stralloc_catb(&moreaddresses,addresses.s + j,16)) nomem();
      }
      else {
        strnum[fmt_ulong(strnum,portremote)] = 0;
	if (ip6_isv4mapped(addresses.s+j))
	  ipstr[ip4_fmt(ipstr,addresses.s + j + 12)] = 0;
	else
	  ipstr[ip6_fmt(ipstr,addresses.s + j)] = 0;
        strerr_warn5(CONNECT,ipstr," port ",strnum,": ",&strerr_sys);
      }
    }
    if (!stralloc_copy(&addresses,&moreaddresses)) nomem();
  }

  _exit(111);



  CONNECTED:

  if (!flagdelay)
    socket_tcpnodelay(s); /* if it fails, bummer */

  if (socket_local6(s,iplocal,&portlocal,&netif) == -1)
    strerr_die2sys(111,FATAL,"unable to get local address: ");

  if (!forcev6 && (ip6_isv4mapped(iplocal) || byte_equal(iplocal,16,V6any)))
    fakev4=1;

  if (!pathexec_env("PROTO",fakev4?"TCP":"TCP6")) nomem();

  strnum[fmt_ulong(strnum,portlocal)] = 0;
  if (!pathexec_env("TCPLOCALPORT",strnum)) nomem();
  if (fakev4)
    ipstr[ip4_fmt(ipstr,iplocal+12)] = 0;
  else
    ipstr[ip6_fmt(ipstr,iplocal)] = 0;
  if (!pathexec_env("TCPLOCALIP",ipstr)) nomem();

  x = forcelocal;
  if (!x)
    if (dns_name6(&tmp,iplocal) == 0) {
      if (!stralloc_0(&tmp)) nomem();
      x = tmp.s;
    }
  if (!pathexec_env("TCPLOCALHOST",x)) nomem();

  if (socket_remote6(s,ipremote,&portremote,&netif) == -1)
    strerr_die2sys(111,FATAL,"unable to get remote address: ");

  strnum[fmt_ulong(strnum,portremote)] = 0;
  if (!pathexec_env("TCPREMOTEPORT",strnum)) nomem();
  if (fakev4)
    ipstr[ip4_fmt(ipstr,ipremote+12)] = 0;
  else
    ipstr[ip6_fmt(ipstr,ipremote)] = 0;
  if (!pathexec_env("TCPREMOTEIP",ipstr)) nomem();
  if (verbosity >= 2)
    strerr_warn4("tcpclient: connected to ",ipstr," port ",strnum,0);

  x = 0;
  if (flagremotehost)
    if (dns_name6(&tmp,ipremote) == 0) {
      if (!stralloc_0(&tmp)) nomem();
      x = tmp.s;
    }
  if (!pathexec_env("TCPREMOTEHOST",x)) nomem();

  x = 0;
  if (flagremoteinfo)
    if (remoteinfo6(&tmp,ipremote,portremote,iplocal,portlocal,itimeout,netif) == 0) {
      if (!stralloc_0(&tmp)) nomem();
      x = tmp.s;
    }
  if (!pathexec_env("TCPREMOTEINFO",x)) nomem();

  if (fd_move(6,s) == -1)
    strerr_die2sys(111,FATAL,"unable to set up descriptor 6: ");
  if (fd_copy(7,6) == -1)
    strerr_die2sys(111,FATAL,"unable to set up descriptor 7: ");
  sig_uncatch(sig_pipe);
 
  pathexec(argv);
  strerr_die4sys(111,FATAL,"unable to run ",*argv,": ");
}
Esempio n. 20
0
void main(int argc,char **argv)
{
  char *sender;
  char *def;
  char *local;
  char *action;
  int flaginheader;
  int flagcomment;
  int flaggoodfield;
  int flagdone;
  int fd, fdlock;
  int match;
  const char *err;
  char encin = '\0';
  unsigned int start,confnum;
  unsigned int pos,i;
  int child;
  int opt;
  char *cp,*cpnext,*cpfirst,*cplast,*cpafter;

  (void) umask(022);
  sig_pipeignore();
  when = now();

  if (!stralloc_copys(&sendopt,"-")) die_nomem();
  opt = getconfopt(argc,argv,options,1,&dir);

  sender = get_sender();
  if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER));
  local = env_get("LOCAL");
  if (!local) strerr_die2x(100,FATAL,MSG(ERR_NOLOCAL));
  def = env_get("DEFAULT");
  if (!def) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT));

  if (!*sender)
    strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
  if (!sender[str_chr(sender,'@')])
    strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
  if (str_equal(sender,"#@[]"))
    strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));

  /* local should be >= def, but who knows ... */
  cp = local + str_len(local) - str_len(def) - 2;
  if (cp < local) die_badformat();
  action = local + byte_rchr(local,cp - local,'-');
  if (action == cp) die_badformat();
  action++;

  if (!action[0]) die_badformat();
  if (!str_start(action,ACTION_ACCEPT) && !str_start(action,ACTION_REJECT))
    die_badformat();
  start = str_chr(action,'-');
  if (!action[start]) die_badformat();
  confnum = 1 + start + str_chr(action + start + 1,'.');
  if (!action[confnum]) die_badformat();
  confnum += 1 + str_chr(action + confnum + 1,'.');
  if (!action[confnum]) die_badformat();
  if (!stralloc_copyb(&fnbase,action+start+1,confnum-start-1)) die_nomem();
  if (!stralloc_0(&fnbase)) die_nomem();
  cookie(hash,key.s,key.len,fnbase.s,"","a");
  if (byte_diff(hash,COOKIE,action+confnum+1))
    die_badformat();

  fdlock = lockfile("mod/lock");

  switch(checkfile(fnbase.s)) {
    case 0:
      strerr_die2x(100,FATAL,MSG(ERR_MOD_TIMEOUT));
    case -1:			/* only error if new request != action taken */
      if (str_start(action,ACTION_ACCEPT))
        strerr_die2x(0,INFO,MSG(ERR_MOD_ACCEPTED));
      else
        strerr_die2x(100,FATAL,MSG(ERR_MOD_ACCEPTED));
    case -2:
      if (str_start(action,ACTION_REJECT))
        strerr_die2x(0,INFO,MSG(ERR_MOD_REJECTED));
      else
        strerr_die2x(100,FATAL,MSG(ERR_MOD_REJECTED));
    default:
      break;
  }
/* Here, we have an existing filename in fnbase with the complete path */
/* from the current dir in fnmsg. */

  if (str_start(action,ACTION_REJECT)) {

    if (qmail_open(&qq, (stralloc *) 0) == -1)
      strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE));


				/* Build recipient from msg return-path */
    fd = open_read(fnmsg.s);
    if (fd == -1) {
      if (errno != error_noent)
        strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fnmsg.s));
      else
        strerr_die2x(100,FATAL,MSG(ERR_MOD_TIMEOUT));
    }
    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));

    if (getln(&sstext,&line,&match,'\n') == -1 || !match)
      strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
    maketo();			/* extract SENDER from return-path */
						/* Build message */
    hdr_add2s("Mailing-List: ",MSG(TXT_MAILING_LIST));
    if (listid.len > 0)
      hdr_add2("List-ID: ",listid.s,listid.len);
    hdr_datemsgid(when);
    hdr_from("-owner");
    if (replyto)
      hdr_add2s("Reply-To: ",replyto);
    hdr_add2s("To: ",to.s);
    hdr_subject(MSG(SUB_RETURNED_POST));

    if (flagmime) {
      hdr_mime(CTYPE_MULTIPART);
      hdr_boundary(0);
      hdr_ctype(CTYPE_TEXT);
      hdr_transferenc();
    }
    copy(&qq,"text/top",flagcd);
    copy(&qq,"text/mod-reject",flagcd);

    flagcomment = 0;
    flaginheader = 1;
    if (!stralloc_copys(&text,"")) die_nomem();
    if (!stralloc_ready(&text,1024)) die_nomem(); 
    for (;;) {		/* copy moderator's rejection comment */
      if (getln(subfdin,&line,&match,'\n') == -1)
        strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
      if (!match) break;
      if (flaginheader) {
        if (case_startb(line.s,line.len,"Content-Transfer-Encoding:")) {
          pos = 26;
          while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;
          if (case_startb(line.s+pos,line.len-pos,"base64"))
            encin = 'B';
          else if (case_startb(line.s+pos,line.len-pos,"quoted-printable"))
            encin = 'Q';
        }
        if (line.len == 1)
          flaginheader = 0;
      } else
        if (!stralloc_cat(&text,&line)) die_nomem();
    }	/* got body */
    if (encin) {
      if (encin == 'B')
        decodeB(text.s,text.len,&line);
      else
        decodeQ(text.s,text.len,&line);
      if (!stralloc_copy(&text,&line)) die_nomem();
    }
    cp = text.s;
    cpafter = text.s + text.len;
    if (!stralloc_copys(&line,"\n>>>>> -------------------- >>>>>\n"))
			die_nomem();
    flaggoodfield = 0;
    flagdone = 0;
    while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) {
      i = byte_chr(cp,cpnext-cp,'%');
      if (i <= 5 && cpnext-cp-i >= 3) {
				/* max 5 "quote characters" and space for %%% */
        if (cp[i+1] == '%' && cp[i+2] == '%') {
          if (!flaggoodfield) {					/* Start tag */
            if (!stralloc_copyb(&quoted,cp,i)) die_nomem();	/* quote chars*/
            flaggoodfield = 1;
            cp = cpnext + 1;
            cpfirst = cp;
            continue;
          } else {						/* end tag */
            if (flagdone)	/* 0 no comment lines, 1 comment line */
              flagdone = 2;	/* 2 at least 1 comment line & end tag */
            break;
          }
        }
      }
      if (flaggoodfield) {
        cplast = cpnext - 1;
        if (*cplast == '\r')	/* CRLF -> '\n' for base64 encoding */
          *cplast = '\n';
        else
          ++cplast;
			/* NUL is now ok, so the test for it was removed */
        flagdone = 1;
        i = cplast - cp + 1;
        if (quoted.len && quoted.len <= i &&
		!str_diffn(cp,quoted.s,quoted.len)) {	/* quote chars */
          if (!stralloc_catb(&line,cp+quoted.len,i-quoted.len)) die_nomem();
        } else
          if (!stralloc_catb(&line,cp,i)) die_nomem();	/* no quote chars */
      }
      cp = cpnext + 1;
    }
    if (flagdone == 2) {
    if (!stralloc_cats(&line,"<<<<< -------------------- <<<<<\n")) die_nomem();
      code_qput(line.s,line.len);
    }
    if (flagcd == 'B') {
      encodeB("",0,&line,2);
      qmail_put(&qq,line.s,line.len);
    }
    if (flagmime) {
      hdr_boundary(0);
      hdr_ctype(CTYPE_MESSAGE);
    }
    qmail_puts(&qq,"\n");
    if (seek_begin(fd) == -1)
      strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,fnmsg.s));

    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
    if (qmail_copy(&qq,&sstext,-1) != 0)
      strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnmsg.s));
    close(fd);

    if (flagmime)
      hdr_boundary(1);

    if (!stralloc_copy(&line,&outlocal)) die_nomem();
    if (!stralloc_cats(&line,"-return-@")) die_nomem();
    if (!stralloc_cat(&line,&outhost)) die_nomem();
    if (!stralloc_0(&line)) die_nomem();
    qmail_from(&qq,line.s);
    if (to.len)
      qmail_to(&qq,to.s);

    if (!stralloc_copys(&fnnew,"mod/rejected/")) die_nomem();
    if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
    if (!stralloc_0(&fnnew)) die_nomem();

/* this is strictly to track what happended to a message to give informative */
/* messages to the 2nd-nth moderator that acts on the same message. Since    */
/* this isn't vital we ignore errors. Also, it is no big ideal if unlinking  */
/* the old file fails. In the worst case it gets acted on again. If we issue */
/*  a temp error the reject will be redone, which is slightly worse.         */

    if (*(err = qmail_close(&qq)) == '\0') {
        fd = open_trunc(fnnew.s);
        if (fd != -1)
          close(fd);
        unlink(fnmsg.s);
        strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
        strerr_die2x(0,"ezmlm-moderate: info: qp ",strnum);
    } else
        strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1);

  } else if (str_start(action,ACTION_ACCEPT)) {
        fd = open_read(fnmsg.s);
        if (fd == -1) {
          if (errno !=error_noent)
            strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fnmsg.s));
          else	/* shouldn't happen since we've got lock */
            strerr_die3x(100,FATAL,fnmsg.s,MSG(ERR_MOD_TIMEOUT));
	}

    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
				/* read "Return-Path:" line */
    if (getln(&sstext,&line,&match,'\n') == -1 || !match)
      strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
    maketo();			/* extract SENDER to "to" */
    env_put2("SENDER",to.s);	/* set SENDER */
    if (seek_begin(fd) == -1)	/* rewind, since we read an entire buffer */
      strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,fnmsg.s));

    if ((child = wrap_fork()) == 0) {
      close(0);
      dup(fd);	/* make fnmsg.s stdin */
      if (argc > opt + 1)
	wrap_execvp((const char **)argv + opt);
      else if (argc > opt)
        wrap_execsh(argv[opt]);
      else
        wrap_execbin("/ezmlm-send", &sendopt, dir);
    }
      /* parent */
      close(fd);
      wrap_exitcode(child);
      if (!stralloc_copys(&fnnew,"mod/accepted/")) die_nomem();

      if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
      if (!stralloc_0(&fnnew)) die_nomem();
/* ignore errors */
      fd = open_trunc(fnnew.s);
      if (fd != -1)
        close(fd);
      unlink(fnmsg.s);
      _exit(0);
   }
}
Esempio n. 21
0
int std_subscribe(const char *dir,
		  const char *subdir,
		  const char *userhost,
		  int flagadd,
		  const char *comment,
		  const char *event,
		  int forcehash)
/* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database  */
/* dbname. Comment is e.g. the subscriber from line or name. It is added to  */
/* the log. Event is the action type, e.g. "probe", "manual", etc. The       */
/* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0   */
/* on failure. If flagmysql is set and the file "sql" is found in the        */
/* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
/* >=0 it is used in place of the calculated hash. This makes it possible to */
/* add addresses with a hash that does not exist. forcehash has to be 0..99. */
/* for unsubscribes, the address is only removed if forcehash matches the    */
/* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
/* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
/* used for sublist addresses (to avoid removal) and sublist aliases (to     */
/* prevent users from subscribing them (although the cookie mechanism would  */
/* prevent the resulting duplicate message from being distributed. */
{
  int fdlock;

  unsigned int j;
  unsigned char ch,lcch;
  int match;
  int flagwasthere;

  if (userhost[str_chr(userhost,'\n')])
    strerr_die2x(100,FATAL,ERR_ADDR_NL);

    if (!stralloc_copys(&addr,"T")) die_nomem();
    if (!stralloc_cats(&addr,userhost)) die_nomem();
    if (addr.len > 401)
      strerr_die2x(100,FATAL,ERR_ADDR_LONG);

    j = byte_rchr(addr.s,addr.len,'@');
    if (j == addr.len)
      strerr_die2x(100,FATAL,ERR_ADDR_AT);
    case_lowerb(addr.s + j + 1,addr.len - j - 1);
    if (!stralloc_copy(&lcaddr,&addr)) die_nomem();
    case_lowerb(lcaddr.s + 1,j - 1);	/* make all-lc version of address */

    if (forcehash >= 0 && forcehash <= 52) {
      ch = lcch = 64 + (unsigned char) forcehash;
    } else {
      ch = 64 + subhashsa(&addr);
      lcch = 64 + subhashsa(&lcaddr);
    }

    if (!stralloc_0(&addr)) die_nomem();
    if (!stralloc_0(&lcaddr)) die_nomem();
    std_makepath(&fn,dir,subdir,"/subscribers/",lcch);
    std_makepath(&fnlock,dir,subdir,"/lock",0);

    if (!stralloc_copyb(&fnnew,fn.s,fn.len-1)) die_nomem();
	/* code later depends on fnnew = fn + 'n' */
    if (!stralloc_cats(&fnnew,"n")) die_nomem();
    if (!stralloc_0(&fnnew)) die_nomem();

    fdlock = lockfile(fnlock.s);

				/* do lower case hashed version first */
    fdnew = open_trunc(fnnew.s);
    if (fdnew == -1) die_write();
    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));

    flagwasthere = 0;

    fd = open_read(fn.s);
    if (fd == -1) {
      if (errno != error_noent) { close(fdnew); die_read(); }
    }
    else {
      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));

      for (;;) {
        if (getln(&ss,&line,&match,'\0') == -1) {
	  close(fd); close(fdnew); die_read();
        }
        if (!match) break;
        if (line.len == addr.len)
          if (!case_diffb(line.s,line.len,addr.s)) {
	    flagwasthere = 1;
	    if (!flagadd)
	      continue;
	  }
        if (substdio_bput(&ssnew,line.s,line.len) == -1) {
	  close(fd); close(fdnew); die_write();
        }
      }

      close(fd);
    }

    if (flagadd && !flagwasthere)
      if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
        close(fdnew); die_write();
      }

    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); }
    if (fsync(fdnew) == -1) { close(fdnew); die_write(); }
    close(fdnew);

    if (rename(fnnew.s,fn.s) == -1)
      strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": ");

    if ((ch == lcch) || flagwasthere) {
      close(fdlock);
      if (flagadd ^ flagwasthere) {
        if (!stralloc_0(&addr)) die_nomem();
        logaddr(dir,subdir,event,addr.s+1,comment);
        return 1;
      }
      return 0;
    }

			/* If unsub and not found and hashed differ, OR */
			/* sub and not found (so added with new hash) */
			/* do the 'case-dependent' hash */

    fn.s[fn.len - 2] = ch;
    fnnew.s[fnnew.len - 3] = ch;
    fdnew = open_trunc(fnnew.s);
    if (fdnew == -1) die_write();
    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));

    fd = open_read(fn.s);
    if (fd == -1) {
      if (errno != error_noent) { close(fdnew); die_read(); }
    } else {
      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));

      for (;;) {
        if (getln(&ss,&line,&match,'\0') == -1)
          { close(fd); close(fdnew); die_read(); }
        if (!match) break;
        if (line.len == addr.len)
          if (!case_diffb(line.s,line.len,addr.s)) {
            flagwasthere = 1;
            continue;	/* always want to remove from case-sensitive hash */
          }
        if (substdio_bput(&ssnew,line.s,line.len) == -1)
          { close(fd); close(fdnew); die_write(); }
      }

      close(fd);
    }

    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); }
    if (fsync(fdnew) == -1) { close(fdnew); die_write(); }
    close(fdnew);

    if (rename(fnnew.s,fn.s) == -1)
      strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": ");

    close(fdlock);
    if (flagadd ^ flagwasthere) {
      if (!stralloc_0(&addr)) die_nomem();
      logaddr(dir,subdir,event,addr.s+1,comment);
      return 1;
    }
    return 0;

}
Esempio n. 22
0
static int mangleurl(stralloc* tag,const char* baseurl) {
  char* x;
  const char* y;
  static stralloc before,arg,after,tmp;
  int found;
  struct stat ss;
  found=0;
  if (stralloc_istag(tag,"a") || stralloc_istag(tag,"link"))
    found=1;
  else if (stralloc_istag(tag,"img") || stralloc_istag(tag,"frame"))
    found=2;
  if (!found) return 0;
  if (extractparam(tag,found==1?"href":"src",&before,&arg,&after)) {
    if (stralloc_starts(&arg,"/") ||
	stralloc_starts(&arg,"http://") ||
	stralloc_starts(&arg,"https://")) {
      canonicalize(&arg,baseurl);
    } else
      return 0;	/* url was already relative */
    if (stralloc_0(&arg)==0) return -1;
    stralloc_chop(&arg);
    x=arg.s+7; if (*x=='/') ++x;
    y=baseurl+7; if (*y=='/') ++y;

    /* now x is something like
      * "www.spiegel.de/img/0,1020,525770,00.jpg"
      * and baseurl is something like
      * "www.spiegel.de/panorama/0,1518,378421,00.html"
      * and we want to change x into "../img/0,1020,525770,00.jpg" */
    if (stat(x,&ss)!=0) return 0;

    for (;;) {
      int i=str_chr(x,'/');
      int j=str_chr(y,'/');
      if (i>0 && i==j && byte_equal(x,i,y)) {
	x+=i+1;
	y+=i+1;
	while (*x=='/') ++x;
	while (*y=='/') ++y;
      } else
	break;
    }
    stralloc_zero(&tmp);
    for (;;) {
      int i=str_chr(y,'/');
      if (y[i]=='/') {
	y+=i+1;
	while (*y=='/') ++y;
	if (stralloc_cats(&tmp,"../")==0) return -1;
      } else
	break;
    }
    {
      int i,needquote;
      for (i=needquote=0; x[i]; ++i)
	if (!isalnum(x[i]) && x[i]!='/' && x[i]!='_' && x[i]!='.') needquote=1;
      if (needquote) {
	if (stralloc_cats(&before,"\"")==0 ||
	    stralloc_cat(&before,&tmp)==0 ||
	    stralloc_cats(&before,x)==0 ||
	    stralloc_cats(&before,"\"")==0) return -1;
      } else
	if (stralloc_cat(&before,&tmp)==0 ||
	    stralloc_cats(&before,x)==0) return -1;
    }
    if (stralloc_cat(&before,&after)==0) return -1;
    if (stralloc_copy(tag,&before)==0) return -1;
  }
  return 0;
}
Esempio n. 23
0
static void do_ed(char *action)
{
  datetime_sec u;
  int flaggoodfield;
  int fd;
  char *x, *y;
  char *cp,*cplast,*cpnext,*cpafter;
  int flagdone;
  unsigned int len;
  const char *fname;
  unsigned int i;

  x = action + LENGTH_ED;
  x += scan_ulong(x,&u);
  if ((u > when) || (u < when - 100000)) die_cookie();
  if (*x == '.') ++x;
  fname = x;
  x += str_chr(x,'.');
  if (!*x) die_cookie();
  *x = (char) 0;
  ++x;
  stralloc_copys(&fnedit,"text/");
  stralloc_cats(&fnedit,fname);
  stralloc_0(&fnedit);
  y = fnedit.s + 5;		/* after "text/" */
  while (*++y) {		/* Name should be guaranteed by the cookie, */
				/* but better safe than sorry ... */
    if (((*y > 'z') || (*y < 'a')) && (*y != '_'))
      strerr_die2x(100,FATAL,MSG(ERR_BAD_NAME));
    if (*y == '_') *y = '-';
  }

  lock();			/* file must not change while here */

  switch (slurp(fnedit.s,&text,1024)) {
  case -1:
    strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnedit.s));
  case 0:
    strerr_die5x(100,FATAL,dir,"/",fnedit.s,MSG(ERR_NOEXIST));
  }
  stralloc_copy(&line,&text);

  subst_nuls(&line);

  stralloc_cat(&line,&fnedit);	/* including '\0' */
  strnum[fmt_ulong(strnum,(unsigned long) u)] = 0;
  cookie(hash,key.s,key.len,strnum,line.s,"-e");
  if (str_len(x) != COOKIE) die_cookie();
  if (byte_diff(hash,COOKIE,x)) die_cookie();
	/* cookie is ok, file exists, lock's on, new file ends in '_' */
  stralloc_copys(&fneditn,fnedit.s);
  stralloc_append(&fneditn,'_');
  stralloc_0(&fneditn);
  fd = open_trunc(fneditn.s);
  if (fd == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s));
  substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf));
  stralloc_copys(&quoted,"");	/* clear */
  stralloc_copys(&text,"");

  for (;;) {			/* get message body */
    if (getln(&ssin,&line,&match,'\n') == -1)
      strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
    if (!match) break;
    stralloc_cat(&text,&line);
  }
  if (encin) {			/* decode if necessary */
    if (encin == 'B')
      decodeB(text.s,text.len,&line);
    else
      decodeQ(text.s,text.len,&line);
    stralloc_copy(&text,&line);
  }
  cp = text.s;
  cpafter = text.s+text.len;
  flaggoodfield = 0;
  flagdone = 0;
  len = 0;
  while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) {
    i = byte_chr(cp,cpnext-cp,'%');
    if (i != (unsigned int) (cpnext - cp)) {
      if (!flaggoodfield) {	/* MSG(TXT_EDIT_START)/END */
	if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_START))) {
		/* start tag. Store users 'quote characters', e.g. '> ' */
	  stralloc_copyb(&quoted,cp,i);
	  flaggoodfield = 1;
	  cp = cpnext + 1;
	  continue;
	}
      } else
	if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_END))) {
	  flagdone = 1;
	  break;
	}
    }
    if (flaggoodfield) {
      if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT)
	strerr_die1x(100,MSG(ERR_EDSIZE));

      if (quoted.len && cpnext-cp >= (int) quoted.len &&
	  !str_diffn(cp,quoted.s,quoted.len))
	cp += quoted.len;	/* skip quoting characters */
      cplast = cpnext - 1;
      if (*cplast == '\r')	/* CRLF -> '\n' for base64 encoding */
	*cplast = '\n';
      else
	++cplast;
      if (substdio_put(&sstext,cp,cplast-cp+1) == -1)
	strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s));
    }
    cp = cpnext + 1;
  }
  if (!flagdone)
    strerr_die2x(100,FATAL,MSG(ERR_NO_MARK));
  if (substdio_flush(&sstext) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s));
  if (fsync(fd) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_SYNC,fneditn.s));
  if (fchmod(fd, 0600) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_CHMOD,fneditn.s));
  if (close(fd) == -1)
    strerr_die2sys(111,FATAL,MSG1(ERR_CLOSE,fneditn.s));
  wrap_rename(fneditn.s,fnedit.s);

  unlock();
  hdr_subject(MSG1(SUB_EDIT_SUCCESS,fname));
  hdr_ctboundary();
  copy(&qq,"text/top",flagcd);
  copy_act("text/edit-done");
  copybottom(0);
  qmail_to(&qq,sender);		/* not necessarily from mod */
}
Esempio n. 24
0
static void do_edit(const char *action)
{
  unsigned int i;
  unsigned int len;
  char ch;

  /* only remote admins and only if -e is specified may edit */
  if (!flagedit || remote.s == 0)
    strerr_die2x(100,FATAL,MSG(ERR_NOT_AVAILABLE));
  if (!ismod)
    strerr_die2x(100,FATAL,MSG(ERR_NOT_ALLOWED));
  len = str_len(ACTION_EDIT);
  if (!case_starts(action,ACTION_EDIT))
    len = str_len(ALT_EDIT);
  if (action[len]) {			/* -edit.file, not just -edit */
    if (action[len] != '.')
      strerr_die2x(100,FATAL,MSG(ERR_BAD_REQUEST));
    showsend2("edit ",action+len+1);
    stralloc_copys(&fnedit,"text/");
    stralloc_cats(&fnedit,action+len+1);
    stralloc_0(&fnedit);
    case_lowerb(fnedit.s,fnedit.len);
    i = 5;	/* after the "text/" */
    while ((ch = fnedit.s[i++])) {
      if (((ch > 'z') || (ch < 'a')) && (ch != '_'))
	strerr_die2x(100,FATAL,MSG(ERR_BAD_NAME));
      if (ch == '_') fnedit.s[i-1] = '-';
    }
    switch(slurp(fnedit.s,&text,1024)) {	/* entire file! */
    case -1:
      strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnedit.s));
    case 0:
      strerr_die5x(100,FATAL,dir,"/",fnedit.s,MSG(ERR_NOEXIST));
    }
    stralloc_copy(&line,&text);

    subst_nuls(&line);

    stralloc_cat(&line,&fnedit);	/* including '\0' */
    strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
    cookie(hash,key.s,key.len,strnum,line.s,"-e");
    stralloc_copy(&confirm,&outlocal);
    stralloc_append(&confirm,'-');
    stralloc_catb(&confirm,ACTION_ED,LENGTH_ED);
    stralloc_cats(&confirm,strnum);
    stralloc_append(&confirm,'.');
		/* action part has been checked for bad chars */
    stralloc_cats(&confirm,action + len + 1);
    stralloc_append(&confirm,'.');
    stralloc_catb(&confirm,hash,COOKIE);
    stralloc_append(&confirm,'@');
    stralloc_cat(&confirm,&outhost);
    stralloc_0(&confirm);
    set_cpconfirm(confirm.s,outlocal.len);
    set_cpaction(ACTION_ED);
    set_cphash(hash);
    set_cpwhen(when);

    qmail_puts(&qq,"Reply-To: ");
    quote2(&quoted,confirm.s);
    qmail_put(&qq,quoted.s,quoted.len);
    qmail_puts(&qq,"\n");

    hdr_subject(MSG1(SUB_EDIT_REQUEST,action+len+1));
    hdr_ctboundary();
    copy(&qq,"text/top",flagcd);
    copy(&qq,"text/edit-do",flagcd);
    (void) code_qputs(MSG(TXT_EDIT_START));
    (void) code_qput(text.s,text.len);
    (void) code_qputs(MSG(TXT_EDIT_END));

  } else {	/* -edit only, so output list of editable files */
    hdr_subject(MSG(SUB_EDIT_LIST));
    hdr_ctboundary();
    copy(&qq,"text/top",flagcd);
    copy_act("text/edit-list");
  }
  qmail_puts(&qq,"\n\n");
  copybottom(0);
  qmail_to(&qq,mod.s);
}
Esempio n. 25
0
void main(int argc,char **argv)
{
  DIR *bouncedir, *bsdir, *hdir;
  direntry *d, *ds;
  unsigned long bouncedate;
  unsigned long bouncetimeout = BOUNCE_TIMEOUT;
  unsigned long lockout = 0L;
  unsigned long ld;
  unsigned long ddir,dfile;
  int fdlock,fd;
  int opt;
  char ch;

  (void) umask(022);
  sig_pipeignore();
  when = (unsigned long) now();
  while ((opt = getopt(argc,argv,"dDl:t:vV")) != opteof)
    switch(opt) {
      case 'd': flagdig = 1; break;
      case 'D': flagdig = 0; break;
      case 'l':
                if (optarg) {	/* lockout in seconds */
                  (void) scan_ulong(optarg,&lockout);
                }
                break;
      case 't':
                if (optarg) {	/* bouncetimeout in days */
                  (void) scan_ulong(optarg,&bouncetimeout);
                  bouncetimeout *= 3600L * 24L;
                }
                break;
      case 'v':
      case 'V': strerr_die2x(0, "ezmlm-warn version: ",auto_version);
      default:
	die_usage();
    }
  startup(dir = argv[optind]);
  load_config(dir);
  getconf_ulong(&copylines,"copylines",0,dir);
  if (flagdig) {
    if (!stralloc_copys(&digdir,dir)) die_nomem();
    if (!stralloc_cats(&digdir,"/digest")) die_nomem();
    if (!stralloc_0(&digdir)) die_nomem();
    workdir = digdir.s;
  } else
    workdir = dir;

  if (!stralloc_copys(&fnlastd,workdir)) die_nomem();
  if (!stralloc_cats(&fnlastd,"/bounce/lastd")) die_nomem();
  if (!stralloc_0(&fnlastd)) die_nomem();
  if (slurp(fnlastd.s,&lastd,16) == -1)		/* last time d was scanned */
      strerr_die4sys(111,FATAL,ERR_READ,fnlastd.s,": ");
  if (!stralloc_0(&lastd)) die_nomem();
  (void) scan_ulong(lastd.s,&ld);
  if (!lockout)
    lockout = bouncetimeout / 50;		/* 5.6 h for default timeout */
  if (ld + lockout > when && ld < when)
    _exit(0);		/* exit silently. Second check is to prevent lockup */
			/* if lastd gets corrupted */

  if (!stralloc_copy(&fnlasth,&fnlastd)) die_nomem();
  fnlasth.s[fnlasth.len - 2] = 'h';		/* bad, but feels good ... */

  if (flagdig)
    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();

  ddir = when / 10000;
  dfile = when - 10000 * ddir;

  if (!stralloc_copys(&line,workdir)) die_nomem();
  if (!stralloc_cats(&line,"/lockbounce")) die_nomem();
  if (!stralloc_0(&line)) die_nomem();
  fdlock = lockfile(line.s);

  if (!stralloc_copys(&line,workdir)) die_nomem();
  if (!stralloc_cats(&line,"/bounce/d")) die_nomem();
  if (!stralloc_0(&line)) die_nomem();
  bouncedir = opendir(line.s);
  if (!bouncedir) {
    if (errno != error_noent)
      strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
    else
      _exit(0);		/* no bouncedir - no bounces! */
  }

  while ((d = readdir(bouncedir))) {		/* dxxx/ */
    if (str_equal(d->d_name,".")) continue;
    if (str_equal(d->d_name,"..")) continue;

    scan_ulong(d->d_name,&bouncedate);
	/* since we do entire dir, we do files that are not old enough. */
	/* to not do this and accept a delay of 10000s (2.8h) of the oldest */
	/* bounce we add to bouncedate. We don't if bouncetimeout=0 so that */
	/* that setting still processes _all_ bounces. */
    if (bouncetimeout) ++bouncedate;
    if (when >= bouncedate * 10000 + bouncetimeout) {
      if (!stralloc_copys(&bdname,workdir)) die_nomem();
      if (!stralloc_cats(&bdname,"/bounce/d/")) die_nomem();
      if (!stralloc_cats(&bdname,d->d_name)) die_nomem();
      if (!stralloc_0(&bdname)) die_nomem();
      bsdir = opendir(bdname.s);
      if (!bsdir) {
	if (errno != error_notdir)
	  strerr_die4sys(111,FATAL,ERR_OPEN,bdname.s,":y ");
	else {				/* leftover nnnnn_dmmmmm file */
	  if (unlink(bdname.s) == -1)
	    strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": ");
	  continue;
	}
      }
      while ((ds = readdir(bsdir))) {			/* dxxxx/yyyy */
	if (str_equal(ds->d_name,".")) continue;
	if (str_equal(ds->d_name,"..")) continue;
	if (!stralloc_copy(&fn,&bdname)) die_nomem();	/* '\0' at end */
	  fn.s[fn.len - 1] = '/';
	if (!stralloc_cats(&fn,ds->d_name)) die_nomem();
	if (!stralloc_0(&fn)) die_nomem();
	if ((ds->d_name[0] == 'd') || (ds->d_name[0] == 'w'))
	  doit(ds->d_name[0] == 'w');
        else				/* other stuff is junk */
	  if (unlink(fn.s) == -1)
	    strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
      }
      closedir(bsdir);
      if (rmdir(bdname.s) == -1)	/* the directory itself */
      if (errno != error_noent)
	   strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": ");
    }
  }
  closedir(bouncedir);

  if (!stralloc_copy(&line,&fnlastd)) die_nomem();
  line.s[line.len - 2] = 'D';
  fd = open_trunc(line.s);			/* write lastd. Do safe */
						/* since we read before lock*/
  if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
  if (substdio_put(&ssout,strnum,fmt_ulong(strnum,when)) == -1)
    strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": ");
  if (substdio_put(&ssout,"\n",1) == -1)	/* prettier */
    strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": ");
  if (substdio_flush(&ssout) == -1)
    strerr_die4sys(111,FATAL,ERR_FLUSH,line.s,": ");
  if (fsync(fd) == -1)
    strerr_die4sys(111,FATAL,ERR_SYNC,line.s,": ");
  if (close(fd) == -1)
    strerr_die4sys(111,FATAL,ERR_CLOSE,line.s,": ");

  if (rename(line.s,fnlastd.s) == -1)
    strerr_die4sys(111,FATAL,ERR_MOVE,fnlastd.s,": ");

				/* no need to do h dir cleaning more than */
				/* once per 1-2 days (17-30 days for all) */
  if (stat(fnlasth.s,&st) == -1) {
    if (errno != error_noent)
      strerr_die4sys(111,FATAL,ERR_STAT,fnlasth.s,": ");
  } else if (when < (unsigned long)st.st_mtime + 100000
	     && when > (unsigned long)st.st_mtime)
    _exit(0);			/* 2nd comp to guard against corruption */

  if (slurp(fnlasth.s,&lasth,16) == -1)		/* last h cleaned */
      strerr_die4sys(111,FATAL,ERR_READ,fnlasth.s,": ");
  if (!stralloc_0(&lasth)) die_nomem();
  ch = lasth.s[0];				 /* clean h */
  if (ch >= 'a' && ch <= 'o')
    ++ch;
  else
    ch = 'a';
  lasth.s[0] = ch;
  if (!stralloc_copys(&line,workdir)) die_nomem();
  if (!stralloc_cats(&line,"/bounce/h/")) die_nomem();
  if (!stralloc_catb(&line,lasth.s,1)) die_nomem();
  if (!stralloc_0(&line)) die_nomem();
  hdir = opendir(line.s);		/* clean ./h/xxxxxx */

  if (!hdir) {
    if (errno != error_noent)
    strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
  } else {

    while ((d = readdir(hdir))) {
      if (str_equal(d->d_name,".")) continue;
      if (str_equal(d->d_name,"..")) continue;
      if (!stralloc_copys(&fn,line.s)) die_nomem();
      if (!stralloc_append(&fn,"/")) die_nomem();
      if (!stralloc_cats(&fn,d->d_name)) die_nomem();
      if (!stralloc_0(&fn)) die_nomem();
      if (stat(fn.s,&st) == -1) {
	if (errno == error_noent) continue;
	strerr_die4sys(111,FATAL,ERR_STAT,fn.s,": ");
      }
      if (when > st.st_mtime + 3 * bouncetimeout)
	if (unlink(fn.s) == -1)
          strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
    }
    closedir(hdir);
  }

  fd = open_trunc(fnlasth.s);			/* write lasth */
  if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
  if (substdio_put(&ssout,lasth.s,1) == -1)
    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
  if (substdio_put(&ssout,"\n",1) == -1)	/* prettier */
    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
  if (substdio_flush(&ssout) == -1)
    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
  (void) close(fd);		/* no big loss. No reason to flush/sync */
				/* See check of ld above to guard against */
				/* it being corrupted and > when */

  closesub();
  _exit(0);
}
Esempio n. 26
0
void doit(int flagw)
{
  unsigned int i;
  int fd;
  int match;
  int fdhash;
  const char *err;

  fd = open_read(fn.s);
  if (fd == -1) die_read();
  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));

  if (getln(&ssin,&addr,&match,'\0') == -1) die_read();
  if (!match) { close(fd); return; }
  if (!issub(workdir,0,addr.s)) { close(fd); /*XXX*/unlink(fn.s); return; }
  cookie(hash,"",0,"",addr.s,"");
  if (!stralloc_copys(&fnhash,workdir)) die_nomem();
  if (!stralloc_cats(&fnhash,"/bounce/h/")) die_nomem();
  if (!stralloc_catb(&fnhash,hash,1)) die_nomem();
  if (!stralloc_cats(&fnhash,"/h")) die_nomem();
  if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem();
  if (!stralloc_0(&fnhash)) die_nomem();

  if (qmail_open(&qq, (stralloc *) 0) == -1)
    strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);

  hdr_add2("Mailing-List: ",mailinglist.s,mailinglist.len);
  if (listid.len > 0)
    hdr_add2("\nList-ID: ",listid.s,listid.len);
  hdr_datemsgid(now());
  if (flagcd) {
    if (!stralloc_0(&line)) die_nomem();
  }
  hdr_from("-help");
  if (!quote2(&quoted,addr.s)) die_nomem();
  hdr_add2("To: ",quoted.s,quoted.len);
  /* to accomodate transfer-encoding */
  hdr_mime(flagcd ? CTYPE_MULTIPART : CTYPE_TEXT);
  hdr_listsubject1(flagw ? "probe from " : "warning from ");

  if (flagcd) {			/* first part for QP/base64 multipart msg */
    hdr_boundary(0);
    hdr_ctype(CTYPE_TEXT);
    hdr_transferenc();
  } else
    qmail_puts(&qq,"\n");

  copy(&qq,"text/top",flagcd);
  copy(&qq,flagw ? "text/bounce-probe" : "text/bounce-warn",flagcd);

  if (!flagw) {
    if (flagdig)
      copy(&qq,"text/dig-bounce-num",flagcd);
    else
      copy(&qq,"text/bounce-num",flagcd);
    if (!flagcd) {
      fdhash = open_read(fnhash.s);
      if (fdhash == -1) {
        if (errno != error_noent)
          strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": ");
      } else {
        substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf));
        for(;;) {
          if (getln(&sstext,&line,&match,'\n') == -1)
            strerr_die4sys(111,FATAL,ERR_READ,fnhash.s,": ");
          if (!match) break;
          code_qput(line.s,line.len);
        }
      }
      close(fdhash);
    } else {
      if (!stralloc_copys(&line,"")) die_nomem();	/* slurp adds! */
      if (slurp(fnhash.s,&line,256) < 0)
        strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": ");
      code_qput(line.s,line.len);
    }
  }

  copy(&qq,"text/bounce-bottom",flagcd);
  if (flagcd) {
    if (flagcd == 'B') {
      encodeB("",0,&line,2);
      qmail_put(&qq,line.s,line.len);	/* flush */
    }
    hdr_boundary(0);
    hdr_ctype(CTYPE_MESSAGE);
    qmail_puts(&qq,"\n");
  }
  if (qmail_copy(&qq,&ssin,copylines) < 0) die_read();
  close(fd);

  if (flagcd)				/* end multipart/mixed */
    hdr_boundary(1);

  strnum[fmt_ulong(strnum,when)] = 0;
  cookie(hash,key.s,key.len,strnum,addr.s,flagw ? "P" : "W");
  if (!stralloc_copy(&line,&outlocal)) die_nomem();
  if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-"))
	die_nomem();
  if (!stralloc_cats(&line,strnum)) die_nomem();
  if (!stralloc_cats(&line,".")) die_nomem();
  if (!stralloc_catb(&line,hash,COOKIE)) die_nomem();
  if (!stralloc_cats(&line,"-")) die_nomem();
  i = str_chr(addr.s,'@');
  if (!stralloc_catb(&line,addr.s,i)) die_nomem();
  if (addr.s[i]) {
    if (!stralloc_cats(&line,"=")) die_nomem();
    if (!stralloc_cats(&line,addr.s + i + 1)) die_nomem();
  }
  if (!stralloc_cats(&line,"@")) die_nomem();
  if (!stralloc_cat(&line,&outhost)) die_nomem();
  if (!stralloc_0(&line)) die_nomem();
  qmail_from(&qq,line.s);

  qmail_to(&qq,addr.s);
  if (*(err = qmail_close(&qq)) != '\0')
    strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE, err + 1);

  strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
  strerr_warn2("ezmlm-warn: info: qp ",strnum,0);

  if (!flagw) {
    if (unlink(fnhash.s) == -1)
      if (errno != error_noent)
        strerr_die4sys(111,FATAL,ERR_DELETE,fnhash.s,": ");
  }
  if (unlink(fn.s) == -1)
    strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
}
Esempio n. 27
0
int main(int argc,char **argv)
{
  char *action;
  const char *err;
  unsigned int i;
  int act = AC_NONE;	/* desired action */
  unsigned int actlen = 0;/* str_len of above */

  (void) umask(022);
  sig_pipeignore();
  when = now();

  getconfopt(argc,argv,options,1,&dir);
  initsub(0);

  sender = get_sender();
  if (!sender) die_sender();
  action = env_get("DEFAULT");
  if (!action) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT));

  if (!*sender)
    strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
  if (!sender[str_chr(sender,'@')])
    strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
  if (str_equal(sender,"#@[]"))
    strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));

  action = set_workdir(action);

  stralloc_copys(&target,sender);
  if (action[0]) {
    i = str_chr(action,'-');
    if (action[i]) {
      action[i] = 0;
      stralloc_copys(&target,action + i + 1);
      i = byte_rchr(target.s,target.len,'=');
      if (i < target.len)
	target.s[i] = '@';
    }
  }
  stralloc_0(&target);
  set_cptarget(target.s);	/* for copy() */
  make_verptarget();

  act = get_act_ismod(action,&actlen);

  stralloc_copy(&from,&outlocal);
  stralloc_cats(&from,"-return-@");
  stralloc_cat(&from,&outhost);
  stralloc_0(&from);

  if (qmail_open(&qq) == -1)
    strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE));
  msg_headers(act);

  if (act == AC_SUBSCRIBE)
    do_subscribe(action);
  else if (act == AC_SC)
    do_sc(action);
  else if (str_start(action,ACTION_RC))
    do_rc_tc(action,ACTION_RC);
  else if(str_start(action,ACTION_TC))
    do_rc_tc(action,ACTION_TC);
  else if (act == AC_UNSUBSCRIBE)
    do_unsubscribe(action);
  else if (str_start(action,ACTION_UC))
    do_uc(action);
  else if (str_start(action,ACTION_VC))
    do_vc_wc(action,ACTION_VC);
  else if (str_start(action,ACTION_WC))
    do_vc_wc(action,ACTION_WC);
  else if (act == AC_LIST || act == AC_LISTN) 
    do_list(act);
  else if (act == AC_LOG)
    do_log(action,actlen);
  else if (act == AC_EDIT)
    do_edit(action);
  else if (str_start(action,ACTION_ED))
    do_ed(action);
  else if (act == AC_GET)
    do_get(action);
  else if (case_starts(action,ACTION_QUERY) ||
		case_starts(action,ALT_QUERY))
    do_query();
  else if (case_starts(action,ACTION_INFO) ||
		case_starts(action,ALT_INFO))
    do_info();
  else if (case_starts(action,ACTION_FAQ) ||
		case_starts(action,ALT_FAQ))
    do_faq();
  else if (ismod && (act == AC_HELP))
    do_mod_help();
  else
    do_help();

  err = qmail_close(&qq);
  closesub();
  if (*err != '\0')
    strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1);
  strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
  strerr_die3x(0,INFO,"qp ",strnum);
}
Esempio n. 28
0
static int get_act_ismod(const char *action,unsigned int *actlen)
{
  int act;

  if (case_equals(action,ACTION_LISTN) ||
		case_equals(action,ALT_LISTN))
    act = AC_LISTN;
  else if (case_equals(action,ACTION_LIST) ||
		case_equals(action,ALT_LIST))
    act = AC_LIST;
  else if (case_starts(action,ACTION_GET) ||
		case_starts(action,ALT_GET))
    act = AC_GET;
  else if (case_equals(action,ACTION_HELP) ||
		case_equals(action,ALT_HELP))
    act = AC_HELP;
  else if (case_starts(action,ACTION_EDIT) ||
		case_starts(action,ALT_EDIT))
    act = AC_EDIT;
  else if (case_starts(action,ACTION_LOG))
    { act = AC_LOG; *actlen = str_len(ACTION_LOG); }
  else if (case_starts(action,ALT_LOG))
    { act = AC_LOG; *actlen = str_len(ALT_LOG); }
  else
    act = AC_NONE;

  if (modsub.s != 0 || remote.s != 0) {
    if (modsub.len) {
      stralloc_copy(&moddir,&modsub);
    } else if (remote.len) {
      stralloc_copy(&moddir,&remote);
    } else {
      stralloc_copys(&moddir,"mod");
    }
    stralloc_0(&moddir);
		/* for these the reply is 'secret' and goes to sender  */
		/* This means that they can be triggered from a SENDER */
		/* that is not a mod, but never send to a non-mod */
    if (act == AC_NONE || flagdig == FLD_DENY)	/* None of the above */
      ismod = issub(moddir.s,sender,&mod);
				/* sender = moderator? */
    else
      ismod = issub(moddir.s,target.s,&mod);
				/* target = moderator? */
   } else
     ismod = 0;			/* always 0 for non-mod/remote lists */
				/* if DIR/public is missing, we still respond*/
				/* to requests from moderators for remote    */
				/* admin and modsub lists. Since ismod   */
				/* is false for all non-mod lists, only it   */
				/* needs to be tested. */

  if (!flagpublic && !(ismod && remote.s != 0) &&
                !case_equals(action,ACTION_HELP))
      strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC));

  if (flagdig == FLD_DENY)
    if (!ismod || remote.s == 0)	/* only mods can do */
      strerr_die1x(100,MSG(ERR_NOT_ALLOWED));

  if (act == AC_NONE) {		/* none of the above */
    if (case_equals(action,ACTION_SUBSCRIBE) ||
		case_equals(action,ALT_SUBSCRIBE))
      act = AC_SUBSCRIBE;
    else if (case_equals(action,ACTION_UNSUBSCRIBE)
		|| case_equals(action,ALT_UNSUBSCRIBE))
      act = AC_UNSUBSCRIBE;
    else if (str_start(action,ACTION_SC))
      act = AC_SC;
  }
  return act;
}
Esempio n. 29
0
void copymsg(int fd,char format)
/* Copy archive message "msg" itself from open file handle fd, in "format" */
{
  int match;
  int flaginheader;
  int flagskipblanks;
  int flaggoodfield;

  switch(format) {
    case VIRGIN:
    case NATIVE:
      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
      for (;;) {
        if (getln(&sstext,&line,&match,'\n') == -1)
           strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s));
        if (match) {
           qmail_put(&qq,line.s,line.len);
	   msgsize += line.len;
        } else
           break;
      }
      break;
    case MIME:
    case MIXED:
      flaginheader = 1;
      flaggoodfield = 0;
      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
      for (;;) {
        if (getln(&sstext,&line,&match,'\n') == -1)
           strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s));
        if (match) {
          if (flaginheader) {
            if (line.len == 1) {
              flaginheader = 0;
              flaggoodfield = 1;
            } else if (line.s[0] != ' ' && line.s[0] != '\t') {
              flaggoodfield = 0;
              if (constmap(&digheadersmap,line.s,
			byte_chr(line.s,line.len,':')))
                flaggoodfield = 1;
            }
            if (flaggoodfield) {
              qmail_put(&qq,line.s,line.len);		/* header */
	      msgsize += line.len;
	    }
          } else {
            qmail_put(&qq,line.s,line.len);		/* body */
	    msgsize += line.len;
	  }
        } else
          break;
      }
      break;
    case RFC1153:		/* Not worth optimizing. Rarely used */
      flaginheader = 1;
      flagskipblanks = 1;	/* must skip terminal blanks acc to rfc1153 */
      archtype = ' ';		/* rfc1153 requires ordered headers */
      if (!stralloc_copys(&archblanklines,"")) die_nomem();
      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
      for (;;) {
        if (getln(&sstext,&line,&match,'\n') == -1)
           strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s));
        if (match) {
          if (flaginheader) {
            if (line.len == 1) {
              flaginheader = 0;
              if (archdate.len) {
                qmail_put(&qq,archdate.s,archdate.len);
                archdate.len = 0;
		msgsize += archdate.len;
              }
              if (archto.len) {
                qmail_put(&qq,archto.s,archto.len);
		msgsize += archto.len;
                archto.len = 0;
              }
              if (archfrom.len) {
                qmail_put(&qq,archfrom.s,archfrom.len);
		msgsize += archfrom.len;
                archfrom.len = 0;
              }
              if (archcc.len) {
                qmail_put(&qq,archcc.s,archcc.len);
		msgsize += archcc.len;
                archcc.len = 0;
              }
              if (archsubject.len) {
                qmail_put(&qq,archsubject.s,archsubject.len);
		msgsize += archsubject.len;
                archsubject.len = 0;
              }
              if (archmessageid.len) {
                qmail_put(&qq,archmessageid.s,archmessageid.len);
		msgsize += archmessageid.len;
                archmessageid.len = 0;
              }
              if (archkeywords.len) {
                qmail_put(&qq,archkeywords.s,archkeywords.len);
		msgsize += archkeywords.len;
                archkeywords.len = 0;
              }
              qmail_puts(&qq,"\n");
            } else if (line.s[0] == ' ' || line.s[0] == '\t') {
              switch (archtype) {	/* continuation lines */
                case ' ':
                  break;
                case 'D':
                  if (!stralloc_cat(&archdate,&line)) die_nomem(); break;
                case 'F':
                  if (!stralloc_cat(&archfrom,&line)) die_nomem(); break;
                case 'T':
                  if (!stralloc_cat(&archto,&line)) die_nomem(); break;
                case 'C':
                  if (!stralloc_cat(&archcc,&line)) die_nomem(); break;
                case 'S':
                  if (!stralloc_cat(&archsubject,&line)) die_nomem(); break;
                case 'M':
                  if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break;
                case 'K':
                  if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break;
                default:
                  strerr_die2x(111,FATAL,
                      "Program error: Bad archive header type");
              }
            } else {
              archtype = ' ';
              if (case_startb(line.s,line.len,"cc:")) {
                archtype='C';
                if (!stralloc_copy(&archcc,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"date:")) {
                archtype='D';
                if (!stralloc_copy(&archdate,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"from:")) {
                archtype='F';
                if (!stralloc_copy(&archfrom,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"keywords:")) {
                archtype='K';
                if (!stralloc_copy(&archkeywords,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"message-id:")) {
                archtype='M';
                if (!stralloc_copy(&archmessageid,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"subject:")) {
                archtype='S';
                if (!stralloc_copy(&archsubject,&line)) die_nomem();
              }
              else if (case_startb(line.s,line.len,"to:")) {
                archtype='T';
                if (!stralloc_copy(&archto,&line)) die_nomem();
              }
            }
          } else if (line.len == 1) {
            if (!flagskipblanks)
              if (!stralloc_copys(&archblanklines,"\n")) die_nomem();
          } else {
            if (archblanklines.len) {
              qmail_put(&qq,archblanklines.s,archblanklines.len);
              archblanklines.len = 0;
            }
            flagskipblanks = 0;
            qmail_put(&qq,line.s,line.len);
	    msgsize += line.len;
          }
        } else
          break;
      }
      break;
    default:
      strerr_die2x(100,FATAL,"Program error: bad format in copymsg()");
  }
}
Esempio n. 30
0
void main(int argc,char **argv)
{
  char *def;
  char *local;
  const char *action = "";
  char *psz;
  const char *err;
  int fd;
  unsigned int i,j;
  int flagremote;
  int match;
  int goodexit = 0;			/* exit code for normal exit */
					/* in manager this will be set to 0 */
  unsigned long from,u,to,issue,prevmax;
  unsigned long mno = 0;
  unsigned long chunk;
  unsigned long subs = 0;
  unsigned int pos,pos1;
  unsigned int len;
  int opt;
  char outformat = 0;
  msgentry *msgtable;
  subentry *subtable;
  authentry *authtable;
  dateentry *datetable;
  struct datetime dt;
  char date[DATE822FMT];

  (void) umask(022);
  sig_pipeignore();
  when = now();
  datetime_tai(&dt,when);

  opt = getconfopt(argc,argv,options,1,0);

  initsub(0);
  if (flagformat != 0)
    if (FORMATS[str_chr(FORMATS,flagformat[0])])
      outformat = flagformat[0];
  if (outformat == 0) {
    outformat =
      (getconf_line(&line,"digformat",0)
       && FORMATS[str_chr(FORMATS,line.s[0])])
      ? line.s[0]
      : DEFAULT_FORMAT;
  }

  /* code to activate digest (-digest-code)*/
  if ((digestcode = argv[opt]) == 0) {
    if (getconf_line(&digestcodefile,"digestcode",0)
	&& digestcodefile.len > 0) {
      if (!stralloc_0(&digestcodefile)) die_nomem();
      digestcode = digestcodefile.s;
    }
  }
  /* ignore any extra args */

  if (!stralloc_copy(&subject,&outlocal)) die_nomem();	/* for subjects */
  if (!stralloc_copy(&listname,&outlocal)) die_nomem();	/* for content disp */

  local = env_get("LOCAL");
  def = env_get("DEFAULT");
  sender = get_sender();
  if (local && *local) {	/* in editor local = outlocal */
    if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER));
    if (!*sender)
      strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
    if (str_equal(sender,"#@[]"))
      strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
    if (!sender[str_chr(sender,'@')])
      strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
    if (def) {
      if (*def) {
	action = def;
	goodexit = 99;
      } else
	_exit(0);		/* list-@host should do -help from manager */
    } else {			/* editor */
      act = AC_DIGEST;		/* on list-@host ! */
      flageditor = 1;		/* to avoid Mailing-list error on sublists */
				/* when running out of dir/editor. */
    }
    if (case_starts(action,"dig")) {
      action += 3;
      if (action[0] == '-' || action [0] == '.') {
        action++;
	if (!digestcode)
            strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE));
        len = str_len(digestcode);
        if (len <= str_len(action) && case_startb(action,len,digestcode)) {
          if (FORMATS[str_chr(FORMATS,*(action+len))])
            outformat = *(action+len);
          act = AC_DIGEST;
        } else
          strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE));
      }
    }
  } else			/* Command line operation */
    act = AC_DIGEST;

	/* Things we deal with. If anything else just die with success!   */
	/* At the moment this is -index, -thread, and -get.               */
	/* If flagdo = 0 we only service -dig commands. This is to support*/
	/* "secret" lists that are still archived and digested. -c on     */
	/* cmd line. */

  if (act == AC_NONE) {
    if (case_equals(action,ACTION_DIGEST)) {
      act = AC_GET;		/* list-digest@ => msg since last digest */
      action = ACTION_GET;
    } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET))
      act = AC_GET;
    else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX))
      act = AC_INDEX;
    else if (case_starts(action,ACTION_THREAD) ||
	 case_starts(action,ALT_THREAD))
      act = AC_THREAD;
  }
  if (act == AC_NONE)			/* not for us. Pass the buck. */
    _exit(0);
  if (act != AC_INDEX) {		/* need to do header processing */
    if(!getconf(&digheaders,"digheaders",0)) {
      if(!stralloc_copys(&digheaders,digsz)) die_nomem();
      if (!stralloc_0(&digheaders)) die_nomem();
      psz = digheaders.s;
      while (*psz) {
        if (*psz == '\\') *psz = '\0';
        ++psz;
      }
    }
    if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0))
	die_nomem();
  }
  if (act != AC_DIGEST) {
    if (!flagdo)			/* only do digests */
      strerr_die2x(100,FATAL,MSG(ERR_NOCMD));
    if (flagpublic < 0)
      flagpublic = !getconf_isset("modgetonly") && getconf_isset("public");
    if (!flagpublic) {
		/* This all to take care of non-public lists. They should*/
		/* still do digests, but do other things only for        */
		/* moderators that have remote access. Since this is rare*/
		/* efforts have been made to keep everything that's not  */
		/* needed elsewhere in here.                   */
      getconf_line(&moddir,"modsub",0);
      flagremote = getconf_line(&line,"remote",0);
      if (!flagremote)
        strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC));
      if (!moddir.len) {
        if (line.len) {
          if (!stralloc_copy(&moddir,&line)) die_nomem();
        } else {
          if (!stralloc_copys(&moddir,"mod")) die_nomem();
        }
      }
      if (!stralloc_0(&moddir)) die_nomem();
      ismod = issub(moddir.s,sender,&mod);
      if (!ismod)			/* sender = moderator? */
        strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC));
    }
  }

  if (act == AC_DIGEST) {
    workdir = "digest";
    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
    if (getconf_line(&line,"chunk",0)) {
      if (!stralloc_0(&line)) die_nomem();
      (void) scan_ulong(line.s,&chunk);		/* same chunk as main list */
      if (chunk == 0)				/* limit range to 1-53 */
	chunk = 1L;
      else if (chunk > 52)
	chunk = 52L;
    } else {
      chunk = 0L;
    }
  } else
    workdir = ".";


  if (!flagarchived)
    strerr_die2x(100,FATAL,MSG(ERR_NOT_ARCHIVED));

  if (qmail_open(&qq) == -1)
    strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE));

  set_cpnum("");	/* default for <#n#> replacement */

  switch (act) {

  case AC_DIGEST:

/* -dig{.|-}'digestcode'[f] returns an rfc1153 digest                        */
/* of messages from the archive. Messages                                    */
/* dignum+1 through the last message received by the list are processed and  */
/* dignum is updated to the last message processed. digissue is advanced.    */

    get_num();				/* max = last successful message */
    to = max;
    lockup();			/* another digest could corrupt dignum */
				/* but will be saved only if flagdigrange==0 */
    if(getconf_line(&num,"dignum",0)) {
      if(!stralloc_0(&num)) die_nomem();
      pos = scan_ulong(num.s,&prevmax);
      if (num.s[pos] == ':') pos++;
      pos += 1 + scan_ulong(num.s+pos,&cumsize);	/* last cumsize */
      if (num.s[pos] == ':') pos++;
      scan_ulong(num.s+pos,&digwhen);			/* last reg dig */
    } else {
      prevmax = 0L;
      cumsize = 0L;
      digwhen = 0L;
    }
    mno = prevmax + 1L;
    if(!max || mno > max)	/* if a digest-list is "sending" the request, */
				/* don't make noise: errors go to postmaster!*/
      strerr_die2x(goodexit,FATAL,MSG(ERR_EMPTY_DIGEST));
    szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0';
    set_cpnum(szmsgnum);	/* for copy */
				/* prepare subject to get entropy for tagmsg*/
    if (!stralloc_cats(&subject," Digest ")) die_nomem();
    if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1))
          die_nomem();		/* skip trailing in date '\n' */
    if (!stralloc_cats(&subject," Issue ")) die_nomem();
    if (getconf_line(&num,"digissue",0)) {
      if(!stralloc_0(&num)) die_nomem();
      scan_ulong(num.s,&issue);
      issue++;
    } else {
      issue = 1;
    }
    if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue)))
      die_nomem();
					/* use the subject as entropy */
    if (!stralloc_copy(&line,&subject)) die_nomem();
    if (!stralloc_0(&line)) die_nomem();

    if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem();
    seed.len = HASHLEN + 1;
    seed.s[HASHLEN] = '\0';
    makehash(line.s,line.len,seed.s);
    if (chunk) {			/* only if slaves are used */
      qmail_puts(&qq,"Ezauth: ");
      qmail_put(&qq,seed.s,HASHLEN);
      qmail_puts(&qq,"\n");
    }

    doheaders();
    qmail_puts(&qq,"To: ");
    if (!quote(&quoted,&listname)) die_nomem();
    qmail_put(&qq,quoted.s,quoted.len);
    qmail_puts(&qq,"@");
    qmail_put(&qq,outhost.s,outhost.len);
    qmail_puts(&qq,"\n");
    if (flagindexed && (outformat != NATIVE))
      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
	mno,to,max,flaglocked);
    else
      idx_mklist(&msgtable,&subtable,&authtable,mno,to);
    digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat);

    write_ulong(issue,0L,0L,"digissue","digissuen");
    write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn");
    break;

  case AC_GET:

/* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted   */
/* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET.  */

    zapnonsub(ACTION_GET);		/* restrict to subs if requested */
    tosender();
				/* for rfc1153 */
    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();

    to = 0;
    pos = str_len(ACTION_GET);
    if (!case_starts(action,ACTION_GET))
      pos = str_len(ALT_GET);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];
       ++pos;
    }
					/* optional - or . after '-get' */
    if (action[pos] == '-' || action[pos] == '.') pos++;
    get_num();				/* max = last successful message */
					/* accept any separator. It may be  */
					/* the terminal '\n', but then      */
					/* scan will = 0 on the \0 so should*/
					/* be safe                          */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/
    doheaders();
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);
    if (u == 0 && to == 0) {		/* default: messages since last */
					/* digest, or last MAXGET if too many */
      to= max;
      u = dignum();
      if (u == 0) {		/* no digest => last up to HISTGET msgs */
	to = max;
	if (max > HISTGET) u = max - HISTGET; else u = 1;
      }
      if (to - u >= MAXGET) u = to - MAXGET + 1;	/* max MAXGET */
    } else if (u > max) {
      if (to) {			/* -get.999999_x returns 30 and msg since last*/
	to = max;		/* digest 30*/
        u = dignum();
	if (u > HISTGET) u -= HISTGET; else u = 1;
        if (to - u >= MAXGET) u = to - MAXGET + 1;
      } else
	u = max;
    }
    if (u == 0) u = 1;			/* -get.5 => 1-5 */
    if (to < u) to = u;			/* -get23_2 => 23 */
    if (to >= u + MAXGET) to = u + MAXGET - 1;
					/* no more than MAXGET at a time */
    if (to > max) to = max;
    if (flagindexed && (outformat != NATIVE))	/* fake out threading */
      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
	u,to,max,0);
    else
      idx_mklist(&msgtable,&subtable,&authtable,u,to);
    digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat);
    break;

  case AC_INDEX:

/* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/
/* sets of 100.                                                              */
/* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */
/* limited to num+MAXINDEX to limit the amount of data sent.                 */

    if (!flagindexed)
      strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED));
    zapnonsub(ACTION_INDEX);	/* restrict to subs if requested */
    to = 0;
    pos = str_len(ACTION_INDEX);
    if (!case_starts(action,ACTION_INDEX))
      pos = str_len(ALT_INDEX);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];		/* ignored, but be nice ... */
       ++pos;
    }
    get_num();				/* max = last successful message */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/

    doheaders();
    tosender();
    if (!stralloc_cats(&subject," Result of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();
    presub(1,1,&subject,AC_INDEX,outformat);

    if (action[pos] == '-' || action[pos] == '.') pos++;
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);

    if (u == 0 && to == 0) { to = max; u = max - 100; }
    if (u <= 0) u = 1;
    if (u > max) u = max;
    if (to < u) to = u;
    if (to > u + MAXINDEX) to = u+MAXINDEX;	/* max MAXINDEX index files */
    if (to > max) to = max;
    u /= 100;
    to /= 100;
    while (u <= to) {
      if (!stralloc_copys(&fn,"archive/")) die_nomem();
      if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem();
      if (!stralloc_cats(&fn,"/index")) die_nomem();
      if (!stralloc_0(&fn)) die_nomem();

      if (u == max/100)	/* lock if last index file in archive */
        lockup();

      fd = open_read(fn.s);
      if (fd == -1)
        if (errno != error_noent)
          strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s));
        else
          code_qputs(MSG(TXT_NOINDEX));
      else {
        substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
        for (;;) {
          if (getln(&sstext,&line,&match,'\n') == -1)
            strerr_die2sys(111,FATAL,MSG1(ERR_READ,fn.s));
          if (match) {
            if (line.s[0] != '\t') {	/* subject line */
              pos = byte_chr(line.s,line.len,' ');
	      pos1 = 0;
	      if (pos && pos != line.len && line.s[pos - 1] == ':')
                pos1 = pos + HASHLEN + 1;	/* after hash */
              if (pos1 >= line.len) {	/* bad! */
                pos = 0;
                pos1 = 0;		/* output as is */
              }
              if (!stralloc_copyb(&line2,line.s,pos)) die_nomem();
              if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem();
            } else {
	      pos = byte_chr(line.s,line.len,';');
	      if (pos + HASHLEN + 1 < line.len && pos > 15 &&
				line.s[pos + 1] != ' ') {
		  if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem();
		  pos++;
		  if (!stralloc_catb(&line2,line.s + pos + HASHLEN,
			line.len - pos - HASHLEN)) die_nomem();
	      } else			/* old format - no author hash */
                if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem();
	    }
            code_qput(line2.s,line2.len);
          } else
            break;
        }
        close(fd);
      }

      if (u == max/100)	/* unlock if last index in archive file */
        unlock();

      u++;
    }
    normal_bottom(outformat);
    postmsg(outformat);
    break;

  case AC_THREAD:

/* -thread[f][-|.]num returns messages with subject matching message        */
/* 'num' in the subject index. If 'num' is not in[1..last_message] an error */
/* message is returned.                                                     */

    if (!flagindexed)
      strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED));

    zapnonsub(ACTION_THREAD);		/* restrict to subs if requested*/

    get_num();				/* max = last successful message */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/

    doheaders();
    tosender();
				/* for rfc1153 */
    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();

    to = 0;
    pos = str_len(ACTION_THREAD);
    if (!case_starts(action,ACTION_THREAD))
      pos = str_len(ALT_THREAD);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];
       ++pos;
    }
    if (action[pos] == '-' || action[pos] == '.') pos++;
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);

    if(u == 0 || u > max) {
      hdr_add2("Subject: ",subject.s,subject.len);
      qmail_puts(&qq,"\n");
      copy(&qq,"text/get-bad",flagcd);
    } else {	/* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */
      if (u > THREAD_BEFORE)
        from = u-THREAD_BEFORE;
      else
        from = 1L;
      if (u + THREAD_AFTER > max) {
        idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0);
        digest(msgtable,subtable,authtable,from,max,&subject,
		AC_THREAD,outformat);
      } else {
        idx_mkthread(&msgtable,&subtable,&authtable,
		from,u+THREAD_AFTER,u,max,0);
        digest(msgtable,subtable,authtable,from,u+THREAD_AFTER,
			&subject,AC_THREAD,outformat);
      }
    }
    break;

  default:
	/* This happens if the initial check at the beginning of 'main'    */
	/* matches something that isn't matched here. Conversely, just     */
	/* adding an action here is not enough - it has to be added to the */
	/* initial check as well.                                          */

    strerr_die2x(100,FATAL,
		 "Program error: I'm supposed to deal with this but I didn't");
  }

  if (!stralloc_copy(&line,&outlocal)) die_nomem();
  if (act == AC_DIGEST) {
    if (chunk) {
      if (!stralloc_cats(&line,"-return-g-")) die_nomem();
    } else
      if (!stralloc_cats(&line,"-return-")) die_nomem();
    strnum[fmt_ulong(strnum,mno)] = '\0';
    if (!stralloc_cats(&line,strnum)) die_nomem();
    if (!stralloc_cats(&line,"-@")) die_nomem();

    if (!stralloc_cat(&line,&outhost)) die_nomem();
    if (!stralloc_cats(&line,"-@[]")) die_nomem();
  } else {
    if (!stralloc_cats(&line,"-return-@")) die_nomem();
    if (!stralloc_cat(&line,&outhost)) die_nomem();
  }
  if (!stralloc_0(&line)) die_nomem();

  qmail_from(&qq,line.s);
  if (act == AC_DIGEST) {	 /* Do recipients */
    tagmsg(mno,seed.s,"d",hashout,qq.msgbytes,chunk);
    if (chunk) {
      if (!stralloc_copys(&line,"T")) die_nomem();
      if (!stralloc_cat(&line,&outlocal)) die_nomem();
      if (!stralloc_cats(&line,"-s-d-")) die_nomem();
      if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem();
      if (!stralloc_cats(&line,"-")) die_nomem();
      if (!stralloc_cats(&line,strnum)) die_nomem();
      if (!stralloc_cats(&line,"-")) die_nomem();
      if (!stralloc_copys(&line2,"@")) die_nomem();
      if (!stralloc_cat(&line2,&outhost)) die_nomem();
      if (!stralloc_0(&line2)) die_nomem();
      j = 0;
      for (i = 0; i <= 52; i += chunk) {		/* To slaves */
        qmail_put(&qq,line.s,line.len);
        schar[0] = '0' + i / 10;
        schar[1] = '0' + (i % 10);
        qmail_put(&qq,schar,3);
        j += (chunk - 1);
        if (j > 52) j = 52;
        schar[0] = '0' + j / 10;
        schar[1] = '0' + (j % 10);
        qmail_put(&qq,schar,2);
        qmail_put(&qq,line2.s,line2.len);
      }
    } else
      subs = putsubs(workdir,0L,52L,&subto);
  } else {			/* if local is set, sender is checked */
    if (ismod)
      qmail_to(&qq,mod.s);
    else
      qmail_to(&qq,sender);
  }

  if (*(err = qmail_close(&qq)) == '\0') {	/* Done. Skip rest. */
    if (act == AC_DIGEST) {
      if (chunk)
	(void) logmsg(mno,0L,0L,2);
      else
        (void) logmsg(mno,0L,subs,4);
    }
    closesub();			/* close db connection */
    unlock();			/* NOP if nothing locked */
    strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
    strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum);
  } else {			/* failed. Reset last msg & issue for digest */
    if(act == AC_DIGEST) {
      issue--;
      write_ulong(issue,0L,0L,"digissue","digissuen");
      write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn");
    }
    unlock();			/* NOP if nothing locked */
    strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1);
  }
}