/* * 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 #include #include #include #include #include #include #include #include #include #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); }