Subject: Kerberos diffs for 3.0 c-client

From: John Gardiner Myers <[email-masked]>
Date: Tue, 28 Sep 1993 15:45:07 -0400 (EDT)

Here are the Kerberos authentication diffs for the 3.0 c-client.  Some
assembley required, especially if you're not compiling on a sun with
gcc.

Compile with the KERBEROS macro defined.  You probably *don't* want to
compile with ANDREW defined.

These diffs include an interface change to server_login() which Mark
hasn't picked up yet.

diff -cr ./makefile.sun /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/makefile.sun
*** ./makefile.sun	Wed Jan  8 02:18:11 1992
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/makefile.sun	Tue Sep 28 13:29:21 1993
***************
*** 35,44 ****
  
  all:
  	rm -rf systype ANSI/c-client/Makefile non-ANSI/c-client/Makefile
! 	ln -s non-ANSI systype
  	cd systype/c-client; ln -s makefile.sun Makefile
! 	cd ms;make
! 	cd ipopd;make
  	cd imapd;make
  
  clean:
--- 35,44 ----
  
  all:
  	rm -rf systype ANSI/c-client/Makefile non-ANSI/c-client/Makefile
! 	ln -s ANSI systype
  	cd systype/c-client; ln -s makefile.sun Makefile
! #	cd ms;make
! #	cd ipopd;make
  	cd imapd;make
  
  clean:
diff -cr ./ANSI/c-client/imap2.c /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/imap2.c
*** ./ANSI/c-client/imap2.c	Wed Sep  8 23:31:22 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/imap2.c	Tue Sep 28 14:31:51 1993
***************
*** 48,53 ****
--- 48,60 ----
  #include "osdep.h"
  #include "imap2.h"
  #include "misc.h"
+ #ifdef KERBEROS
+ #include "kerberos.h"
+ #endif /* KERBEROS */
+ 
+ #ifdef ANDREW
+ static char *defaulthost="hogtown.andrew.cmu.edu";
+ #endif
  
  /* Driver dispatch used by MAIL */
  
***************
*** 103,109 ****
--- 110,120 ----
  
  DRIVER *map_valid (char *name)
  {
+ #ifdef ANDREW
+   return &imapdriver;
+ #else /* ANDREW */
    return mail_valid_net (name,&imapdriver,NIL,NIL);
+ #endif /* ANDREW */
  }

***************
*** 376,388 ****
--- 387,416 ----
  MAILSTREAM *map_open (MAILSTREAM *stream)
  {
    long i,j;
+ #ifdef KERBEROS
+   char username[MAILTMPLEN],pwd[3*MAILTMPLEN],tmp[MAILTMPLEN];
+ #else
    char username[MAILTMPLEN],pwd[MAILTMPLEN],tmp[MAILTMPLEN];
+ #endif
    NETMBX mb;
    char *s;
    IMAPPARSEDREPLY *reply = NIL;
  				/* return prototype for OP_PROTOTYPE call */
    if (!stream) return &imapproto;
+ #ifdef ANDREW
+   if (!mail_valid_net_parse (stream->mailbox,&mb)) {
+       strcpy(mb.host, defaulthost);
+       if (*stream->mailbox == '*') {
+ 	  mb.bbdflag = 1;
+ 	  strcpy(mb.mailbox, stream->mailbox+1);
+       }
+       else {
+ 	  strcpy(mb.mailbox, stream->mailbox);
+       }
+   }
+ #else ANDREW
    mail_valid_net_parse (stream->mailbox,&mb);
+ #endif
  				/* default mailbox name */
    if (!*mb.mailbox) strcpy (mb.mailbox,mb.bbdflag ? "general" : "INBOX");
    if (LOCAL) {			/* if stream opened earlier by us */
***************
*** 446,457 ****
--- 474,505 ----
  	  strcpy (username,"anonymous");
  	  strcpy (pwd,*lhostn ? lhostn : "foo");
  	}
+ #ifdef KERBEROS
+ 	else if (i == 0) {
+ 	    kerberos_login(mb.host, username, pwd, tmp);
+ 	}
+ #endif /* KERBEROS */
  	else mm_login (tcp_host (LOCAL->tcpstream),username,pwd,i);
  				/* abort if he refuses to give a password */
  	if (*pwd == '\0') i = map_maxlogintrials;
  	else {			/* send "LOGIN username pwd" */
+ #ifdef KERBEROS
  	  if (imap_OK (stream,reply = imap_send (stream,"LOGIN",username,
+ 						 pwd))) {
+ 	      if (i == 0) {
+ 		  if (!tmp[0]) {
+ 		      mm_log("Unable to check authentication of server", WARN);
+ 		  }
+ 		  else if (strncmp(reply->text, tmp, strlen(tmp))) {
+ 		      mm_log("Server failed to authenticate itself", WARN);
+ 		  }
+ 	      }
+ 	      break;
+ 	  }
+ #else
+ 	  if (imap_OK (stream,reply = imap_send (stream,"LOGIN",username,
  						 pwd))) break;
+ #endif
  				/* output failure and try again */
  	  mm_log (reply->text,WARN);
  				/* give up now if connection died */
diff -cr ./ANSI/c-client/kerberos.c /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/kerberos.c
*** ./ANSI/c-client/kerberos.c	Tue Sep 28 15:31:13 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/kerberos.c	Tue Sep 28 14:57:47 1993
***************
*** 0 ****
--- 1,391 ----
+ #ifdef KERBEROS
+ #include <stdio.h>
+ #include <krb.h>
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include "mail.h"
+ #include "osdep.h"
+ #include "imap2.h"
+ #include "kerberos.h"
+ 
+ static des_cblock session;	/* Our session key */
+ static des_key_schedule schedule; /* Schedule for our session key */
+ 
+ /* Table for converting binary values to and from hexadecimal */
+ static char hex[] = "0123456789abcdef";
+ static char dec[256] = {
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /*   0 -  15 */
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /*  16 -  37 */
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* ' ' - '/' */
+     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,   /* '0' - '?' */
+     0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* '@' - 'O' */
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 'P' - '_' */
+     0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* '`' - 'o' */
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 'p' - DEL */
+ };
+ 
+ /*
+  * Convert a KTEXT to an ascii string.
+  * Accepts: ktext
+  * Returnss: a pointer to the result, or null pointer on malloc failure
+  *           The caller is responsible for freeing the returned value.
+  */
+ static char *ktext_to_str(KTEXT ktext)
+ {
+     char *result, *p;
+     int i;
+ 
+     p = result = malloc(ktext->length*2+1);
+     if (!result) return 0;
+ 
+     for (i=0; i<ktext->length; i++) {
+ 	*p++ = hex[(ktext->dat[i]>>4)&0xf];
+ 	*p++ = hex[(ktext->dat[i])&0xf];
+     }
+     *p++ = '\0';
+     return result;
+ }
+ 
+ /*
+  * Convert string to a ktext.
+  * Accepts: string to convert
+  *          ktext to put result in
+  * Returns: T on success, NIL on failure
+  */
+ static int str_to_ktext(char *str,KTEXT ktext)
+ {
+     int i, len;
+     
+     len = strlen(str);
+     if (len&1) return NIL;
+     len /= 2;
+     if (len > MAX_KTXT_LEN) return NIL;
+ 
+     ktext->length = len;
+     ktext->mbz = 0;
+     
+     for (i=0; *str; i++, str += 2) {
+ 	ktext->dat[i] = (dec[str[0]]<<4) + dec[str[1]];
+     }
+     return T;
+ }	
+ 
+ /*
+  * Kerberos-authenticated server log in
+  * Accepts: user name string
+  *          password string
+  *          pointer to char pointer.  The char pointer is set to the
+  *               text we want returned in the reply message.
+  * Returns: T if login ok, NIL otherwise
+  */
+ int kerberos_server_login(char *user,char *pass,char **reply)
+ {
+     static char lrealm[REALM_SZ] = "";
+     KTEXT_ST authent;
+     char instance[INST_SZ];
+     AUTH_DAT kdata;
+     int code;
+     static char replybuf[256];
+     char *p;
+ 
+     /* Convert pass to authent */
+     if (str_to_ktext(pass, &authent) == NIL) {
+ 	*reply = "Invalid Kerberos authenticator";
+ 	return NIL;
+     }
+ 
+     /* Verify authenticator */
+     strcpy(instance, "*");
+     code = krb_rd_req(&authent, "imap", instance, 0L, &kdata, "");
+     if (code) {
+ 	strcpy(replybuf, krb_err_txt[code]);
+ 	*reply = replybuf;
+ 	return NIL;
+     }
+ 
+     /* Check authorization of the Kerberos user */
+     if (kuserok(&kdata, user)) {
+ 	if (strcmp(kdata.pname, "imap") ||
+ 	    (!(*lrealm) && krb_get_lrealm(lrealm, 1) == KFAILURE) ||
+ 	    strcmp(kdata.prealm, lrealm)) {
+ 	    strcpy(replybuf, "Permission denied.");
+ 	    *reply = replybuf;
+ 	    return NIL;
+ 	}
+     }
+ 
+     /* Save the session key */
+     bcopy(kdata.session, session, sizeof(des_cblock));
+     des_key_sched(session, schedule);
+     
+     /* Construct the response for mutual authentication */
+     authent.length = sizeof(des_cblock);
+     bzero(authent.dat, sizeof(des_cblock));
+     *((long *)authent.dat) = htonl(kdata.checksum + 1);
+     des_ecb_encrypt(authent.dat, authent.dat, schedule, 1);
+ 
+     /* Convert response to string and place in buffer */
+     p = ktext_to_str(&authent);
+     if (p) {
+ 	*replybuf = '[';
+ 	strcpy(replybuf+1, p);
+ 	strcat(replybuf, "] User ");
+ 	strcat(replybuf, user);
+ 	strcat(replybuf, " logged in");
+ 	free(p);
+     }
+     else {
+ 	/* XXX Out of memory */
+ 	exit(1);
+     }
+ 
+     *reply = replybuf;
+     return T;
+ }
+ 
+ /*
+  * Kerberos build password
+  * Accepts: host name
+  *	    buffer to store user name in
+  *	    buffer to store password in
+  *	    optional buffer to store expected return token in
+  * Returns: T on success, NIL on failure
+  */
+ int kerberos_login(char *host,char *user,char *pwd, char *token)
+ {
+     struct hostent *host_name;
+     char hostname[MAILTMPLEN];
+     char phost[MAILTMPLEN];
+     KTEXT_ST authent;
+     int checksum;
+     char *pass;
+     int code;
+     int i;
+     CREDENTIALS cr;
+     Key_schedule key_s;
+ 
+     *pwd = '\0';
+     
+     if (krb_get_tf_fullname(TKT_FILE, user, (char *)0, (char *)0)) {
+ 	return NIL;
+     }
+ 
+     /* Canonicalize hostname */
+     /* The domain literal form is used (rather than simply the dotted decimal
+      as with other Unix programs) because it has to be a valid "host name"
+      in mailsystem terminology. */
+ 				/* look like domain literal? */
+     if (host[0] == '[' && host[i = (strlen (host))-1] == ']') {
+ 	strcpy (hostname,host+1);	/* yes, copy without brackets */
+ 	hostname[i-1] = '\0';
+     }
+ 				/* note that Unix requires lowercase! */
+     else if (host_name = gethostbyname (lcase (strcpy (hostname,host))))
+       strcpy (hostname,host_name->h_name);
+ 
+ 
+     /*
+      * Build an authenticator.
+      */
+     checksum = time(0) ^ getpid();
+     strcpy(phost, krb_get_phost(hostname));
+     code = krb_mk_req(&authent, "imap", phost,
+ 		      krb_realmofhost(hostname), checksum);
+     if (code) return NIL;
+ 
+     pass = ktext_to_str(&authent);
+     if (!pass) return NIL;
+ 
+     /* Send Kerberos-format LOGIN command */
+     sprintf(pwd, "%s%s", KERBEROS_IDENT, pass);
+     free(pass);
+     if (!token) return T;
+     *token = '\0';
+ 
+     /*
+      * Build expected mutual authentication reply.
+      */
+     if (code = krb_get_cred("imap", phost, krb_realmofhost(hostname),&cr)) {
+ 	return T;
+     }
+     des_key_sched(cr.session,key_s);
+     authent.length = sizeof(des_cblock);
+     bzero(authent.dat, sizeof(des_cblock));
+     *((long *)authent.dat) = htonl(checksum + 1);
+     des_ecb_encrypt(authent.dat, authent.dat, key_s, 1);
+     pass = ktext_to_str(&authent);
+     if (pass) {
+ 	sprintf(token, "[%s]", pass);
+ 	free(pass);
+     }
+     return T;
+ }
+ 
+ static use_key(char *user,char *instance,char *realm,des_cblock key,des_cblock returned_key)
+ {
+     bcopy (key, returned_key, sizeof(des_cblock));
+     return 0;
+ }
+ 
+ kerberos_verify_password(char *user,char *passwd)
+ {
+ 
+     int result;
+     des_cblock key;
+     char realm[REALM_SZ];
+     char cell[REALM_SZ];
+     int i;
+     char buf[1024];
+ 
+     if (krb_get_lrealm(realm,1)) return NIL;
+ 
+     /* First try Kerberos string-to-key */
+     des_string_to_key(passwd, key);
+     
+     result = krb_get_in_tkt(user, "", realm,
+ 			    "krbtgt", realm, 1, use_key, NULL, key);
+ 
+     if (result == 0) {
+ 	bzero(key, sizeof(key));
+ 	if (krb_get_cred("krbtgt", realm, realm, buf)) {
+ 	    /* no ticket means we got an error packet with "0" error code */
+ 	    return NIL;
+ 	}
+ 	bzero(buf, sizeof(buf));
+ 	dest_tkt();
+ 	return T;
+     }
+ 
+     /* Now try andrew string-to-key */
+     strcpy(cell, realm);
+     for (i = 0; cell[i]; i++) {
+ 	if (isupper(cell[i])) cell[i] = tolower(cell[i]);
+     }
+     afs_string_to_key(passwd, &key, cell);
+     
+     result = krb_get_in_tkt(user, "", realm,
+ 			    "krbtgt", realm, 1, use_key, NULL, key);
+ 
+     bzero(key, sizeof(key));
+ 
+     if (result == 0) {
+ 	if (krb_get_cred("krbtgt", realm, realm, buf)) {
+ 	    /* no ticket means we got an error packet with "0" error code */
+ 	    return NIL;
+ 	}
+ 	bzero(buf, sizeof(buf));
+ 	dest_tkt();
+ 	return T;
+     }
+ 
+     return NIL;
+ }
+ 
+ /* andrewstk.c -- afs string to key function
+  *
+  * Code taken from AuthMan from University of Michigan
+  */
+ 
+ extern void des_fixup_key_parity();
+ extern unsigned long des_cbc_cksum();
+ 
+ /* forward declarations */
+ void afs_transarc_StringToKey();
+ void transarc_StringToKey();
+ int transarc_passwd_to_key();
+ char *crypt();
+ void des_fixup_key_parity();
+ 
+ void afs_cmu_StringToKey();
+ void athena_StringToKey();
+ int athena_passwd_to_key();
+ 
+ /* This defines the Andrew string_to_key function.  It accepts a password
+  * string as input and converts its via a one-way encryption algorithm to a DES
+  * encryption key.  It is compatible with the original Andrew authentication
+  * service password database.
+  */
+ 
+ void
+ afs_cmu_StringToKey (str, cell, key)
+   char          *str;
+   char          *cell;                  /* cell for password */
+   des_cblock *key;
+ {   char  password[8+1];                /* crypt is limited to 8 chars anyway */
+     int   i;
+     int   passlen;
+ 
+     bzero (key, sizeof(des_cblock));
+ 	bzero( (void *)password, sizeof( password ));
+ 
+     strncpy (password, cell, 8);
+     passlen = strlen (str);
+     if (passlen > 8) passlen = 8;
+ 
+     for (i=0; i<passlen; i++)
+         password[i] = str[i] ^ cell[i];
+ 
+     for (i=0;i<8;i++)
+         if (password[i] == '\0') password[i] = 'X';
+ 
+     /* crypt only considers the first 8 characters of password but for some
+        reason returns eleven characters of result (plus the two salt chars). */
+     strncpy((void *)key, crypt(password, "#~") + 2, sizeof(des_cblock));
+ 
+     /* parity is inserted into the LSB so leftshift each byte up one bit.  This
+        allows ascii characters with a zero MSB to retain as much significance
+        as possible. */
+     {   char *keybytes = (char *)key;
+         unsigned int temp;
+ 
+         for (i = 0; i < 8; i++) {
+             temp = (unsigned int) keybytes[i];
+             keybytes[i] = (unsigned char) (temp << 1);
+         }
+     }
+     des_fixup_key_parity (key);
+ }
+ 
+ void
+ afs_transarc_StringToKey (str, cell, key)
+   char          *str;
+   char          *cell;                  /* cell for password */
+   des_cblock *key;
+ {   des_key_schedule schedule;
+     char temp_key[8];
+     char ivec[8];
+     char password[BUFSIZ];
+     int  passlen;
+ 
+     strncpy (password, str, sizeof(password));
+     if ((passlen = strlen (password)) < sizeof(password)-1)
+         strncat (password, cell, sizeof(password)-passlen);
+     if ((passlen = strlen(password)) > sizeof(password)) passlen = sizeof(password);
+ 
+     bcopy ("kerberos", ivec, 8);
+     bcopy ("kerberos", temp_key, 8);
+     des_fixup_key_parity ((void *)temp_key);
+     des_key_sched (temp_key, schedule);
+     des_cbc_cksum (password, ivec, passlen, schedule, ivec);
+ 
+     bcopy (ivec, temp_key, 8);
+     des_fixup_key_parity ((void *)temp_key);
+     des_key_sched (temp_key, schedule);
+     des_cbc_cksum (password, (void *)key, passlen, schedule, ivec);
+ 
+     des_fixup_key_parity (key);
+ }
+ 
+ afs_string_to_key(str, key, cell)
+   char          *str;
+   des_cblock	*key;
+   char          *cell;                  /* cell for password */
+ {
+ 	if (strlen(str) > 8)
+ 		afs_transarc_StringToKey (str, cell, key);
+ 	else
+ 		afs_cmu_StringToKey (str, cell, key);
+ }
+ 
+ #endif /* KERBEROS */
diff -cr ./ANSI/c-client/kerberos.h /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/kerberos.h
*** ./ANSI/c-client/kerberos.h	Tue Sep 28 15:31:13 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/kerberos.h	Tue Sep 28 15:37:08 1993
***************
*** 0 ****
--- 1,6 ----
+ /* Passwords starting with this string indicate a Kerberos login attempt */
+ #define KERBEROS_IDENT "@KERBEROS:"
+ 
+ int kerberos_server_login(char *user,char *pass,char **reply);
+ int kerberos_login(char *host,char *user,char *pwd, char *token);
+ 
diff -cr ./ANSI/c-client/makefile.sun /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/makefile.sun
*** ./ANSI/c-client/makefile.sun	Tue Sep 28 15:31:13 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/makefile.sun	Tue Sep 28 14:49:31 1993
***************
*** 0 ****
--- 1,96 ----
+ # Program:	Portable C client makefile -- SUN-OS version
+ #
+ # Author:	Mark Crispin
+ #		Networks and Distributed Computing
+ #		Computing & Communications
+ #		University of Washington
+ #		Administration Building, AG-44
+ #		Seattle, WA  98195
+ #		Internet: [email-masked]
+ #
+ # Date:		11 May 1989
+ # Last Edited:	25 January 1993
+ #
+ # Copyright 1993 by the University of Washington
+ #
+ #  Permission to use, copy, modify, and distribute this software and its
+ # documentation for any purpose and without fee is hereby granted, provided
+ # that the above copyright notice appears in all copies and that both the
+ # above copyright notice and this permission notice appear in supporting
+ # documentation, and that the name of the University of Washington not be
+ # used in advertising or publicity pertaining to distribution of the software
+ # without specific, written prior permission.  This software is made
+ # available "as is", and
+ # THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+ # WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+ # NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+ # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+ # (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ 
+ 
+ CFLAGS = -g -DKERBEROS -DANDREW -I/usr/local/include
+ LDFLAGS = /usr/local/lib/libkrb.a /usr/local/lib/libdes.a #-ldl
+ CC = gcc
+ 
+ mtest: mtest.o c-client.a
+ 	echo $(CFLAGS) > CFLAGS
+ 	echo $(LDFLAGS) > LDFLAGS
+ 	$(CC) $(CFLAGS) -o mtest mtest.o c-client.a $(LDFLAGS)
+ 
+ clean:
+ 	rm -f *.o mtest c-client.a osdep.* CFLAGS LDFLAGS
+ 
+ mtest.o: mail.h smtp.h nntp.h misc.h osdep.h
+ 
+ c-client.a: mail.o bezerk.o tenex2.o mbox.o mh.o imap2.o news.o nntpclient.o \
+ 	phile.o dummy.o smtp.o nntp.o rfc822.o misc.o osdep.o sm_unix.o \
+ 	kerberos.o
+ 	rm -f c-client.a
+ 	ar rc c-client.a mail.o bezerk.o tenex2.o mbox.o mh.o imap2.o news.o \
+ 	nntpclient.o phile.o dummy.o smtp.o nntp.o rfc822.o misc.o osdep.o \
+ 	sm_unix.o kerberos.o
+ 	ranlib c-client.a
+ 
+ mail.o: mail.h misc.h osdep.h
+ 
+ bezerk.o: mail.h bezerk.h rfc822.h misc.h osdep.h
+ 
+ tenex.o2: mail.h tenex2.h rfc822.h misc.h osdep.h
+ 
+ mbox.o: mail.h mbox.h bezerk.h misc.h osdep.h
+ 
+ mh.o: mail.h mh.h rfc822.h misc.h osdep.h
+ 
+ imap2.o: mail.h imap2.h misc.h osdep.h
+ 
+ news.o: mail.h news.h misc.h osdep.h
+ 
+ nntpclient.o: mail.h nntp.h nntpclient.h misc.h rfc822.h news.h smtp.h osdep.h
+ 
+ dummy.o: mail.h dummy.h misc.h osdep.h
+ 
+ smtp.o: mail.h smtp.h rfc822.h misc.h osdep.h
+ 
+ nntp.o: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h
+ 
+ rfc822.o: mail.h rfc822.h misc.h
+ 
+ misc.o: mail.h misc.h osdep.h
+ 
+ sm_unix.o: mail.h misc.h osdep.h
+ 
+ osdep.o: mail.h osdep.h os_sun.c
+ 	$(CC) $(CFLAGS) -c os_sun.c
+ 	mv os_sun.o osdep.o
+ 
+ osdep.h: os_sun.h
+ 	rm -f osdep.h
+ 	ln os_sun.h osdep.h
+ 
+ # A monument to a hack of long ago and far away...
+ love:
+ 	@echo 'not war?'
+ 
diff -cr ./ANSI/c-client/os_sun.c /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/os_sun.c
*** ./ANSI/c-client/os_sun.c	Tue Sep 28 15:31:13 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/os_sun.c	Tue Sep 28 13:29:28 1993
***************
*** 0 ****
--- 1,680 ----
+ /*
+  * Program:	Operating-system dependent routines -- SUN-OS version
+  *
+  * Author:	Mark Crispin
+  *		Networks and Distributed Computing
+  *		Computing & Communications
+  *		University of Washington
+  *		Administration Building, AG-44
+  *		Seattle, WA  98195
+  *
+  * Date:	11 May 1989
+  * Last Edited:	2 November 1992
+  *
+  * Copyright 1992 by the University of Washington
+  *
+  *  Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose and without fee is hereby granted, provided
+  * that the above copyright notice appears in all copies and that both the
+  * above copyright notice and this permission notice appear in supporting
+  * documentation, and that the name of the University of Washington not be
+  * used in advertising or publicity pertaining to distribution of the software
+  * without specific, written prior permission.  This software is made
+  * available "as is", and
+  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  *
+  */
+ 
+ /* TCP input buffer */
+ 
+ #define BUFLEN 8192
+ 
+ 
+ /* TCP I/O stream (must be before osdep.h is included) */
+ 
+ #define TCPSTREAM struct tcp_stream
+ TCPSTREAM {
+   char *host;			/* host name */
+   char *localhost;		/* local host name */
+   int tcpsi;			/* input socket */
+   int tcpso;			/* output socket */
+   int ictr;			/* input counter */
+   char *iptr;			/* input pointer */
+   char ibuf[BUFLEN];		/* input buffer */
+ };
+ 
+ 
+ #include "mail.h"
+ #include "osdep.h"
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <ctype.h>
+ #include <errno.h>
+ extern int errno;		/* just in case */
+ #include <pwd.h>
+ #include <syslog.h>
+ #include "misc.h"
+ #ifdef KERBEROS
+ #include "kerberos.h"
+ #endif /* KERBEROS */
+ 
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ 
+ /* Write current time in RFC 822 format
+  * Accepts: destination string
+  */
+ 
+ char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ 
+ void rfc822_date (date)
+ 	char *date;
+ {
+   int zone;
+   char *zonename;
+   struct tm *t;
+   struct timeval tv;
+   struct timezone tz;
+   gettimeofday (&tv,&tz);	/* get time and timezone poop */
+   t = localtime (&tv.tv_sec);	/* convert to individual items */
+   zone = t->tm_gmtoff/60;	/* get timezone from TZ environment stuff */
+   zonename = t->tm_zone;
+ 				/* and output it */
+   sprintf (date,"%s, %d %s %d %02d:%02d:%02d %+03d%02d (%s)",
+ 	   days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ 	   t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60,zonename);
+ }
+ 
+ /* Get a block of free storage
+  * Accepts: size of desired block
+  * Returns: free storage block
+  */
+ 
+ void *fs_get (size)
+ 	size_t size;
+ {
+   void *block = malloc (size);
+   if (!block) fatal ("Out of free storage");
+   return (block);
+ }
+ 
+ 
+ /* Resize a block of free storage
+  * Accepts: ** pointer to current block
+  *	    new size
+  */
+ 
+ void fs_resize (block,size)
+ 	void **block;
+ 	size_t size;
+ {
+   if (!(*block = realloc (*block,size))) fatal ("Can't resize free storage");
+ }
+ 
+ 
+ /* Return a block of free storage
+  * Accepts: ** pointer to free storage block
+  */
+ 
+ void fs_give (block)
+ 	void **block;
+ {
+   free (*block);
+   *block = NIL;
+ }
+ 
+ 
+ /* Report a fatal error
+  * Accepts: string to output
+  */
+ 
+ void fatal (string)
+ 	char *string;
+ {
+   mm_fatal (string);		/* output the string */
+   syslog (LOG_ALERT,"IMAP C-Client crash: %s",string);
+   abort ();			/* die horribly */
+ }
+ 
+ /* Copy string with CRLF newlines
+  * Accepts: destination string
+  *	    pointer to size of destination string
+  *	    source string
+  *	    length of source string
+  */
+ 
+ char *strcrlfcpy (dst,dstl,src,srcl)
+ 	char **dst;
+ 	unsigned long *dstl;
+ 	char *src;
+ 	unsigned long srcl;
+ {
+   long i,j;
+   char *d = src;
+ 				/* count number of LF's in source string(s) */
+   for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
+   if (i > *dstl) {		/* resize if not enough space */
+     fs_give ((void **) dst);	/* fs_resize does an unnecessary copy */
+     *dst = (char *) fs_get ((*dstl = i) + 1);
+   }
+   d = *dst;			/* destination string */
+ 				/* copy strings, inserting CR's before LF's */
+   while (srcl--) switch (*src) {
+   case '\015':			/* unlikely carriage return */
+     *d++ = *src++;		/* copy it and any succeeding linefeed */
+     if (srcl && *src == '\012') {
+       *d++ = *src++;
+       srcl--;
+     }
+     break;
+   case '\012':			/* line feed? */
+     *d++ ='\015';		/* yes, prepend a CR, drop into default case */
+   default:			/* ordinary chararacter */
+     *d++ = *src++;		/* just copy character */
+     break;
+   }
+   *d = '\0';			/* tie off destination */
+   return *dst;			/* return destination */
+ }
+ 
+ 
+ /* Length of string after strcrlfcpy applied
+  * Accepts: source string
+  *	    length of source string
+  */
+ 
+ unsigned long strcrlflen (s)
+ 	STRING *s;
+ {
+   unsigned long pos = GETPOS (s);
+   unsigned long i = SIZE (s);
+   unsigned long j = i;
+   while (j--) switch (SNX (s)) {/* search for newlines */
+   case '\015':			/* unlikely carriage return */
+     if (j && (CHR (s) == '\012')) {
+       SNX (s);			/* eat the line feed */
+       j--;
+     }
+     break;
+   case '\012':			/* line feed? */
+     i++;
+   default:			/* ordinary chararacter */
+     break;
+   }
+   SETPOS (s,pos);		/* restore old position */
+   return i;
+ }
+ 
+ /* Server log in
+  * Accepts: user name string
+  *	    password string
+  *	    optional place to return home directory
+  * Returns: T if password validated, NIL otherwise
+  */
+ 
+ long server_login (user,pass,home,reply)
+ 	char *user;
+ 	char *pass;
+ 	char **home;
+         char **reply;
+ {
+   struct passwd *pw = getpwnam (lcase (user));
+ 				/* no entry for this user or root */
+   if (!(pw && pw->pw_uid)) return NIL;
+ 				/* validate password */
+ #ifdef KERBEROS
+   if (strncmp(pass, KERBEROS_IDENT, strlen(KERBEROS_IDENT)) == 0) {
+       if (kerberos_server_login(user, pass+strlen(KERBEROS_IDENT), reply) == NIL) return NIL;
+   }
+   else if (strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd)) == 0) {
+       /* OK */
+   }
+   else {
+       if (kerberos_verify_password(user, pass) == NIL) return NIL;
+   }
+ #else /* KERBEROS */
+   if (strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) return NIL;
+ #endif /* KERBEROS */
+   setgid (pw->pw_gid);		/* all OK, login in as that user */
+   initgroups (user,pw->pw_gid);	/* initialize groups */
+   setuid (pw->pw_uid);
+ 				/* note home directory */
+ #ifdef ANDREW
+   {
+       char *hd = myhomedir();
+       (void) mkdir(hd, 0700);
+   }
+ #endif /* ANDREW */
+   if (home) *home = myhomedir();
+   return T;
+ }
+ 
+ /* Return my user name
+  * Returns: my user name
+  */
+ 
+ char *uname = NIL;
+ 
+ char *myusername ()
+ {
+   return uname ? uname : (uname = cpystr (getpwuid (geteuid ())->pw_name));
+ }
+ 
+ 
+ /* Return my home directory name
+  * Returns: my home directory name
+  */
+ 
+ char *hdname = NIL;
+ 
+ char *myhomedir ()
+ {
+ #ifdef ANDREW
+   char buf[80];
+   if (hdname) return hdname;
+   sprintf(buf, "/usr/user/mbox/%s", myusername());
+   return hdname = cpystr(buf);
+ #else
+   return hdname ? hdname : (hdname = cpystr (getpwuid (geteuid ())->pw_dir));
+ #endif
+ }
+ 
+ 
+ /* Build status lock file name
+  * Accepts: scratch buffer
+  *	    file name
+  * Returns: name of file to lock
+  */
+ 
+ char *lockname (tmp,fname)
+ 	char *tmp;
+ 	char *fname;
+ {
+   int i;
+   sprintf (tmp,"/tmp/.%s",fname);
+   for (i = 6; i < strlen (tmp); ++i) if (tmp[i] == '/') tmp[i] = '\\';
+   return tmp;			/* note name for later */
+ }
+ 
+ /* TCP/IP open
+  * Accepts: host name
+  *	    contact port number
+  * Returns: TCP/IP stream if success else NIL
+  */
+ 
+ TCPSTREAM *tcp_open (host, port)
+ 	char *host;
+ 	int port;
+ {
+   TCPSTREAM *stream = NIL;
+   int sock;
+   char *s;
+   struct sockaddr_in sin;
+   struct hostent *host_name;
+   char hostname[MAILTMPLEN];
+   char tmp[MAILTMPLEN];
+   /* The domain literal form is used (rather than simply the dotted decimal
+      as with other Unix programs) because it has to be a valid "host name"
+      in mailsystem terminology. */
+ 				/* look like domain literal? */
+   if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+     strcpy (hostname,host+1);	/* yes, copy number part */
+     hostname[(strlen (hostname))-1] = '\0';
+     if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
+       sin.sin_family = AF_INET;	/* family is always Internet */
+       strcpy (hostname,host);	/* hostname is user's argument */
+     }
+     else {
+       sprintf (tmp,"Bad format domain-literal: %.80s",host);
+       mm_log (tmp,ERROR);
+       return NIL;
+     }
+   }
+ 
+   else {			/* lookup host name, note that brain-dead Unix
+ 				   requires lowercase! */
+     strcpy (hostname,host);	/* in case host is in write-protected memory */
+     if ((host_name = gethostbyname (lcase (hostname)))) {
+ 				/* copy address type */
+       sin.sin_family = host_name->h_addrtype;
+ 				/* copy host name */
+       strcpy (hostname,host_name->h_name);
+ 				/* copy host addresses */
+       memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length);
+     }
+     else {
+       sprintf (tmp,"No such host as %.80s",host);
+       mm_log (tmp,ERROR);
+       return NIL;
+     }
+   }
+ 
+ 				/* copy port number in network format */
+   if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
+ 				/* get a TCP stream */
+   if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
+     sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+     mm_log (tmp,ERROR);
+     return NIL;
+   }
+ 				/* open connection */
+   if (connect (sock,(struct sockaddr *)&sin,sizeof (sin)) < 0) {
+     sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
+ 	     strerror (errno));
+     mm_log (tmp,ERROR);
+     return NIL;
+   }
+ 				/* create TCP/IP stream */
+   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ 				/* copy official host name */
+   stream->host = cpystr (hostname);
+ 				/* get local name */
+   gethostname (tmp,MAILTMPLEN-1);
+   stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ?
+ 			      host_name->h_name : tmp);
+ 				/* init sockets */
+   stream->tcpsi = stream->tcpso = sock;
+   stream->ictr = 0;		/* init input counter */
+   return stream;		/* return success */
+ }
+ 
+ /* TCP/IP authenticated open
+  * Accepts: host name
+  *	    service name
+  * Returns: TCP/IP stream if success else NIL
+  */
+ 
+ TCPSTREAM *tcp_aopen (host,service)
+ 	char *host;
+ 	char *service;
+ {
+ #ifdef ANDREW
+   return NIL;
+ #else
+   TCPSTREAM *stream = NIL;
+   struct hostent *host_name;
+   char hostname[MAILTMPLEN];
+   int i;
+   int pipei[2],pipeo[2];
+   /* The domain literal form is used (rather than simply the dotted decimal
+      as with other Unix programs) because it has to be a valid "host name"
+      in mailsystem terminology. */
+ 				/* look like domain literal? */
+   if (host[0] == '[' && host[i = (strlen (host))-1] == ']') {
+     strcpy (hostname,host+1);	/* yes, copy without brackets */
+     hostname[i-1] = '\0';
+   }
+ 				/* note that Unix requires lowercase! */
+   else if (host_name = gethostbyname (lcase (strcpy (hostname,host))))
+     strcpy (hostname,host_name->h_name);
+ 				/* make command pipes */
+   if (pipe (pipei) < 0) return NIL;
+   if (pipe (pipeo) < 0) {
+     close (pipei[0]); close (pipei[1]);
+     return NIL;
+   }
+   if ((i = fork ()) < 0) {	/* make inferior process */
+     close (pipei[0]); close (pipei[1]);
+     close (pipeo[0]); close (pipeo[1]);
+     return NIL;
+   }
+   if (i) {			/* parent? */
+     close (pipei[1]);		/* close child's side of the pipes */
+     close (pipeo[0]);
+   }
+   else {			/* child */
+     dup2 (pipei[1],1);		/* parent's input is my output */
+     dup2 (pipei[1],2);		/* parent's input is my error output too */
+     close (pipei[0]); close (pipei[1]);
+     dup2 (pipeo[0],0);		/* parent's output is my input */
+     close (pipeo[0]); close (pipeo[1]);
+ 				/* now run it */
+     execl ("/usr/ucb/rsh","rsh",hostname,"exec",service,0);
+     _exit (1);			/* spazzed */
+   }
+ 
+ 				/* create TCP/IP stream */
+   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ 				/* copy official host name */
+   stream->host = cpystr (hostname);
+ 				/* get local name */
+   gethostname (hostname,MAILTMPLEN-1);
+   stream->localhost = cpystr ((host_name = gethostbyname (hostname)) ?
+ 			      host_name->h_name : hostname);
+   stream->tcpsi = pipei[0];	/* init sockets */
+   stream->tcpso = pipeo[1];
+   stream->ictr = 0;		/* init input counter */
+   return stream;		/* return success */
+ #endif /* ANDREW */
+ }
+ 
+ /* TCP/IP receive line
+  * Accepts: TCP/IP stream
+  * Returns: text line string or NIL if failure
+  */
+ 
+ char *tcp_getline (stream)
+ 	TCPSTREAM *stream;
+ {
+   int n,m;
+   char *st,*ret,*stp;
+   char tmp[2];
+   char c = '\0';
+   char d;
+ 				/* make sure have data */
+   if (!tcp_getdata (stream)) return NIL;
+   st = stream->iptr;		/* save start of string */
+   n = 0;			/* init string count */
+   while (stream->ictr--) {	/* look for end of line */
+     d = *stream->iptr++;	/* slurp another character */
+     if ((c == '\015') && (d == '\012')) {
+       ret = (char *) fs_get (n--);
+       memcpy (ret,st,n);	/* copy into a free storage string */
+       ret[n] = '\0';		/* tie off string with null */
+       return ret;
+     }
+     n++;			/* count another character searched */
+     c = d;			/* remember previous character */
+   }
+ 				/* copy partial string from buffer */
+   memcpy ((ret = stp = (char *) fs_get (n)),st,n);
+ 				/* get more data from the net */
+   if (!tcp_getdata (stream)) return NIL;
+ 				/* special case of newline broken by buffer */
+   if ((c == '\015') && (*stream->iptr == '\012')) {
+     stream->iptr++;		/* eat the line feed */
+     stream->ictr--;
+     ret[n - 1] = '\0';		/* tie off string with null */
+   }
+ 				/* else recurse to get remainder */
+   else if (st = tcp_getline (stream)) {
+     ret = (char *) fs_get (n + 1 + (m = strlen (st)));
+     memcpy (ret,stp,n);		/* copy first part */
+     memcpy (ret + n,st,m);	/* and second part */
+     fs_give ((void **) &stp);	/* flush first part */
+     fs_give ((void **) &st);	/* flush second part */
+     ret[n + m] = '\0';		/* tie off string with null */
+   }
+   return ret;
+ }
+ 
+ /* TCP/IP receive buffer
+  * Accepts: TCP/IP stream
+  *	    size in bytes
+  *	    buffer to read into
+  * Returns: T if success, NIL otherwise
+  */
+ 
+ long tcp_getbuffer (stream,size,buffer)
+ 	TCPSTREAM *stream;
+ 	unsigned long size;
+ 	char *buffer;
+ {
+   unsigned long n;
+   char *bufptr = buffer;
+   while (size > 0) {		/* until request satisfied */
+     if (!tcp_getdata (stream)) return NIL;
+     n = min (size,stream->ictr);/* number of bytes to transfer */
+ 				/* do the copy */
+     memcpy (bufptr,stream->iptr,n);
+     bufptr += n;		/* update pointer */
+     stream->iptr +=n;
+     size -= n;			/* update # of bytes to do */
+     stream->ictr -=n;
+   }
+   bufptr[0] = '\0';		/* tie off string */
+   return T;
+ }
+ 
+ 
+ /* TCP/IP receive data
+  * Accepts: TCP/IP stream
+  * Returns: T if success, NIL otherwise
+  */
+ 
+ long tcp_getdata (stream)
+ 	TCPSTREAM *stream;
+ {
+   fd_set fds;
+   FD_ZERO (&fds);		/* initialize selection vector */
+   if (stream->tcpsi < 0) return NIL;
+   while (stream->ictr < 1) {	/* if nothing in the buffer */
+     FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ 				/* block and read */
+     if ((select (stream->tcpsi+1,&fds,0,0,0) < 0) ||
+ 	((stream->ictr = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 1)) {
+       close (stream->tcpsi);	/* nuke the socket */
+       if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+       stream->tcpsi = stream->tcpso = -1;
+       return NIL;
+     }
+     stream->iptr = stream->ibuf;/* point at TCP buffer */
+   }
+   return T;
+ }
+ 
+ /* TCP/IP send string as record
+  * Accepts: TCP/IP stream
+  *	    string pointer
+  * Returns: T if success else NIL
+  */
+ 
+ long tcp_soutr (stream,string)
+ 	TCPSTREAM *stream;
+ 	char *string;
+ {
+   return tcp_sout (stream,string,(unsigned long) strlen (string));
+ }
+ 
+ 
+ /* TCP/IP send string
+  * Accepts: TCP/IP stream
+  *	    string pointer
+  *	    byte count
+  * Returns: T if success else NIL
+  */
+ 
+ long tcp_sout (stream,string,size)
+ 	TCPSTREAM *stream;
+ 	char *string;
+ 	unsigned long size;
+ {
+   int i;
+   fd_set fds;
+   FD_ZERO (&fds);		/* initialize selection vector */
+   if (stream->tcpso < 0) return NIL;
+   while (size > 0) {		/* until request satisfied */
+     FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+     if ((select (stream->tcpso+1,0,&fds,0,0) < 0) ||
+ 	((i = write (stream->tcpso,string,size)) < 0)) {
+       puts (strerror (errno));
+       close (stream->tcpsi);	/* nuke the socket */
+       if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+       stream->tcpsi = stream->tcpso = -1;
+       return NIL;
+     }
+     size -= i;			/* count this size */
+     string += i;
+   }
+   return T;			/* all done */
+ }
+ 
+ /* TCP/IP close
+  * Accepts: TCP/IP stream
+  */
+ 
+ void tcp_close (stream)
+ 	TCPSTREAM *stream;
+ {
+ 
+   if (stream->tcpsi >= 0) {	/* no-op if no socket */
+     close (stream->tcpsi);	/* nuke the socket */
+     if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+     stream->tcpsi = stream->tcpso = -1;
+   }
+ 				/* flush host names */
+   fs_give ((void **) &stream->host);
+   fs_give ((void **) &stream->localhost);
+   fs_give ((void **) &stream);	/* flush the stream */
+ }
+ 
+ 
+ /* TCP/IP get host name
+  * Accepts: TCP/IP stream
+  * Returns: host name for this stream
+  */
+ 
+ char *tcp_host (stream)
+ 	TCPSTREAM *stream;
+ {
+   return stream->host;		/* return host name */
+ }
+ 
+ 
+ /* TCP/IP get local host name
+  * Accepts: TCP/IP stream
+  * Returns: local host name
+  */
+ 
+ char *tcp_localhost (stream)
+ 	TCPSTREAM *stream;
+ {
+   return stream->localhost;	/* return local host name */
+ }
+ 
+ /* Copy memory block
+  * Accepts: destination pointer
+  *	    source pointer
+  *	    length
+  * Returns: destination pointer
+  */
+ 
+ char *memmove (s,ct,n)
+      char *s;
+      char *ct;
+      int n;
+ {
+   bcopy (ct,s,n);		/* they should have this one */
+   return ct;
+ }
+ 
+ 
+ /* Return implementation-defined string corresponding to error
+  * Accepts: error number
+  * Returns: string for that error
+  */
+ 
+ char *strerror (n)
+      int n;
+ {
+   return (n >= 0 && n < sys_nerr) ? sys_errlist[n] : NIL;
+ }
diff -cr ./ANSI/c-client/os_sun.h /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/os_sun.h
*** ./ANSI/c-client/os_sun.h	Tue Sep 28 15:31:13 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/c-client/os_sun.h	Tue Sep 28 13:29:29 1993
***************
*** 0 ****
--- 1,80 ----
+ /*
+  * Program:	Operating-system dependent routines -- SUN-OS version
+  *
+  * Author:	Mark Crispin
+  *		Networks and Distributed Computing
+  *		Computing & Communications
+  *		University of Washington
+  *		Administration Building, AG-44
+  *		Seattle, WA  98195
+  *		Internet: [email-masked]
+  *
+  * Date:	11 May 1989
+  * Last Edited:	4 December 1992
+  *
+  * Copyright 1992 by the University of Washington
+  *
+  *  Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose and without fee is hereby granted, provided
+  * that the above copyright notice appears in all copies and that both the
+  * above copyright notice and this permission notice appear in supporting
+  * documentation, and that the name of the University of Washington not be
+  * used in advertising or publicity pertaining to distribution of the software
+  * without specific, written prior permission.  This software is made
+  * available "as is", and
+  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  *
+  */
+ 
+ #define MAILFILE "/usr/spool/mail/%s"
+ #define ACTIVEFILE "/usr/lib/news/active"
+ #define NEWSSPOOL "/usr/spool/news"
+ #define NEWSRC strcat (strcpy (tmp,myhomedir ()),"/.newsrc")
+ #define NFSKLUDGE
+ 
+ #include <sys/types.h>
+ #include <sys/dir.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/uio.h>		/* needed for writev() prototypes */
+ 
+ extern char *strerror ();
+ extern char *memmove ();
+ 
+ 
+ /* Dummy definition overridden by TCP routines */
+ 
+ #ifndef TCPSTREAM
+ #define TCPSTREAM void
+ #endif
+ 
+ /* Function prototypes */
+ 
+ void rfc822_date (char *date);
+ void *fs_get (size_t size);
+ void fs_resize (void **block,size_t size);
+ void fs_give (void **block);
+ void fatal (char *string);
+ char *strcrlfcpy (char **dst,unsigned long *dstl,char *src,unsigned long srcl);
+ unsigned long strcrlflen (STRING *s);
+ long server_login (char *user,char *pass,char **home,char **reply);
+ char *myusername ();
+ char *myhomedir ();
+ char *lockname (char *tmp,char *fname);
+ TCPSTREAM *tcp_open (char *host,int port);
+ TCPSTREAM *tcp_aopen (char *host,char *service);
+ char *tcp_getline (TCPSTREAM *stream);
+ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer);
+ long tcp_getdata (TCPSTREAM *stream);
+ long tcp_soutr (TCPSTREAM *stream,char *string);
+ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size);
+ void tcp_close (TCPSTREAM *stream);
+ char *tcp_host (TCPSTREAM *stream);
+ char *tcp_localhost (TCPSTREAM *stream);
diff -cr ./ANSI/imapd/Makefile /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/imapd/Makefile
*** ./ANSI/imapd/Makefile	Fri Jan 10 20:49:34 1992
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/imapd/Makefile	Tue Sep 28 13:29:56 1993
***************
*** 37,42 ****
--- 37,43 ----
  
  CFLAGS = -I$C `cat $C/CFLAGS`
  LDFLAGS = $C/c-client.a `cat $C/LDFLAGS`
+ CC=gcc
  
  imapd: C-CLIENT imapd.o
  	$(CC) $(CFLAGS) -o imapd imapd.o $(LDFLAGS)
diff -cr ./ANSI/imapd/imapd.c /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/imapd/imapd.c
*** ./ANSI/imapd/imapd.c	Thu Sep 23 02:45:08 1993
--- /afs/andrew.cmu.edu/usr7/jm36/proj/imap/imap-3.0/ANSI/imapd/imapd.c	Tue Sep 28 15:38:43 1993
***************
*** 147,153 ****
--- 147,155 ----
    void (*f) () = NIL;
    mail_link (&tenexdriver);	/* install the Tenex mail driver */
    mail_link (&bezerkdriver);	/* install the Berkeley mail driver */
+ #ifndef ANDREW
    mail_link (&imapdriver);	/* install the IMAP driver */
+ #endif
    mail_link (&newsdriver);	/* install the netnews driver */
    mail_link (&nntpdriver);	/* install the NNTP driver */
    mail_link (&philedriver);	/* install the file driver */
***************
*** 232,243 ****
  	  struct passwd *pwd;
  	  fs_give ((void **) &user);
  	  fs_give ((void **) &pass);
  				/* two arguments */
  	  if (!((user = cpystr (snarf (&arg))) &&
  		(pass = cpystr (snarf (&arg))))) response = misarg;
  	  else if (arg) response = badarg;
  				/* see if username and password are OK */
! 	  else if (server_login (user,pass,&home,argc,argv)) state = SELECT;
  				/* nope, see if we allow anonymous */
  	  else if (!stat ("/etc/anonymous.newsgroups",&sbuf) &&
  		   !strcmp (user,"anonymous") && (pwd = getpwnam ("nobody"))) {
--- 234,249 ----
  	  struct passwd *pwd;
  	  fs_give ((void **) &user);
  	  fs_give ((void **) &pass);
+ 	  lsterr = NULL;
  				/* two arguments */
  	  if (!((user = cpystr (snarf (&arg))) &&
  		(pass = cpystr (snarf (&arg))))) response = misarg;
  	  else if (arg) response = badarg;
  				/* see if username and password are OK */
! 	  else if (server_login (user,pass,&home,&lsterr)) {
! 	      state = SELECT;
! 	      if (lsterr) response = altwin;
! 	  }
  				/* nope, see if we allow anonymous */
  	  else if (!stat ("/etc/anonymous.newsgroups",&sbuf) &&
  		   !strcmp (user,"anonymous") && (pwd = getpwnam ("nobody"))) {
***************
*** 248,253 ****
--- 254,260 ----
  				/* run user's init */
  	    dorc (strcat (strcpy (tmp,myhomedir ()),"/.imaprc"));
  	  }
+ 	  else if (lsterr) response = lose;
  	  else response = "%s NO Bad %s user name and/or password\015\012";
  	}
  	else response = "%s BAD Command unrecognized/login please: %s\015\012";

---

From: John Gardiner Myers <[email-masked]>
Date: Tue, 28 Sep 1993 17:45:30 -0400 (EDT)

The diffs I provided included an old version of strcrlfcpy() inside
os_sun.c.  This needs to be replaced with an up-to-date version from
one of the other os_* files in the 3.0 distribution.

				_.John