text: #if !defined(lint) && !defined(DOS)
  static char rcsid[] = "$Id: rpdump.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
  #endif
  
  /*
   * ========================================================================
   * Copyright 2013-2019 Eduardo Chappa
   * Copyright 2006-2008 University of Washington
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * ========================================================================
   */
  
  #include "headers.h"
  #include "radio.h"		/* OE_PASSWD */
  #include "../pith/util.h"	/* IS_REMOTE() */
  #include "../pith/remote.h"	/* REMOTE_ABOOK_SUBTYPE... */
  
  
  typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType;
  
  
  int        parse_args(int, char **, int *, char **, char **);
  RemoteType check_for_header_msg(MAILSTREAM *);
  char      *ptype(RemoteType);
  char      *spechdr(RemoteType);
  char      *err_desc(int);
  int        opt_enter(char *, int, char *, int *);
  char      *last_cmpnt(char *);
  int        wantto(char *, int, int);
  
  
  char *ustr = "usage: %s [-f] -l Local_file  -r Remote_folder\n";
  int   noshow_error = 0;
  
  /* look for my_timer_period in pico directory for an explanation */
  int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
  
  
  #ifdef _WINDOWS
  
  #undef main
  
  app_main (argc, argv)
      int argc;
      char argv[];
  {
  }
  
  #endif /* _WINDOWS */
  
  
  /*
   *  rpdump [-f] -l Local_file -r Remote_folder
   *
   *             -f          (skip check for special header)
   *
   *   Note: We're not worrying about memory leaks.
   */
  int
  main(argc, argv)
      int   argc;
      char *argv[];
  {
      MAILSTREAM *stream = NULL;
      FILE       *fp;
      int         usage = 0;
      char        buf[10000];
      char       *local = NULL, *remote = NULL;
      int         force = 0;
      BODY       *body = NULL;
      char       *data, *p;
      RemoteType  rtype;
      unsigned long i;
      struct stat sbuf;
  
  #include "../c-client/linkage.c"
  
      if(parse_args(argc, argv, &force, &local, &remote)){
  	fprintf(stderr, ustr, argv[0]);
  	exit(-1);
      }
  
      if(!local || !*local){
  	fprintf(stderr, "No local file specified\n");
  	usage++;
      }
  
      if(!remote || !*remote){
  	fprintf(stderr, "No remote folder specified\n");
  	usage++;
      }
  
      if(usage){
  	fprintf(stderr, ustr, argv[0]);
  	exit(-1);
      }
  
      if(!IS_REMOTE(remote)){
  	fprintf(stderr,
  		"Remote folder name \"%s\" %s\n", remote,
  		(*remote != '{') ? "must begin with \"{\"" : "not valid");
  	exit(-1);
      }
  
      if(IS_REMOTE(local)){
  	fprintf(stderr, "Argument to -l (%s) must be a local filename", local);
  	exit(-1);
      }
  
  #ifdef	_WINDOWS
      if(stat(local, &sbuf))
  #else
      if(lstat(local, &sbuf))
  #endif
      {
  	if(errno == ENOENT){		/* File did not exist */
  	    int fd;
  
  	    /* create it */
  	    if(((fd = open(local, O_CREAT|O_EXCL|O_WRONLY,0600)) < 0)
  	       || (close(fd) != 0)){
  		fprintf(stderr, "%s: %s\n", local, err_desc(errno));
  		exit(-1);
  	    }
  
  	    /* now it exists! */
  	}
  	else{				/* unknown error */
  	    fprintf(stderr, "%s: %s\n", local, err_desc(errno));
  	    exit(-1);
  	}
      }
      else{				/* file exists */
  
  	/* is it a regular file? */
  #ifdef	S_ISREG
  	if(!S_ISREG(sbuf.st_mode))
  #else
  	if(!(S_IFREG & sbuf.st_mode))
  #endif
  	{
  	    fprintf(stderr, "Only allowed to write to regular local files. Try another filename.\n");
  	    exit(-1);
  	}
  
  	if(access(local, WRITE_ACCESS) == 0){
  
  	    snprintf(buf, sizeof(buf), "Local file \"%s\" exists, overwrite it",
  		    (p = last_cmpnt(local)) ? p : local);
  	    if(wantto(buf, 'n', 'n') != 'y'){
  		fprintf(stderr, "Dump cancelled\n");
  		exit(-1);
  	    }
  	}
  	else{
  	    fprintf(stderr, "Local file \"%s\" is not writable\n", local);
  	    exit(-1);
  	}
      }
  
      /*
       * Try opening the local file.
       */
      if((fp = fopen(local, "w")) == NULL){
  	fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno));
  	mail_close(stream);
  	exit(-1);
      }
  
      /*
       * Try opening the remote folder.
       */
      stream = mail_open(NULL, remote, OP_READONLY);
      if(!stream || stream->halfopen){
  	fprintf(stderr, "Remote folder \"%s\" is not readable\n", remote);
  	if(stream)
  	  mail_close(stream);
  
  	exit(-1);
      }
  
      if(stream->nmsgs >= 2){
  	/*
  	 * There is a first message already. Check to see if it is one of
  	 * our special header messages.
  	 */
  	rtype = check_for_header_msg(stream);
  	if(!force && rtype == NotSet){
  	    fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote data folder.\nUse -f to force.\n", remote);
  	    mail_close(stream);
  	    exit(-1);
  	}
      }
      else if(stream->nmsgs == 1){
  	fprintf(stderr, "No data in remote folder to copy (only 1 message)\n");
  	mail_close(stream);
  	exit(-1);
      }
      else{
  	fprintf(stderr, "No data in remote folder to copy\n");
  	mail_close(stream);
  	exit(-1);
      }
  
      if(!mail_fetchstructure(stream, stream->nmsgs, &body)){
  	fprintf(stderr, "Can't access remote IMAP data\n");
  	mail_close(stream);
  	exit(-1);
      }
  
      if(!body ||
         body->type != REMOTE_DATA_TYPE ||
         !body->subtype ||
         (!force && strucmp(body->subtype, spechdr(rtype)))){
  	fprintf(stderr, "Remote IMAP folder has wrong contents\n");
  	mail_close(stream);
  	exit(-1);
      }
      
      if(!mail_fetchenvelope(stream, stream->nmsgs)){
  	fprintf(stderr, "Can't access envelope in remote data\n");
  	mail_close(stream);
  	exit(-1);
      }
  
      data = mail_fetch_body(stream, stream->nmsgs, "1", &i, FT_PEEK);
  
      p = data;
      for(p = data; p < data+i; p++){
  
  	/* convert to unix newlines */
  	if(*p == '\r' && *(p+1) == '\n')
  	  p++;
  
  	if(putc(*p, fp) == EOF){
  	    fprintf(stderr,
  		    "Error writing \"%s\": %s\n", local, err_desc(errno));
  	    fclose(fp);
  	    mail_close(stream);
  	    exit(-1);
  	}
      }
  
      mail_close(stream);
      fclose(fp);
  
      fprintf(stderr,
  	    "Remote folder is of type \"%s\", contents saved to \"%s\"\n",
  	    ptype(rtype) ? ptype(rtype) : "unknown", local);
      exit(0);
  }
  
  
  RemoteType
  check_for_header_msg(stream)
      MAILSTREAM *stream;
  {
      STRINGLIST *sl;
      int         ret = NotSet;
      char       *h, *try;
      size_t      len;
      char       *pinerc, *abook, *sig, *smime;
  
      pinerc = spechdr(Pinerc);
      abook  = spechdr(Abook);
      sig    = spechdr(Sig);
      smime  = spechdr(Smime);
      
      len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime)));
  
      sl = mail_newstringlist();
      sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char));
      try = pinerc;
      strncpy((char *) sl->text.data, try, len);
      sl->text.data[len] = '\0';
      sl->text.size = strlen((char *) sl->text.data);
  
      if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
  
  	if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
  	  ret = Pinerc;
      }
  
      if(ret == NotSet){
  	try = abook;
  	strncpy((char *)sl->text.data, try, len);
  	sl->text.data[len] = '\0';
  	sl->text.size = strlen((char *) sl->text.data);
  	if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
  
  	    if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
  	      ret = Abook;
  	}
      }
  
      if(ret == NotSet){
  	try = sig;
  	strncpy((char *) sl->text.data, try, len);
  	sl->text.data[len] = '\0';
  	sl->text.size = strlen((char *) sl->text.data);
  	if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
  
  	    if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
  	      ret = Sig;
  	}
      }
  
      if(sl)
        mail_free_stringlist(&sl);
  
      if(pinerc)
        fs_give((void **)&pinerc);
      if(abook)
        fs_give((void **)&abook);
      if(sig)
        fs_give((void **)&sig);
      if(smime)
        fs_give((void **)&smime);
  
      return(ret);
  }
  
  
  char *
  ptype(rtype)
      RemoteType rtype;
  {
      char *ret = NULL;
  
      switch(rtype){
        case Pinerc:
          ret = cpystr("pinerc");
  	break;
        case Abook:
          ret = cpystr("abook");
  	break;
        case Sig:
          ret = cpystr("sig");
  	break;
        case Smime:
          ret = cpystr("smime");
  	break;
        default:
  	break;
      }
  
      return(ret);
  }
  
  
  char *
  spechdr(rtype)
      RemoteType rtype;
  {
      char *ret = NULL;
  
      switch(rtype){
        case Pinerc:
          ret = cpystr(REMOTE_PINERC_SUBTYPE);
  	break;
        case Abook:
          ret = cpystr(REMOTE_ABOOK_SUBTYPE);
  	break;
        case Sig:
          ret = cpystr(REMOTE_SIG_SUBTYPE);
  	break;
        case Smime:
          ret = cpystr(REMOTE_SMIME_SUBTYPE);
  	break;
        default:
  	break;
      }
  
      return(ret);
  }
  
  
  int
  parse_args(argc, argv, force, local, remote)
      int          argc;
      char       **argv;
      int         *force;
      char       **local, **remote;
  {
      int    ac;
      char **av;
      int    c;
      char  *str;
      int    usage = 0;
  
      ac = argc;
      av = argv;
  
        /* while more arguments with leading - */
  Loop: while(--ac > 0 && **++av == '-'){
  	/* while more chars in this argument */
  	while(*++*av){
  	    switch(c = **av){
  	      case 'h':
  		usage++;
  		break;
  	      case 'f':
  		(*force)++;
  		break;
  
  	      case 'r': case 'l':	/* string args */
  		if(*++*av)
  		  str = *av;
  		else if(--ac)
  		  str = *++av;
  		else{
  		    fprintf(stderr, "missing argument for flag \"%c\"\n", c);
  		    ++usage;
  		    goto Loop;
  		}
  
  		switch(c){
  		  case 'l':
  		    if(str)
  		      *local = str;
  
  		    break;
  		  case 'r':
  		    if(str)
  		      *remote = str;
  
  		    break;
  		}
  
  		goto Loop;
  
  		default:
  		  fprintf(stderr, "unknown flag \"%c\"\n", c);
  		  ++usage;
  		  break;
  	    }
  	 }
        }
  
      if(ac != 0)
        usage++;
  
      return(usage);
  }
  
  
  char *
  err_desc(err)
      int err;
  {
      return((char *) strerror(err));
  }
  
  
  void mm_exists(stream, number)
      MAILSTREAM *stream;
      unsigned long number;
  {
  }
  
  
  void mm_expunged(stream, number)
      MAILSTREAM *stream;
      unsigned long number;
  {
  }
  
  
  void mm_flags(stream, number)
      MAILSTREAM *stream;
      unsigned long number;
  {
  }
  
  
  void mm_list(stream, delim, name, attrib)
      MAILSTREAM *stream;
      int   delim;
      char *name;
      long attrib;
  {
  }
  
  
  void mm_lsub(stream, delimiter, name, attributes)
      MAILSTREAM *stream;
      int delimiter;
      char *name;
      long attributes;
  {
  }
  
  
  void mm_notify(stream, string, errflg)
      MAILSTREAM *stream;
      char *string;
      long errflg;
  {
      mm_log(string, errflg);
  }
  
  
  void mm_log(string, errflg)
      char *string;
      long errflg;
  {
      if(noshow_error)
        return;
  
      switch(errflg){  
        case BYE:
        case NIL:
  	break;
  
        case PARSE:
  	fprintf(stderr, "PARSE: %s\n", string);
  	break;
  
        case WARN:
  	fprintf(stderr, "WARN: %s\n", string);
  	break;
  
        case ERROR:
  	fprintf(stderr, "ERROR: %s\n", string);
  	break;
  
        default:
  	fprintf(stderr, "%s\n", string);
  	break;
      }
  }
  
  void mm_login_method(mb, user, pwd, trial, method)
      NETMBX *mb;
      char   *user;
      void   *pwd;
      long    trial;
      char   *method;
  {
      mm_login(mb, user, (char **) pwd, trial);
  }
  
  void mm_login(mb, user, pwd, trial)
      NETMBX *mb;
      char   *user;
      char   **pwd;
      long    trial;
  {
      char prompt[100], *last, tmp[MAILTMPLEN];
      int  i, j, goal, ugoal, len, rc, flags = 0;
  #define NETMAXPASSWD 100
  
      user[NETMAXUSER-1] = '\0';
  
      if(trial == 0L){
  	if(mb->user && *mb->user){
  	    strncpy(user, mb->user, NETMAXUSER);
  	    user[NETMAXUSER-1] = '\0';
  	}
      }
  
      if(!*mb->user){
  	/* Dress up long hostnames */
  	snprintf(prompt, sizeof(prompt), "%sHOST: ",
  		(mb->sslflag||mb->tlsflag) ? "+ " : "");
  	len = strlen(prompt);
  	/* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
  	goal = 80 - (len + 20 + MIN(15, 80/5));
  	last = "  ENTER LOGIN NAME: ";
  	if(goal < 9){
  	    last = " LOGIN: ";
  	    if((goal += 13) < 9){
  		last += 1;
  		goal = 0;
  	    }
  	}
  
  	if(goal){
  	    for(i = len, j = 0;
  		i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
  	      if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
  		  strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
  		  prompt[sizeof(prompt)-1] = '\0';
  		  break;
  	      }
  	}
  	else
  	  i = 0;
  
  	strncpy(&prompt[i], last, sizeof(prompt)-i);
  	prompt[sizeof(prompt)-1] = '\0';
  
  	while(1) {
  	    rc = opt_enter(user, NETMAXUSER, prompt, &flags);
  	    if(rc != 4)
  	      break;
  	}
  
  	if(rc == 1 || !user[0]) {
  	    user[0]   = '\0';
  	    *pwd = NULL;
  	}
      }
      else
        strncpy(user, mb->user, NETMAXUSER);
  
      user[NETMAXUSER-1] = '\0';
  //    pwd[NETMAXPASSWD-1] = '\0';
  
      if(!user[0])
        return;
  
  
      /* Dress up long host/user names */
      /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
      snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : "");
      len = strlen(prompt);
      goal  = strlen(mb->host);
      ugoal = strlen(user);
      if((i = 80 - (len + 8 + 18 + 6)) < 14){
  	goal = 0;		/* no host! */
  	if((i = 80 - (6 + 18 + 6)) <= 6){
  	    ugoal = 0;		/* no user! */
  	    if((i = 80 - (18 + 6)) <= 0)
  	      i = 0;
  	}
  	else{
  	    ugoal = i;		/* whatever's left */
  	    i     = 0;
  	}
      }
      else
        while(goal + ugoal > i)
  	if(goal > ugoal)
  	  goal--;
  	else
  	  ugoal--;
  
      if(goal){
  	snprintf(prompt, sizeof(prompt), "%sHOST: ",
  		(mb->sslflag||mb->tlsflag) ? "+ " : "");
  	for(i = len, j = 0;
  	    i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
  	  if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
  	      strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
  	      prompt[sizeof(prompt)-1] = '\0';
  	      break;
  	  }
      }
      else
        i = 0;
  
      if(ugoal){
  	strncpy(&prompt[i], &"  USER: "[i ? 0 : 2], sizeof(prompt)-i);
  	prompt[sizeof(prompt)-1] = '\0';
  	for(i += strlen(&prompt[i]), j = 0;
  	    i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++)
  	  if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){
  	      strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
  	      prompt[sizeof(prompt)-1] = '\0';
  	      break;
  	  }
      }
  
      strncpy(&prompt[i], &"  ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
      prompt[sizeof(prompt)-1] = '\0';
  
      tmp[0] = '\0';
      while(1) {
  	flags = OE_PASSWD;
          rc = opt_enter(tmp, NETMAXPASSWD, prompt, &flags);
  	if(rc != 4)
            break;
      }
  
      if(tmp[0]) *pwd = cpystr(tmp);
      if(rc == 1 || !tmp[0]) {
          user[0] = '\0';
          return;
      }
  }
  
  
  void mm_critical(stream)
      MAILSTREAM *stream;
  {
  }
  
  
  void mm_nocritical(stream)
      MAILSTREAM *stream;
  {
  }
  
  
  long mm_diskerror(stream, errcode, serious)
      MAILSTREAM *stream;
      long errcode;
      long serious;
  {
      return T;
  }
  
  
  void mm_fatal(string)
      char *string;
  {
      fprintf(stderr, "%s\n", string);
  }
  
  
  void mm_searched(stream, msgno)
      MAILSTREAM *stream;
      unsigned long msgno;
  {
  }
  
  
  void mm_status(stream, mailbox, status)
      MAILSTREAM *stream;
      char *mailbox;
      MAILSTATUS *status;
  {
  }
  
  void mm_dlog(string)
      char *string;
  {
      fprintf(stderr, "%s\n", string);
  }
  
  
  int
  opt_enter(string, field_len, prompt, flags)
       char       *string, *prompt;
       int         field_len;
       int	*flags;
  {
      char *pw;
      int   return_v = -10;
  
      while(return_v == -10){
  
  	if(flags && *flags & OE_PASSWD){
  	    if((pw = getpass(prompt)) != NULL){
  		if(strlen(pw) < field_len){
  		    strncpy(string, pw, field_len);
  		    string[field_len-1] = '\0';
  		    return_v = 0;
  		}
  		else{
  		    fputs("Password too long\n", stderr);
  		    return_v = -1;
  		}
  	    }
  	    else
  	      return_v = 1;	/* cancel? */
  	}
  	else{
  	    char *p;
  
  	    fputs(prompt, stdout);
  	    fgets(string, field_len, stdin);
  	    string[field_len-1] = '\0';
  	    if((p = strpbrk(string, "\r\n")) != NULL)
  	      *p = '\0';
  
  	    return_v = 0;
  	}
      }
  
      return(return_v);
  }
  
  char *
  last_cmpnt(filename)
      char *filename;
  {
      register char *p = NULL, *q = filename;
  
      if(!q)
        return(q);
  
      while((q = strchr(q, '/')) != NULL)
        if(*++q)
  	p = q;
  
      return(p);
  }
  
  int
  wantto(question, dflt, on_ctrl_C)
       char    *question;
       int      dflt, on_ctrl_C;
  {
      int ret = 0;
      char rep[1000], *p;
  
      while(!ret){
        fprintf(stdout, "%s? [%c]:", question, dflt);
        fgets(rep, sizeof(rep), stdin);
        if((p = strpbrk(rep, "\r\n")) != NULL)
  	*p = '\0';
        switch(*rep){
          case 'Y':
          case 'y':
  	  ret = (int)'y';
  	  break;
          case 'N':
          case 'n':
  	  ret = (int)'n';
  	  break;
          case '\0':
  	  ret = dflt;
  	  break;
          default:
  	  break;
        }
      }
  
      return ret;
  }
  