Comment servir des pintes de Guinness "over Internet" ;)
Reprise/Fork d'un très ancien code d'un pilier de f.m.b.l : http://tnemeth.free.fr/projets/guinness-server.html
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
15 KiB
529 lines
15 KiB
/* |
|
* guinness |
|
* architecture clients/serveur guinness. |
|
* Thomas Nemeth -- le 15 juin 2001 |
|
* |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
#include <getopt.h> |
|
#include <signal.h> |
|
#include <pthread.h> |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#include <string.h> |
|
#include <strings.h> |
|
#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 : recuperation 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 parametres\n" |
|
" -v : affiche la version\n" |
|
" -m machine : speficie le nom du serveur\n" |
|
" -p port : specifie le numero du port\n" |
|
" -u utilisateur : nom d'utilisateur\n" |
|
" -b boisson : boisson preferee\n" |
|
" -q msgdx : message d'au-revoir a la deconnexion\n" |
|
" Online (precede du caractere 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; |
|
|
|
/* Verification des parametres */ |
|
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); |
|
} |
|
|
|
/* Determination 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 a 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 a intervalle */ |
|
cmp = ( (strchr (commandes[i]+1, commande[0]) != NULL) && |
|
( (strlen (commande) == 1) || |
|
(commande[1] == ' ') ) ) ? 0 : -1; |
|
else { /* Commande simple : verification pour la commande entiere |
|
* ou le premier caractere. |
|
*/ |
|
cmp = ( |
|
/* comparaison exacte entre les deux commandes |
|
*/ |
|
(strncmp (commandes[i], commande, |
|
strlen (commandes[i]) ) == 0) || |
|
/* comparaison entre les 2 premieres lettres en |
|
* cas de presence de parametres |
|
*/ |
|
( (commandes[i][0] == commande[0]) && |
|
(commande[1] == ' ') ) || |
|
/* Comparaison entre les deux premieres lettres en cas |
|
* de presence d'une unique lettre dans la commande |
|
*/ |
|
( (commandes[i][0] == commande[0]) && |
|
(strlen (commande) == 1) ) ) ? 0 : -1; |
|
/* Commande trouvee : si un seul caractere, remplacement par |
|
* la commande complete. |
|
*/ |
|
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); |
|
|
|
/* Reception 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 donnees 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 reception |
|
* |
|
*/ |
|
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 a 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' separe deux messages */ |
|
is ++; |
|
} while (is < MAXSTRLEN && infos[is]); |
|
|
|
/* Hack pour faire afficher le message par xmessage |
|
* Pas tres joli lors des deconnexions. |
|
*/ |
|
/* 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 recu...\n"); |
|
} |
|
|
|
|
|
/* |
|
* Fonction principale |
|
* |
|
*/ |
|
int main (int argc, char *argv[]) { |
|
int socket_client; |
|
char *environ_user; |
|
|
|
signal (SIGPIPE, handler_sigpipe); |
|
|
|
/* Verification des parametres */ |
|
traite_argv (argc, argv); |
|
|
|
/* Lecture du fichier de config */ |
|
load_config (); |
|
|
|
/* Valeurs par defaut */ |
|
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 ("Prefixe : [%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; |
|
}
|
|
|