#include <stdio.h> #include <string.h> #include <ctype.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> #include <resolv.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include "db.h" #include "externs.h" struct email_server_struct { char *server; int pref; }; struct email_struct { int socket; OBJ *player; OBJ *victim; char *from; char *send_to; char *message; int num_servers; int cur_server; struct email_server_struct *email_servers; struct email_struct *next; }; typedef struct email_server_struct EMAIL_SRVR; typedef struct email_struct EMAIL; EMAIL *pending_email_list = NULL; static const char *server = "dreams.cpugeek.com"; static const char *serverid = "med"; static const char *admin_email = "itsme@dreams.cpugeek.com"; /* Prototypes */ static int try_next_server P((EMAIL *)); static void send_email P((EMAIL *)); static void fail_mail(OBJ *player, int fd, char *msg) { if(msg) notify(player, tprintf("EMAIL ERROR: %s", msg)); else notify(player, "There was an error while sending your email."); if(fd >= 0) close(fd); } static int get_reply(int fd) { char buf[4096]; if(read(fd, buf, sizeof(buf)) <= 0) return(-1); return(atoi(buf)); } static int send_reply(OBJ *player, int fd, char *msg) { if(write(fd, tprintf("%s\r\n", msg), strlen(msg)+2) <= 0) { fail_mail(player, fd, "Couldn't write to server."); return(0); } return(1); } int is_valid_email(char *address) { char *p = strchr(address, '@'); if(!*address || !p || p == address || !*(p+1) || strchr(address, ' ')) return(0); return(1); } static EMAIL *is_sending_email(OBJ *player) { EMAIL *e; for(e = pending_email_list;e;e = e->next) if(e->player == player) break; return(e); } static EMAIL *add_email_send(OBJ *player, OBJ *victim, char *from, char *send_to, char *msg) { EMAIL *new; new = (EMAIL *)stack_alloc(sizeof(EMAIL), 1, 0); new->socket = 0; new->player = player; new->victim = victim; new->num_servers = 0; new->cur_server = -1; new->email_servers = NULL; SET(new->message, msg); SET(new->from, from); SET(new->send_to, send_to); new->next = pending_email_list; pending_email_list = new; return(new); } static void del_email_send(EMAIL *email) { EMAIL *e, *eprev = NULL; int i; for(e = pending_email_list;e;e = e->next) { if(e == email) break; eprev = e; } if(!e) return; if(eprev) eprev->next = e->next; else pending_email_list = e->next; for(i = 0;i < e->num_servers;++i) stack_free(e->email_servers[i].server); stack_free(e->message); stack_free(e->send_to); stack_free(e->email_servers); stack_free(e); } void poll_pending_email() { EMAIL *e, *enext; fd_set set; int max_sock = 0; int rv; struct timeval tv; int blocking_flags; int err, len = sizeof(int); /* For getsockopt() */ FD_ZERO(&set); tv.tv_sec = 0; /* Don't */ tv.tv_usec = 0; /* Wait */ for(e = pending_email_list;e;e = e->next) { FD_SET(e->socket, &set); if(e->socket > max_sock) max_sock = (e->socket)+1; } if((rv = select(max_sock, NULL, &set, NULL, &tv)) == -1) { log_error(tprintf("poll_pending_email() - select(): %s", strerror(errno))); return; } if(!rv) /* Nothing ready */ return; for(e = pending_email_list;e;e = enext) { enext = e->next; if(!FD_ISSET(e->socket, &set)) continue; getsockopt(e->socket, SOL_SOCKET, SO_ERROR, (void *)&err, &len); if(err) /* connect() failed */ { while((rv = try_next_server(e)) == 0); /* Whole loop */ if(rv == -1) { notify(e->player, tprintf("I couldn't send email to %s.", (e->victim)?name(e->victim):e->send_to)); del_email_send(e); } continue; } blocking_flags = fcntl(e->socket, F_GETFL); blocking_flags &= ~O_NONBLOCK; fcntl(e->socket, F_SETFL, blocking_flags); send_email(e); del_email_send(e); } } static unsigned short getshort(unsigned char *ptr) { unsigned short out; out = (ptr[0] << 8) | ptr[1]; return(out); } static void add_server(EMAIL *email, char *server, int pref, int *ctr) { if(!email->email_servers) email->email_servers = (EMAIL_SRVR *)stack_alloc(sizeof(EMAIL_SRVR), 1, 0); else email->email_servers = (EMAIL_SRVR *)stack_realloc(email->email_servers, sizeof(EMAIL_SRVR)*((*ctr)+1)); email->email_servers[*ctr].server = stack_string_alloc(server, 1); email->email_servers[*ctr].pref = pref; (*ctr)++; } static void sort_mail_servers(EMAIL *email) { EMAIL_SRVR *ei, *ej; char stemp[4096]; int itemp; int i, j; for(i = 0;i < email->num_servers;++i) for(j = i+1;j < email->num_servers;++j) if(email->email_servers[j].pref < email->email_servers[i].pref) { ei = &email->email_servers[i]; ej = &email->email_servers[j]; strcpy(stemp, ei->server); ei->server = stack_string_realloc(ei->server, ej->server); ej->server = stack_string_realloc(ej->server, stemp); itemp = ei->pref; ei->pref = ej->pref; ej->pref = itemp; } } static int get_email_servers(EMAIL *email, char *hostname) { union { HEADER hdr; unsigned char buf[1024]; } response; int response_len; unsigned char *response_end, *response_pos; char name[1024]; unsigned short rrtype, rrdlen; int pref; int n, i; response_len = res_query(hostname, C_IN, T_MX, response.buf, sizeof(response)); if(response_len < 0) { add_server(email, hostname, 0, &email->num_servers); return(email->num_servers); } if(response_len >= sizeof(response)) return(0); response_end = response.buf+response_len; response_pos = response.buf+sizeof(HEADER); n = ntohs(response.hdr.qdcount); while(n-- > 0) { if((i = dn_expand(response.buf, response_end, response_pos, name, MAXDNAME)) < 0) { return(0); } response_pos += i; i = response_end - response_pos; if(i < QFIXEDSZ) return(0); response_pos += QFIXEDSZ; } n = ntohs(response.hdr.ancount); while(n-- > 0) { if((i = dn_expand(response.buf, response_end, response_pos, name, MAXDNAME)) < 0) { return(0); } response_pos += i; rrtype = getshort(response_pos); rrdlen = getshort(response_pos+8); response_pos += 10; if(rrtype == T_MX) { if(rrdlen < 3) return(0); pref = getshort(response_pos); if((i = dn_expand(response.buf, response_end, response_pos+2, name, MAXDNAME)) < 0) { return(0); } add_server(email, name, pref, &email->num_servers); } response_pos += rrdlen; } if(!email->num_servers) { add_server(email, hostname, 0, &email->num_servers); return(1); } sort_mail_servers(email); return(email->num_servers); } void do_email(OBJ *player, char *arg1, char *msg) { EMAIL *e; OBJ *victim = NULL; char address[4096], from[4096]; char *hostname; if((e = is_sending_email(player))) { notify(player, tprintf("You're already sending email to %s.", (e->victim)?name(e->victim):e->send_to)); notify(player, "You may send another email when it is done."); return; } strcpy(from, atr_get(player, "EMAIL")); strcpy(address, arg1); if(!is_valid_email(from)) { notify(player, "Your EMAIL attribute must be set to a valid reply-to address."); return; } if(!is_valid_email(address)) { if(!(victim = match_player(NULL, address))) { notify(player, tprintf("Invalid recipient: %s", address)); return; } if(!could_doit(player, victim, "LPAGE")) { notify(player, tprintf("%s doesn't want to talk to you.", name(victim))); return; } strcpy(address, atr_get(victim, "EMAIL")); if(!is_valid_email(address)) { notify(player, tprintf("%s EMAIL attribute is not set.", poss(victim))); return; } } hostname = strchr(address, '@'); *hostname++ = '\0'; /* A period (.) will interrupt the message DATA */ if(!*msg || *msg == '.') { notify(player, "You must specify a message."); return; } e = add_email_send(player, victim, from, tprintf("%s@%s", address, hostname), msg); if(!get_email_servers(e, hostname)) { notify(player, tprintf("I can't send email to %s.", (victim)?name(victim):tprintf("%s@%s", address, hostname))); del_email_send(e); return; } notify(player, tprintf("Attempting to send email to %s...Please wait.", (victim)?name(victim):tprintf("%s@%s", address, hostname))); try_next_server(e); /* Ignore return value. There's at least one server */ } /* Return values: (-1) - Every server exhausted ( 0) - Current server failed ( 1) - Connecting to valid server */ static int try_next_server(EMAIL *e) { struct sockaddr_in addr; struct hostent *hent; char *email_server; e->cur_server++; if(e->cur_server >= e->num_servers) return(-1); email_server = e->email_servers[e->cur_server].server; if((e->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { notify(e->player, "I can't send email at this time."); return(0); } addr.sin_family = AF_INET; addr.sin_port = htons(25); /* SMTP server is on port 25 */ if(!(hent = gethostbyname(email_server))) { if(!inet_aton(email_server, &addr.sin_addr)) return(0); } else addr.sin_addr.s_addr = *(long *)hent->h_addr_list[0]; fcntl(e->socket, F_SETFL, O_NONBLOCK); connect(e->socket, (struct sockaddr *)&addr, sizeof(addr)); return(1); } void send_email(EMAIL *e) { int replies[] = { 220, 250, 250, 250, 354, 250, 250 }; int *reply = replies; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, NULL); return; } if(!send_reply(e->player, e->socket, tprintf("HELO %s", server))) return; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "Introduction failed."); return; } if(!send_reply(e->player, e->socket, tprintf("MAIL FROM:<%s@%s>", serverid, server))) { return; } if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "Invalid sender."); return; } if(!send_reply(e->player, e->socket, tprintf("RCPT TO:<%s>", e->send_to))) return; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "Invalid recipient."); return; } if(!send_reply(e->player, e->socket, "DATA")) return; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "Attempt for DATA send denied."); return; } if(!send_reply(e->player, e->socket, tprintf("From:%s@%s", serverid, server))) { return; } if(!send_reply(e->player, e->socket, tprintf("To:%s", e->send_to))) return; if(!send_reply(e->player, e->socket, tprintf("Reply-To:%s", e->from))) return; if(!send_reply(e->player, e->socket, tprintf("Subject:A message from %s on %s", strip_color(unparse_object(e->player, e->player)), config.maze_name))) { return; } if(!send_reply(e->player, e->socket, "")) return; if(!send_reply(e->player, e->socket, "---------- Message ----------")) return; if(!send_reply(e->player, e->socket, e->message)) return; if(!send_reply(e->player, e->socket, "-------- End Message --------")) return; if(!send_reply(e->player, e->socket, tprintf("\r\nThis message was automatically delivered by the %s server.", config.maze_name))) { return; } if(!send_reply(e->player, e->socket, tprintf("If the sender of this email (%s - %s) is harassing you," " send email to %s and the player will be disallowed from sending" " emails to your address.", strip_color(unparse_object(e->player, e->player)), e->from, admin_email))) { return; } if(!send_reply(e->player, e->socket, ".")) return; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "DATA message was refused."); return; } if(!send_reply(e->player, e->socket, "RSET")) return; if(get_reply(e->socket) != *reply++) { fail_mail(e->player, e->socket, "RSET failed."); return; } if(e->victim) notify(e->player, tprintf("Your email to %s was successfully sent.", name(e->victim))); else notify(e->player, "Your email was successfully sent."); close(e->socket); }