/*
* File: address.c
*
* Author: Ulli Horlacher (framstag@rus.uni-stuttgart.de)
*
* Contrib.: Stefan Zehl (sec@42.org)
*
* History:
*
* 1995-08-12 Framstag initial version
* 1995-11-07 Framstag added URL addressing
* 1995-11-15 Framstag added sendfile alias file
* 1995-12-13 Framstag correct bug when reading alias file
* 1996-04-30 Framstag checking elm alias only if configured
* 1997-01-04 Framstag renamed from destination.c to address.c
* added check_forward()
* 1997-01-22 Framstag added connect-test to generic saft address
* 1997-02-23 Framstag modified str_* function names
* 1997-02-24 Framstag sprintf() -> snprintf()
* 1997-03-18 Framstag better URL parsing
* 1997-11-22 Framstag added saft2rfc822()
* better SAFT URL parsing in check_forward()
* 1997-11-27 Framstag accept also saft.domain/user pseudo URLs
* (idea by Eumel, thanx!)
* 1998-01-04 Framstag fixed bug in saft2rfc822()
* 1998-01-13 Sec look for generic saft.domain address
* 1998-01-17 Framstag check SAFT-URL for alternative tcp port
* 1998-01-20 Framstag check_forward() works now better with sendfiled
* 1998-01-25 Framstag better check_forward()
* (accept user name without host)
* 1998-03-07 Framstag added finger_saft_port()
* 1998-03-11 Framstag aliases is now in $HOME/.sendfile/
* 1998-07-11 Framstag aliases may have an entry for CLI arguments
* 1998-08-26 Framstag allow port names, too (framstag@bofh:saft)
* 1998-12-13 Framstag fixed port determination bug
* 1999-08-07 Framstag fixed redirect message collecting bug
* "forward address found" is now a Info
*
* Various address routines for sendfile.c and sendmsg.c
*
* Copyright © 1995-1999 Ulli Horlacher
* This file is covered by the GNU General Public License
*/
#include "config.h" /* various #defines */
#include <ctype.h>
#include <stdio.h>
#include <pwd.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <netinet/in.h>
#include "string.h" /* Extended string functions */
#include "io.h" /* misc IO routines */
#include "net.h" /* the network routines */
#include "utf7.h" /* UTF-7 coding */
#include "message.h" /* information, warning and error messages */
#include "address.h" /* address routines */
#if defined(SOLARIS2)
int gethostname(char *, int);
#endif
#if defined(LINUX)
int gethostname(char *, size_t);
int symlink(const char *, const char *);
#endif
/* check an alias file */
int check_alias(char *, char *, char *, char *);
/* test if there is a forward address set */
int check_forward(int, char *, char *, char *);
/* finger user@host and look for user SAFT port */
int finger_saft_port(char *, char *);
extern int
client, /* flag to determine client or server */
verbose, /* flag for verbose mode */
quiet; /* quiet mode flag */
extern char *prg; /* name of the game */
/*
* destination - get recipient user and host
*
* INPUT: argc - shell argument count
* argv - the shell arguments
*
* OUTPUT: user - own user login and real name
* recipient - recipient user name
* host - recipient host name
* aopt - alias options
*/
void destination(int argc, char **argv,
char *user, char *recipient, char *host, char *aopt) {
char *cp, /* simple char pointer */
*at, /* @ character in recepient@host */
*larg, /* last argument */
gecos[FLEN], /* user real name */
tmp[MAXLEN], /* tmp string */
line[MAXLEN], /* one text line */
localhost[FLEN], /* name of the local host */
userconfig[MAXLEN], /* user configuration directory */
aliasfile[MAXLEN]; /* the alias file */
FILE *inf; /* input file */
struct passwd *pwe; /* password entry */
struct stat finfo; /* file information */
/* get the own user name */
if ((pwe=getpwuid(getuid())) == NULL)
message(prg,'F',"could not determine own user name");
/* translate the real name to UTF-7 and add it */
iso2utf(gecos,pwe->pw_gecos);
if ((cp=strchr(gecos,','))) *cp=0;
snprintf(user,FLEN-1,"%s %s",pwe->pw_name,gecos);
/* check user configuration directory */
snprintf(MAXS(userconfig),"%s/.sendfile",pwe->pw_dir);
snprintf(MAXS(tmp),SPOOL"/%s/config",pwe->pw_name);
if (stat(userconfig,&finfo)<0 && stat(tmp,&finfo)==0)
symlink(tmp,userconfig);
/* trick: argc == 0, when message reply mode */
if (argc==0) {
if (gethostname(localhost,FLEN-1)<0) strcpy(localhost,"localhost");
snprintf(MAXS(tmp),"%s/msg@%s",userconfig,localhost);
if ((inf=rfopen(tmp,"r")) && fgetl(line,inf)) {
if ((cp=strchr(line,'\n'))) *cp=0;
if ((cp=strchr(line,'@'))) {
*cp=0;
snprintf(host,FLEN-1,"%s",cp+1);
}
snprintf(recipient,FLEN-1,"%s",line);
}
fclose(inf);
return;
}
/* trick: argc < 0, when called from quak */
if (argc<0)
larg=argv[-argc];
else
larg=argv[argc-1];
*host=0;
/* user@host specified? */
if ((at=strchr(larg,'@'))) {
/* store recipient name and host */
*recipient=0;
strncat(recipient,larg,at-larg);
strcpy(host,at+1);
/* SAFT-URL specified? */
} else if (str_neq_nocase(larg,"saft",4) && strchr(larg,'/')) {
if (str_neq_nocase(larg,"saft://",7)) larg+=7;
cp=strrchr(larg,'/');
if (!cp || strchr(larg,'@')) message(prg,'F',"illegal SAFT-URL");
strcpy(recipient,cp+1);
*cp=0;
while ((cp=strchr(larg,'/'))) *cp='.';
strcpy(host,larg);
/*
if ((cp=strchr(host,':'))) {
*cp=0;
*port=atoi(cp+1);
}
*/
/* local user or alias specified */
} else {
strcpy(recipient,larg);
/* check the sendfile alias file */
snprintf(MAXS(aliasfile),"%s/aliases",userconfig);
if (check_alias(aliasfile,recipient,host,aopt)<0) {
#ifdef RESPECT_MAIL_ALIASES
/* check the elm alias file */
snprintf(MAXS(aliasfile),"%s/.elm/aliases.text",pwe->pw_dir);
if (check_alias(aliasfile,recipient,host,aopt)<0) {
#endif
/* store local recipient name and local host */
/* trick: argc <= 0, when called from quak */
if (argc<=0)
strcpy(recipient,argv[-argc]);
else
strcpy(recipient,argv[argc-1]);
strcpy(host,"127.0.0.1");
#ifdef RESPECT_MAIL_ALIASES
}
#endif
}
}
/*if (!*port) *port=SAFT;*/
}
/*
* check_alias - check an alias file
*
* INPUT: aliasfile - the alias file
* recipient - recipient alias name
*
* OUTPUT: recipient - recipient user name
* host - recipient host name
* aopt - alias options
*/
int check_alias(char *aliasfile, char *recipient, char *host, char *aopt) {
char *cp, /* a character pointer */
line[MAXLEN], /* one line of the alias file */
address[MAXLEN]; /* address from the alias */
FILE *inf; /* input file to read */
/* if there is an alias file, open it (what else? :-) ) */
inf=rfopen(aliasfile,"r");
if (inf==NULL) return(-1);
*address=0;
if (aopt) *aopt=0;
/* loop over all lines */
while (fgetl(line,inf)) {
/* trim line */
if ((cp=strchr(line,'\n'))) *cp=0;
if ((cp=strchr(line,'#'))) *cp=0;
str_trim(line);
if (!*line) continue;
/* save the address and options */
cp=strchr(line,' ');
if (cp)
*cp=0;
else
continue;
strcpy(address,cp+1);
if ((cp=strchr(address,' '))) {
*cp=0;
if (aopt) snprintf(aopt,FLEN-1,"%s",cp+1);
}
/* is it the correct alias, we are ready */
if (str_eq(recipient,line)) break;
*address=0;
if (aopt) *aopt=0;
}
fclose(inf);
/* alias found? */
if (*address) {
/* convert SAFT to mail address */
if (str_beq_nocase(address,"saft://")) saft2rfc822(address);
/* store recipient name and host */
cp=strchr(address,'@');
if (cp) {
*cp=0;
strcpy(host,cp+1);
} else
strcpy(host,"0");
strcpy(recipient,address);
return(0);
}
return(-1);
}
/*
* check_forward - test if there is a forward address set
*
* INPUT: sockfd - socket file descriptor
* recipient - recipient user name
* host - host to connect
* redirect - redirect comment
*
* OUTPUT: recipient - new recipient user name
* host - new host to connect
* redirect - new redirect comment
*
* RETURN: 1 if a forward is set, 0 if not, -1 on error
*/
int check_forward(int sockfd, char *recipient, char *host, char *redirect) {
char
*cp,*at, /* simple string pointer */
*adr, /* address in forwarding reply string */
*reply, /* reply string from server */
tmp[MAXLEN]; /* temporary string */
/* get reply from server */
reply=getreply(sockfd);
str_trim(reply);
/* forward address set? */
if (str_beq(reply,"510 ")) {
adr=strrchr(reply,' ')+1;
/* convert SAFT to mail address */
if (str_beq_nocase(adr,"saft://")) saft2rfc822(adr);
if (quiet<2) message(prg,'I',"forward address found");
if (*redirect) {
strcpy(tmp,redirect);
snprintf(redirect,MAXLEN-1,"%s\r\nredirected by %s@%s",
tmp,recipient,host);
} else {
snprintf(redirect,MAXLEN-1,"redirected by %s@%s",recipient,host);
}
/* save new recipient and host name */
if ((at=strchr(adr,'@'))) {
*at=0;
strcpy(host,at+1);
}
strcpy(recipient,adr);
return(1);
}
/* illegal answer */
if (!str_beq(reply,"200 ")) {
snprintf(MAXS(tmp),"server error: %s",reply+4);
errno=0;
if (client) message(prg,'F',tmp);
strcpy(redirect,reply+4);
if ((cp=strrchr(redirect,'.'))) *cp=0;
return(-1);
}
return(0);
}
/*
* saft_connect - look for correct SAFT server and open connection
*
* INPUT: type - file or message
* recipient - recipient user name
* user - local user name
* host - host to connect
* redirect - redirect comment
*
* OUTPUT: recipient - new recipient user name
* host - new host to connect
* redirect - redirect comment
*
* RETURN: socket file descriptor; -1 on error
*/
int saft_connect(const char *type,
char *recipient, char *user, char *host, char *redirect) {
int
port, /* tcp port to connect */
sockfd, /* socket file descriptor */
hopcount; /* count for forwarding addresses */
char
*cp, /* simple character pointer */
answer[FLEN], /* answer string */
tmp[MAXLEN], /* temporary string */
ahost[MAXLEN], /* alternate host */
line[MAXLEN]; /* one line of text */
extern int client; /* flag to determine client or server mode */
struct servent *sinfo;/* service info */
port=SAFT;
hopcount=0;
*answer=0;
/* get port number */
if ((cp=strchr(host,':'))) {
cp++;
if (*cp>'9') {
if ((sinfo=getservbyname(cp,"tcp"))) port=ntohs(sinfo->s_port);
} else
port=atoi(cp);
}
/* try to connect to the recipient's server */
for (hopcount=1; hopcount<11; hopcount++) {
/* if the finger-port is specified get real port from there */
if (port==79 || !port) {
if ((cp=strchr(host,':'))) *cp=0;
snprintf(MAXS(tmp),"opening connection to finger://%s/%s",host,recipient);
if (quiet<2) message(prg,'I',tmp);
port=finger_saft_port(recipient,host);
if (port<1) {
errno=0;
message(prg,'F',"no SAFT port information");
}
snprintf(tmp,FLEN-1,"%s:%d",host,port);
strcpy(host,tmp);
}
/* initiate the SAFT-connection to the server */
snprintf(MAXS(tmp),"opening connection to saft://%s/%s",host,recipient);
if (quiet<2) message(prg,'I',tmp);
sockfd=open_connection(host,port);
if (port==SAFT) {
/* host has no ip-address, but we can try more ... */
if (sockfd==-3 && str_eq(type,"file")) {
if (client) {
snprintf(MAXS(tmp),"%s has no internet-address",host);
if (quiet<2) message(prg,'W',tmp);
}
/* try generic saft-address for this host/domain */
if (port==SAFT) {
snprintf(MAXS(ahost),"saft.%s",host);
if (client) {
if(gethostbyname(ahost)){
if (!quiet) {
printf("try sending to %s@%s ? ",recipient,ahost);
fgetl(answer,stdin);
} else
*answer='y';
} else
*answer='n';
}
if (tolower(*answer)!='n' || !client) {
strcpy(host,ahost);
if (client) {
snprintf(MAXS(tmp),"opening connection to %s@%s",recipient,host);
if (quiet<2) message(prg,'I',tmp);
}
sockfd=open_connection(host,port);
}
}
}
/* try user SAFT port on connection failure */
if (sockfd==-2 && str_eq(type,"file")) {
if (verbose)
message(prg,'I',"found no system SAFT server, try to finger user");
port=finger_saft_port(recipient,host);
if (port>0 && port!=SAFT) {
snprintf(MAXS(tmp),"%s has no system SAFT server, "
"trying user SAFT server on port %d",host,port);
if (quiet<2) message(prg,'W',tmp);
sockfd=open_connection(host,port);
} else
port=SAFT;
}
}
if (sockfd==-1) snprintf(MAXS(tmp),"cannot create a network socket");
if (sockfd==-2) snprintf(MAXS(tmp),"cannot open connection to %s",host);
if (sockfd==-3) snprintf(MAXS(tmp),"%s is unknown",host);
if (sockfd<0) {
if (client) {
errno=0;
message(prg,'F',tmp);
} else
return(-1);
}
/* no remote server or protocol error? */
sock_getline(sockfd,line);
if (!str_beq(line,"220 ") || !strstr(line,"SAFT")) {
snprintf(MAXS(tmp),"No SAFT server on port %d at %s",port,host);
if ((cp=strrchr(tmp,':'))) *cp=0;
if (client) {
errno=0;
message(prg,'F',tmp);
} else {
strcpy(redirect,tmp);
return(-1);
}
}
/* send constant header lines */
snprintf(MAXS(tmp),"FROM %s",user);
sendheader(sockfd,tmp);
snprintf(MAXS(tmp),"TO %s",recipient);
sock_putline(sockfd,tmp);
/* is there a forward set? */
if (check_forward(sockfd,recipient,host,redirect)) {
/* close current connection */
sock_putline(sockfd,"QUIT");
getreply(sockfd);
shutdown(sockfd,2);
continue;
}
/* sendfile connection? */
if (str_eq(type,"file")) {
if (verbose) message(prg,'I',"testing remote server");
/* test if server can receive files */
sock_putline(sockfd,"FILE test");
if (check_forward(sockfd,recipient,host,redirect)) {
/* close current connection */
sock_putline(sockfd,"QUIT");
getreply(sockfd);
shutdown(sockfd,2);
continue;
}
}
/* connection is successfull */
break;
}
if (hopcount>10) {
strcpy(tmp,"maximum hopcount reached (forward loop?)");
if (client) {
errno=0;
message(prg,'F',tmp);
} else {
strcpy(redirect,tmp);
return(-1);
}
}
return(sockfd);
}
/*
* finger_saft_port - finger user@host and look for user SAFT port
*
* INPUT: user - user login name
*
* OUTPUT: host - host to connect
*
* RETURN: user saft port on success, -1 on failure
*/
int finger_saft_port(char *user, char *host) {
int
port, /* user SAFT tcp port */
sockfd; /* socket file descriptor */
char
*cp, /* character pointer */
line[MAXLEN]; /* one line of text */
port=-1;
*line=0;
/* connect to the finger port of the remote host */
sockfd=open_connection(host,79);
if (sockfd>0) {
sock_putline(sockfd,user);
while (sock_getline(sockfd,line)>=0) {
str_trim(line);
if (str_beq_nocase(line,"saftport") && (cp=strchr(line,'='))) {
port=atoi(cp+1);
break;
}
}
shutdown(sockfd,2);
}
return(port);
}
/*
* saft2rfc822 - SAFT URL to RFC822 mail address translation
*
* INPUT: adr - SAFT URL
*
* OUTPUT: adr - SAFT address in RFC822 format
*
* RETURN: 0 on success, -1 on failure
*/
int saft2rfc822(char *adr) {
char
*cp, /* simple char pointer */
*user, /* user name */
host[MAXLEN]; /* host name */
if (!str_beq_nocase(adr,"saft://")) return(-1);
strcpy(host,adr+7);
cp=strchr(host,'/');
if (!cp) return(-1);
*cp=0;
user=cp+1;
sprintf(adr,"%s@%s",user,host);
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1