#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: abookcpy.c,v 4.4 1999/06/23 19:15:01 hubert Exp $";
#endif
/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Builiding, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"


   Pine and Pico are registered trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior written
   permission of the University of Washington.

   Pine, Pico, and Pilot software and its included text are Copyright
   1989-1998 by the University of Washington.

   The full text of our legal notices is contained in the file called
   CPYRIGHT, included with this distribution.


   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  Revision: 2.13                             *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

#include "headers.h"
#include "adrbklib.h"

int   parse_args PROTO((int, char **, char **, char **));
int   add_initial_msg PROTO((MAILSTREAM *, char *));
int   append_addrbook_data PROTO((MAILSTREAM *, char *, FILE *));
void  trim_addrbook_data PROTO((MAILSTREAM *, int));
void  write_fake_headers PROTO((char *, char *, char *));
char *err_desc PROTO((int));

int noshow_error = 0;

/*
 *  abookcpy [-t trimsize] <local_abook_file> <remote_abook_folder>
 */
main(argc, argv)
    int   argc;
    char *argv[];
{
    char      *local = NULL, *remote = NULL;
    MAILSTREAM *stream = NULL;
    FILE       *fp;
    int         trimsize;

#include "../c-client/linkage.c"

    trimsize = parse_args(argc, argv, &local, &remote);

    if(!local){
	fprintf(stderr, "No local addressbook file specified\n");
	exit(-1);
    }

    if(!remote || !remote[0]){
	fprintf(stderr, "No remote addressbook specified\n");
	exit(-1);
    }

    if(remote[0] != '{'){
	fprintf(stderr,
		"Remote addressbook name \"%s\" must begin with \"{\"\n",
	        remote);
	exit(-1);
    }

    if(local[0] == '{'){
	fprintf(stderr,
		"Local addressbook name \"%s\" must not begin with \"{\"\n",
	        local);
	exit(-1);
    }

    if(access(local, 0) != 0){
	fprintf(stderr, "Addressbook file \"%s\" does not exist\n", local);
	exit(-1);
    }

    if(access(local, 04) != 0){
	fprintf(stderr,
		"Can't read addressbook file \"%s\": %s\n",
		local, err_desc(errno));
	exit(-1);
    }

    /*
     * Try opening the local file.
     */
    if((fp = fopen(local, "r")) == NULL){
	fprintf(stderr, "Can't open %s: %s\n", local, err_desc(errno));
	exit(-1);
    }

    /*
     * Try opening the remote abook. If it doesn't exist, create it.
     */
    noshow_error = 1;
    stream = mail_open(NULL, remote, 0L);
    if(!stream || stream->halfopen){
	noshow_error = 0;
        /* try to create */
	if(!mail_create(stream, remote))
	  exit(-1);

	stream = mail_open(stream, remote, 0L);
	if(!stream)
	  exit(-1);
    }

    noshow_error = 0;

    /*
     * Add the explanatory header message if no messages there yet.
     */
    if(stream->nmsgs == 0)
      if(add_initial_msg(stream, remote) != 0){
	  mail_close(stream);
	  exit(-1);
      }

    /*
     * Add the actual addrbook data in a message.
     */
    if(append_addrbook_data(stream, remote, fp) != 0){
	mail_close(stream);
	exit(-1);
    }

    /*
     * Trim the size of the addrbook folder.
     */
    if(trimsize)
      trim_addrbook_data(stream, trimsize);

    mail_close(stream);
    exit(0);
}


/*
 * Returns the trimsize, the number of addrbooks to be left behind and not
 * trimmed. If trimsize isn't set that means don't trim.
 */
int
parse_args(argc, argv, local, remote)
    int          argc;
    char       **argv;
    char       **local;
    char       **remote;
{
    int    ac;
    char **av;
    int    c;
    char  *str, *name;
    int    usage     = 0;
    int    trimsize  = 0;

    ac = argc;
    av = argv;
    name = av[0];

      /* 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 't':			/* integer 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 't':
		    if(!isdigit((unsigned char)str[0])){
			fprintf(stderr,
				"non-numeric argument for flag \"%c\"\n", c);
			++usage;
			break;
		    }

		    trimsize = atoi(str);
		    if(trimsize < 1){
			fprintf(stderr, "trimsize of %d is too small, have to leave at least one addrbook copy\n", trimsize);
			++usage;
		    }
		    else if(trimsize > 100){
			fprintf(stderr,
				"trimsize of %d is too large\n", trimsize);
			++usage;
		    }

		    break;
		}

		goto Loop;

		default:
		  fprintf(stderr, "unknown flag \"%c\"\n", c);
		  ++usage;
		  break;
	    }
	 }
      }

    if(ac != 2)
      usage++;

    if(usage){
	fprintf(stderr,
	   "usage: %s [-t trimsize] <local_abook_file> <remote_abook_folder>\n",
	   name);
	exit(-1);
    }

    *local = *av++;
    *remote = *av;

    return(trimsize);
}


/*
 * Add an explanatory first message advising user that this is a
 * special sort of folder.
 */
int
add_initial_msg(stream, mailbox)
    MAILSTREAM *stream;
    char       *mailbox;
{
    STRING        msg;
    char          buf[1000], *bufp;

    write_fake_headers(buf, "Header Message for Remote Addressbook",
			      "plain");
    bufp = buf + strlen(buf);

    strcpy(bufp, "This folder contains a single Pine addressbook.\015\012");
    strcat(bufp, "This message is just an explanatory message.\015\012");
    strcat(bufp, "The last message in the folder is the live addressbook data.\015\012");
    strcat(bufp, "The rest of the messages contain previous revisions of the addressbook data.\015\012");
    strcat(bufp, "To restore a previous revision just delete and expunge all of the messages\015\012");
    strcat(bufp, "which come after it.\015\012");

    INIT(&msg, mail_string, (void *)buf, strlen(buf));
    if(!mail_append(stream, mailbox, &msg))
      return(-1);
    
    return(0);
}


/*
 * Add a message to the folder with the contents of the local addrbook
 * in it.
 */
int
append_addrbook_data(stream, mailbox, fp)
    MAILSTREAM *stream;
    char       *mailbox;
    FILE       *fp;
{
    STRING        msg;
    char          buf[1000], *sto, *p;
    struct stat   sbuf;
    long          filelen;
    int           c, last_c = 0;

    if(fstat(fileno(fp), &sbuf) != 0){
	fprintf(stderr, "stat of local addrbook failed\n");
	return(-1);
    }

    filelen = (long)sbuf.st_size;

    write_fake_headers(buf, "Remote Addressbook Container",
		       REMOTE_ABOOK_SUBTYPE);

    /* very conservative estimate of space needed */
    sto = fs_get(filelen + filelen + strlen(buf) + 10);

    strcpy(sto, buf);
    p = sto + strlen(sto);
    /* Write the addressbook contents */
    while((c = getc(fp)) != EOF){
	/*
	 * c-client expects CRLF-terminated lines. Address book lines
	 * can be either CRLF- or LF-terminated. We have to convert them
	 * when we copy into c-client.
	 */
	if(last_c == '\r' || last_c == '\n'){
	    /* write the CRFL */
	    *p++ = '\r';
	    *p++ = '\n';
	    last_c = 0;

	    /* write this char if not CR or LF */
	    if(!(c == '\r' || c == '\n'))
	      *p++ = c;
	}
	else if(c == '\r' || c == '\n')
	  last_c = c;
	else
	  *p++ = c;
    }

    fclose(fp);

    /* Pick up a trailing CR or LF */
    if(last_c == '\r' || last_c == '\n'){
	*p++ = '\r';
	*p++ = '\n';
    }

    *p = '\0';

    INIT(&msg, mail_string, (void *)sto, strlen(sto));
    if(!mail_append(stream, mailbox, &msg)){
	fprintf(stderr, "Copy failed\n");
	return(-1);
    }

    fs_give((void **)&sto);

    return(0);
}


/*
 * Trim the number of saved copies of the address book history in case
 * this is the only way this address book is ever updated. We leave
 * the first message there because it is supposed to be an explanatory
 * message, but we don't actually check to see whether or not it is
 * such a message or not.
 */
void
trim_addrbook_data(stream, trimsize)
    MAILSTREAM *stream;
    int         trimsize;
{
    if(stream->nmsgs > trimsize + 1){
	char sequence[20];

	noshow_error = 1;
	mail_ping(stream);
	sprintf(sequence, "2:%ld", stream->nmsgs - trimsize);
	mail_flag(stream, sequence, "\\DELETED", ST_SET);
	mail_expunge(stream);
	noshow_error = 0;
    }
}


void
write_fake_headers(where, subject, subtype)
    char *where;
    char *subject;
    char *subtype;
{
    ENVELOPE *fake_env;
    BODY     *fake_body;
    ADDRESS  *fake_from;
    char      date[200], vers[3];

    fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
    memset(fake_env, 0, sizeof(ENVELOPE));
    fake_body = (BODY *)fs_get(sizeof(BODY));
    memset(fake_body, 0, sizeof(BODY));
    fake_from = (ADDRESS *)fs_get(sizeof(ADDRESS));
    memset(fake_from, 0, sizeof(ADDRESS));
    rfc822_date(date);

    fake_env->subject = cpystr(subject);
    fake_env->date = cpystr(date);
    fake_from->personal = cpystr("Pine AddrBook");
    fake_from->mailbox = cpystr("nobody");
    fake_from->host = cpystr("nowhere");
    fake_env->from = fake_from;
    fake_body->type = REMOTE_ABOOK_TYPE;
    fake_body->subtype = cpystr(subtype);

    sprintf(vers, "%ld", REMOTE_ABOOK_VERS_NUM);
    /* re-use subtype for special header name, too */
    *where = '\0';
    rfc822_header_line(&where, REMOTE_ABOOK_SUBTYPE, fake_env, vers);
    rfc822_header(where+strlen(where), fake_env, fake_body);
    mail_free_envelope(&fake_env);
    mail_free_body(&fake_body);
}


extern char *sys_errlist[];

char *
err_desc(err)
    int err;
{
    static char buffer[50];

    strcpy(buffer, sys_errlist[err]);

    return((char *)buffer);
}


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;
    char 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(mb, username, password, trial)
    NETMBX *mb;
    char *username;
    char *password;
    long trial;
{
    fprintf(stderr,
	    "Must be able to login without a password to use this program.\n");
    exit(-1);
}


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);
}
