diff --git a/.gitignore b/.gitignore index a568eaa..be8e5ab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.o guinnessd +guinness diff --git a/Makefile b/Makefile index 3a48752..65e0e24 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,8 @@ COPT = -Wall -g D_OBJS = xmem.o broadcast.o printlog.o lists.o tools.o drinks.o \ commands.o clients.o +C_OBJS = xmem.o tools.o + D_LIBS = -lpthread -lcrypt # --------------------------------------------------------- @@ -25,6 +27,14 @@ guinnessd: guinnessd.o Makefile $(D_OBJS) guinnessd.o: guinnessd.c Makefile $(H_DEP) gcc $(COPT) -c $< +# + +guinness: guinness.o Makefile $(D_OBJS) + gcc -g $< $(C_OBJS) $(D_LIBS) -o $@ + +guinness.o: guinness.c Makefile $(H_DEP) + gcc $(COPT) -c $< + # --------------------------------------------------------- # # modules needed by the daemon @@ -49,6 +59,4 @@ clients.o: clients.c Makefile $(H_DEP) # --------------------------------------------------------- -clients.o: clients.c Makefile $(H_DEP) - gcc $(COPT) -c $< diff --git a/guinness.c b/guinness.c new file mode 100644 index 0000000..c25cbcb --- /dev/null +++ b/guinness.c @@ -0,0 +1,529 @@ +/* + * guinness + * architecture clients/serveur guinness. + * Thomas Nemeth -- le 15 juin 2001 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "defines.h" +#include "xmem.h" +#include "guinness.h" +#include "tools.h" +#include "config.h" + + +#ifdef SunOS +char *crypt(const char *key, const char *salt); +#else +extern char *crypt __P ((__const char *__key, __const char *__salt)); +#endif + + +/* VARIABLES GLOBALES */ +char **commandes = NULL; +char *utilisateur = NULL; +char *boisson = NULL; +char *serveur = NULL; +char *prompt = NULL; +char *logout = NULL; +char cmdchr = 0; +int port = 0; +int nb_cmd = 0; + + +/* + * Lecture du fichier de config : récupération des valeurs + * + */ +char *get_string_from_token (char *line) { + char *result = line; + char *tmp; + int keyval = 0; + + while ( (keyval != 1) && (result [0] != 0) ) { + if (result [0] == '=') keyval = 1; + if (keyval == 0) result++; + } + tmp = result++; + while (tmp [0] != 0) { + if (tmp [0] == '\n') tmp [0] = 0; + tmp++; + } + return result; +} + + +/* + * Lecture du fichier de config + * + */ +void load_config () { + FILE *file; + char *configfile; + char tmpstr[MAXSTRLEN + 1]; + char *Home; + char *value; + + Home = getenv ("HOME"); + configfile = xmalloc ((strlen (Home) + + strlen (CONFIG_FILE) + 2) * sizeof (char)); + snprintf (configfile, MAXSTRLEN, "%s/%s", Home, CONFIG_FILE); + + /* Ouverture du fichier */ + if ( (file = fopen (configfile, "r") ) != NULL) { + /* Lecture du fichier */ + while (! feof (file) ) { + memset (tmpstr, 0, MAXSTRLEN + 1); + fgets (tmpstr, (int) MAXSTRLEN, file); + value = get_string_from_token (tmpstr); + if ( (strncmp (tmpstr, "user=", 5) == 0) && (utilisateur == NULL) ) + utilisateur = xstrdup (value); + if ( (strncmp (tmpstr, "server=", 7) == 0) && (serveur == NULL) ) + serveur = xstrdup (value); + if ( (strncmp (tmpstr, "port=", 5) == 0) && (port == 0) ) + port = atoi (value); + if ( (strncmp (tmpstr, "prompt=", 7) == 0) && (prompt == NULL) ) + prompt = xstrdup (value); + if ( (strncmp (tmpstr, "pref=", 5) == 0) && (boisson == NULL) ) + boisson = xstrdup (value); + if ( (strncmp (tmpstr, "cmdchr=", 7) == 0) && (cmdchr == 0) ) + cmdchr = value[0]; + if ( (strncmp (tmpstr, "logout=", 7) == 0) && (logout == NULL) ) + logout = xstrdup (value); + } + fclose (file); +#ifdef DEBUG + } else { + fprintf (stderr, "Pas de fichier de config (%s).\n", configfile); +#endif + } + free (configfile); +} + + +/* + * Fonction de nettoyage + * + */ +void libere () { + if (utilisateur) free (utilisateur); + if (serveur) free (serveur); + if (boisson) free (boisson); + if (logout) free (logout); +} + + +/* + * Aide + * + */ +void Usage (int help) { + printf ("guinness by Thomas Nemeth - v %s (compilation %s : %s)\n", + VERSION, OS_TYPE, COMPIL_DATE); + if (help) + printf ("Usage : guinness [-h] [-v] [-m machine] " + "[-p port] [-u utilisateur] [-b boisson] [-q msgdx]\n" + " -h : aide sur les paramètres\n" + " -v : affiche la version\n" + " -m machine : spéficie le nom du serveur\n" + " -p port : spécifie le numéro du port\n" + " -u utilisateur : nom d'utilisateur\n" + " -b boisson : boisson préférée\n" + " -q msgdx : message d'au-revoir à la déconnexion\n" + " Online (précédé du caractère de commande \"/\") :\n" + " help : affiche l'aide sur les commandes\n" + " quit : quitte\n\n"); + libere (); + exit (1); +} + + +/* + * Traitement des arguments + * + */ +void traite_argv (int argc, char *argv[]) { + int option; + + /* Vérification des paramètres */ + while ((option = getopt (argc, argv, "hvm:p:u:b:q:")) != -1) { + switch (option) { + case 'h' : + Usage (TRUE); + break; + case 'v' : + Usage (FALSE); + break; + case 'm' : + SET_STRING (serveur, optarg); + break; + case 'p' : + port = atoi (optarg); + break; + case 'u' : + SET_STRING (utilisateur, optarg); + break; + case 'b' : + SET_STRING (boisson, optarg); + break; + case 'q' : + SET_STRING (logout, optarg); + break; + default: + Usage (TRUE); + break; + } + } + if (optind < argc) { + if (argc - optind == 1) + fprintf (stderr, "%s: option inconnue --", argv[0]); + else + fprintf (stderr, "%s: options inconnues --", argv[0]); + for ( ; optind < argc ; optind++) + fprintf (stderr, " %s", argv[optind]); + fprintf (stderr, "\n"); + Usage (TRUE); + } +} + + +/* + * Envoi de commande + * + */ +int send_cmd (int socket_client) { + char clavier[MAXSTRLEN + 1]; + char *commande; + char *admin_crypt; + int i, found = FALSE, cont = TRUE; + + /* Lecture d'une commande au clavier */ + memset (clavier, 0, MAXSTRLEN); + if ( (fgets (clavier, (int) MAXSTRLEN, stdin) == NULL) && + feof (stdin) ) { + snprintf (clavier, MAXSTRLEN, "%cquit", cmdchr); + } + + /* Détermination du type (commande explicite / message) */ + if (clavier[0] == cmdchr) + commande = xstrdup (clavier + 1); + else { + commande = xmalloc ((strlen (clavier) + 6) * sizeof (char)); + snprintf (commande, strlen (clavier) + 6, "msg %s", clavier); + } + + /* Suppression des retours à la ligne pour comparaison */ + for (i = 0 ; i < strlen (commande) ; i++) + if (commande[i] == '\n') commande[i] = '\0'; + + /* Recherche de la bonne commande */ + if (strlen (commande) > 0) + for (i = 0 ; i < nb_cmd ; i++) { + int cmp = -1; + + if (commandes[i][0] == '*') /* Commandes d'administration */ + cmp = strncmp (commandes[i]+1, commande, + strlen (commandes[i])-1); + else if (commandes[i][0] == '+') /* Commandes à intervalle */ + cmp = ( (strchr (commandes[i]+1, commande[0]) != NULL) && + ( (strlen (commande) == 1) || + (commande[1] == ' ') ) ) ? 0 : -1; + else { /* Commande simple : vérification pour la commande entière + * ou le premier caractère. + */ + cmp = ( + /* comparaison exacte entre les deux commandes + */ + (strncmp (commandes[i], commande, + strlen (commandes[i]) ) == 0) || + /* comparaison entre les 2 premières lettres en + * cas de présence de paramètres + */ + ( (commandes[i][0] == commande[0]) && + (commande[1] == ' ') ) || + /* Comparaison entre les deux premières lettres en cas + * de présence d'une unique lettre dans la commande + */ + ( (commandes[i][0] == commande[0]) && + (strlen (commande) == 1) ) ) ? 0 : -1; + /* Commande trouvée : si un seul caractère, remplacement par + * la commande complète. + */ + if ( (cmp == 0) && + (strncmp (commandes[i], commande, + strlen (commandes[i]) ) != 0) ) { + char *tmpstr; + tmpstr = xmalloc ( (strlen (commande) + + strlen (commandes[i]) ) * + sizeof (char) ); + strcpy (tmpstr, commandes[i]); + strcat (tmpstr, commande+1); +#ifdef DEBUG + printf ("Nouvelle commande = %s | %d\n", + tmpstr, strlen (tmpstr) ); +#endif + if (strlen (tmpstr) > MAXSTRLEN) { + fprintf (stderr, "Commande trop longue !\n"); + printf (prompt); + return TRUE; /* continue */ + } + free (commande); + commande = xstrdup (tmpstr); + free (tmpstr); + } +#ifdef DEBUG + printf ("l = %c / c = %c ==> %d\n", + commandes[i][0], commande[0], cmp); +#endif + } + + if (cmp == 0) { +#ifdef DEBUG + int j; + printf ("Commande reconnue : %s\n[%s (%d)] :", + commandes[i], commande, strlen (commande) ); + for (j = 0 ; j < strlen (commande) ; j++) + printf (" %d", commande[j]); + printf ("\n"); +#endif + found = TRUE; + } + } + + if (! found) { +#ifdef DEBUG + fprintf (stderr, "Commande inconnue : [%s (%d)]", + commande, strlen (commande) ); + for (i = 0 ; i < strlen (commande) ; i++) + fprintf (stderr, " %d", commande[i]); + fprintf (stderr, ".\n\n"); +#else + fprintf (stderr, "Commande inconnue : [%s]\n", commande); +#endif + printf (prompt); + return TRUE; /* continue */ + } + + /* Mode administrateur */ + if ( (strncmp (commande, "admin ", 6) == 0) && (strlen (commande) > 6) ) { + int taille; + admin_crypt = xstrdup (crypt (commande + 6, "Gs")); + free (commande); + taille = strlen ("admin ") + strlen (admin_crypt) + 1; + commande = xmalloc (taille); + snprintf (commande, taille, "admin %s", admin_crypt + 2); + } + + cont = send_infos (socket_client, commande); + + free (commande); + + return cont; +} + + +void set_cmds (char *cmds) { + char *tok, *save; + int i = 0; +#ifdef DEBUG + int j; +#endif + + nb_cmd = 0; + save = xstrdup (cmds); +#ifdef DEBUG + printf ("Commandes disponibles : \n"); +/* printf ("%s\n", cmds); */ +#endif + tok = strtok (save, "\n"); + while (tok != NULL) { + nb_cmd++; + tok = strtok (NULL, "\n"); + } + + commandes = xmalloc ((nb_cmd) * sizeof (char*)); + + tok = strtok (cmds, "\n"); + while (tok != NULL) { + commandes[i] = xstrdup (tok); +#ifdef DEBUG + if (tok[0] == '+') { + for (j = 1 ; j < strlen (commandes[i]) ; j++) + printf ("%c%c ", cmdchr, commandes[i][j]); + } else if (tok[0] != '*') { + printf ("%c%s ", cmdchr, commandes[i]); + } +#endif + i++; + tok = strtok (NULL, "\n"); + } + printf ("\n"); + + free (save); +} + + +/* + * Fonction d'initialisation de la communication + * + */ +int initiate (int socket_client) { + char cmds[MAXSTRLEN]; + char datas[MAXSTRLEN]; + char nick_ok[MAXSTRLEN]; + int cont = TRUE; + + memset (datas, 0, MAXSTRLEN); + + /* Réception de la liste des commandes */ + cont = read_infos (socket_client, cmds); + + if (cont) { + set_cmds (cmds); + + snprintf (datas, MAXSTRLEN - 1, "%s\n%s\n%s", + utilisateur, boisson, logout); + + /* Envoi des données utilisateur */ + cont = send_infos (socket_client, datas); + + read_infos (socket_client, nick_ok); + if (nick_ok[0] == '@') { + printf ("%s", nick_ok + 1); + return FALSE; + } + printf ("%s", nick_ok); + + /* Traitement des commandes et des infos */ + printf (prompt); + } + + return cont; +} + + +/* + * Boucle de commande et de réception + * + */ +void data_rw (int socket_client) { + int cont = TRUE; + char infos[MAXSTRLEN]; + fd_set lire; + + cont = initiate (socket_client); + + while (cont == TRUE) { + fflush (stdout); + + /* Construction de l'ensemble à scruter en lecture : */ + FD_ZERO (&lire); + FD_SET (STDIN_FILENO, &lire); + FD_SET (socket_client, &lire); + + /* Attente d'un message sur la liste : */ + select (socket_client + 1, &lire, NULL, NULL, NULL); + + if (FD_ISSET (STDIN_FILENO, &lire) ) { + cont = send_cmd (socket_client); + } else { + int is; + + cont = read_infos (socket_client, infos); + + is = 0; + do { + printf ("%s\n", infos + is); + while (is < MAXSTRLEN && infos[is]) is ++; + /* on suppose qu'un seul '\0' sépare deux messages */ + is ++; + } while (is < MAXSTRLEN && infos[is]); + + /* Hack pour faire afficher le message par xmessage + * Pas très joli lors des déconnexions. + */ +/* if (getenv ("DISPLAY")) { */ +/* if (! fork ()) */ +/* execlp ("xmessage", "xmessage", infos, NULL); */ +/* } */ + fflush (stdout); + printf (prompt); + } + } + + printf ("%s\n", logout); + close (socket_client); +} + + +/* + * Gestionnaire de signal SIGPIPE + * + */ +void handler_sigpipe (int sig) { + printf ("Signal SIGPIPE reçu...\n"); +} + + +/* + * Fonction principale + * + */ +int main (int argc, char *argv[]) { + int socket_client; + char *environ_user; + + signal (SIGPIPE, handler_sigpipe); + + /* Vérification des paramètres */ + traite_argv (argc, argv); + + /* Lecture du fichier de config */ + load_config (); + + /* Valeurs par défaut */ + if (! utilisateur) { + environ_user = getenv ("USER"); + if (environ_user && *environ_user) + utilisateur = xstrdup (environ_user); + else + utilisateur = xstrdup ("toto"); + } + if (IS_NOT_GOOD (serveur)) SET_STRING (serveur, DEFAULT_SERVER); + if (IS_NOT_GOOD (prompt)) SET_STRING (prompt, DEFAULT_PROMPT); + if (IS_NOT_GOOD (boisson)) SET_STRING (boisson, DEFAULT_DRINK); + if (IS_NOT_GOOD (logout)) SET_STRING (logout, DEFAULT_LOGOUT); + if (cmdchr == 0) cmdchr = '/'; + if (port == 0) port = DEFAULT_SERVER_PORT; + + printf ("Serveur : [%s]\t", serveur); + printf ("Port : [%d]\n", port); + printf ("Utilisateur : [%s]\t", utilisateur); + printf ("\tBoisson : [%s]\t", boisson); + printf ("Logout : [%s]\n", logout); + printf ("Préfixe : [%c]\n", cmdchr); + + /* Connexion au serveur */ + if ( (socket_client = connect_server (serveur, port) ) == -1) { + fprintf (stderr, "Connexion refusee...\n"); + return -1; + } + printf ("-+- Connexion acceptee. -+-\n"); + + data_rw (socket_client); + + libere (); + return 0; +} diff --git a/guinness.h b/guinness.h new file mode 100644 index 0000000..66711cf --- /dev/null +++ b/guinness.h @@ -0,0 +1,17 @@ +/* + * guinness + * architecture clients/serveur guinness. + * Thomas Nemeth -- le 15 juin 2001 + * + */ + +#ifndef GUINNESS_CLIENT +#define GUINNESS_CLIENT + +#define CONFIG_FILE ".guinnessrc" + +#define DEFAULT_SERVER "127.0.0.1" +#define DEFAULT_PROMPT "> " + + +#endif