IMA2a4 2019/2020 EC1 : Différence entre versions
(→Validation de l'épreuve) |
(→Documents) |
||
(37 révisions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 7 : | Ligne 7 : | ||
=== Objectif === | === Objectif === | ||
− | Le but est de développer un système de messagerie personnelle très léger. Vous développerez plus particulièrement un serveur IMAP. | + | Le but est de développer un système de messagerie personnelle très léger. Vous développerez plus particulièrement un serveur IMAP fonctionnant en IPv4 et en IPv6. |
=== Description du projet === | === Description du projet === | ||
Ligne 16 : | Ligne 16 : | ||
* Pas de gestion de l'identification. Le serveur n'écoute que sur l'interface loopback. Donc seul un utilisateur ayant accès à la machine pourra utiliser le serveur IMAP. Pour un accès à distance, un tunnel créé par l'option <code>-L</code> de l'utilitaire <code>ssh</code> sera utilisé. Dans la phase d'identification du client IMAP seul le nom d'utilisateur sera exploité pour retrouver les messages. Le mot de passe sera lu sans effectuer de contrôle. | * Pas de gestion de l'identification. Le serveur n'écoute que sur l'interface loopback. Donc seul un utilisateur ayant accès à la machine pourra utiliser le serveur IMAP. Pour un accès à distance, un tunnel créé par l'option <code>-L</code> de l'utilitaire <code>ssh</code> sera utilisé. Dans la phase d'identification du client IMAP seul le nom d'utilisateur sera exploité pour retrouver les messages. Le mot de passe sera lu sans effectuer de contrôle. | ||
− | * Vous utiliserez le même format de stockage des messages que celui décrit dans l'épreuve complémentaire dont le sujet est le développement d'un serveur SMTP. Les drapeaux des messages sont stockés dans le fichier d'extension <code>.status</code> en toutes lettres et un seul par ligne. Lors de la manipulation d'un message (ou de ses drapeaux) en lecture ou en écriture vous bloquerez les fichiers avec la fonction système <code>lockf</code>. | + | * Vous utiliserez le même format de stockage des messages que celui décrit dans l'épreuve complémentaire dont le sujet est le développement d'un serveur SMTP. Les drapeaux des messages sont stockés dans le fichier d'extension <code>.status</code> en toutes lettres et un seul par ligne. Lors de la manipulation d'un message (ou de ses drapeaux) en lecture ou en écriture vous bloquerez les fichiers avec la fonction système <code>lockf</code>. Prévoyez une option <code>-s</code> ou <code>--spool</code> permettant de donner le nom du répertoire racine des boites aux lettres (<code>/var/spool/mail</code> par défaut). |
− | * Vous implanterez un serveur IMAP version 2 tel que défini par la | + | * Vous implanterez un serveur IMAP version 2 tel que défini par la [https://tools.ietf.org/html/rfc1176 RFC 1176]. Vous implanterez en particulier les commandes <code>NOP</code>, <code>LOGIN</code>, <code>LOGOUT</code>, <code>SELECT</code>, <code>CHECK</code>, <code>EXPUNGE</code>, <code>COPY</code>, <code>FETCH</code> et <code>STORE</code>. Tenez-vous en aux 5 drapeaux systèmes. |
=== Validation de l'épreuve === | === Validation de l'épreuve === | ||
Ligne 30 : | Ligne 30 : | ||
== Travail effectué == | == Travail effectué == | ||
+ | |||
+ | |||
+ | '''SEMAINE 1 :''' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Durant la première semaine j'ai fait des recherches pour prendre en main le projet, comprendre le fonctionnement du protocole IMAP qui est un protocole qui permet d'accéder à ses courriers | ||
+ | électroniques directement sur les serveurs. | ||
+ | *J'ai cherché à comprendre la fonctionnement des fonctions <code>NOP</code>, <code>LOGIN</code>, <code>LOGOUT</code>, <code>SELECT</code>, <code>CHECK</code>, <code>EXPUNGE</code>, <code>COPY</code>, <code>FETCH</code> et <code>STORE</code> | ||
+ | *Et aussi le fonctionnement des 5 drapeaux systèmes <code>RECENT</code>, <code>SEEN</code>, <code>ANSWERED</code>, <code>FLAGGED</code>, <code>DELETED</code>. | ||
+ | *Ensuite, en reprenant et modifiant le serveur TCP qui a été crée pour le premier projet S&R. J'ai commencé à faire la réalisation du serveur IMAP2. | ||
+ | |||
+ | Le client se connecte et j'arrive a obtenir le TAG (A001,A002..) ainsi que la fonction (LOGIN, SELECT, FETCH...) et les arguments (ex: login mdp). | ||
+ | |||
+ | Il faut maintenant faire le traitement des information obtenu. | ||
+ | |||
+ | Pour la suite je vais commencer les fonctions "simples" du type LOGIN et LOGOUT. | ||
+ | |||
+ | |||
+ | '''SEMAINE 2 & 3 :''' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ''note : durant la semaine 2 et 3 j'ai eu des déplacements professionnels et personnels je n'ai pas pu beaucoup avancer le projet IMAP2..'' | ||
+ | |||
+ | Pendant cette période j'ai ajouté la fonction <code>LOGIN</code> et <code>LOGOUT</code> : | ||
+ | |||
+ | *La fonction <code>LOGIN</code> : Pour la fonction <code>LOGIN</code> je suis parti du principe que le serveur a une base de données avec tous les utilisateurs, | ||
+ | |||
+ | pour vérifier si l'utilisateur existe bien dans la base de données j'ai créé un fichier <code>LOGIN.txt</code> qui contient tous les utilisateurs. | ||
+ | |||
+ | Lorsqu' un utilisateur met son identifiant pour se connecter, je vérifie qu'il soit bien dans la base de données (vérification du ficher <code>LOGIN.txt</code>) s'il existe je lui renvoi : <code>"A001 OK User logged in"</code> | ||
+ | |||
+ | sinon je renvoie un message d'erreur. | ||
+ | |||
+ | *La fonction <code>LOGOUT</code> : Une fois l'utilisateur connecté il a accès aux fonctions <code>NOOP</code>, <code>SELECT</code>, <code>CHECK</code>, <code>EXPUNGE</code>, <code>COPY</code>, <code>FETCH</code>, <code>STORE</code>et enfin <code>LOGOUT</code>. | ||
+ | |||
+ | Tant qu'il ne se déconnecte pas avec la fonction <code>LOGOUT</code> , il reste connecté. Une fois qu'il se déconnecte je lui renvoie : <code>"A007 OK Logout complete"</code> | ||
+ | |||
+ | *La fonction <code>NOOP</code> : Pour cette fonction, lorsque le client m'envoie un <code>NOOP</code>, je lui réponds un simple : <code>"OK NOOP"</code>. il faudra que je rajoute le bon tag devant. | ||
+ | |||
+ | *La fonction <code>SELECT</code> : Pour la fonction <code>SELECT</code> j'ai développé deux fonctions : <code>Nombre_mail</code>, <code>Lect_Flags</code> | ||
+ | |||
+ | La fonction <code>Nombre_mail</code> parcourt le répertoire de l'utilisateur connecte (passé en paramètre de la fonction) et elle retourne le nombre de mail dans son répertoire. | ||
+ | |||
+ | La fonction <code>Lect_Flags</code> permet de lire les flags dans chacun des <code>.status</code> de chaque message. Etant donné que les messages sont stockés sous la forme d'un fichier de nom numérique | ||
+ | |||
+ | représentant l'ordre de réception des messages, il m'a suffit de faire une boucle <code>FOR</code> qui s’incrémente et j'utilise l'incrémentation pour passer d'un .status à l'autre . | ||
+ | |||
+ | <code>sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.status",user,i);</code> | ||
+ | |||
+ | le <code>%s</code> c'est l'utilisateur connecté et le <code>%d</code> l'incrément de la boucle FOR qui parcourt le nombre de mails passés en paramètres. | ||
+ | |||
+ | Pour la semaine 4 je vais continuer à optimiser les fonctions actuelles et développer la fonction <code>CHECK</code>. | ||
+ | |||
+ | '''SEMAINE 4 :''' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Durant la semaine 4 j'ai avancé sur plusieurs fonctions (version 4) : | ||
+ | |||
+ | *1-Modification de la fonction <code>Lect_Flags</code> pour renvoyer plusieurs paramètre . | ||
+ | *2-Fonction Sup_ligne qui permet de supprimer une ligne dans les fichiers <code> .status </code> . | ||
+ | *3-Amélioration de la fonction <code>SELECT</code> qui permet maintenant de supprimer les flags <code>RECENT</code> une fois la commande effectuée. | ||
+ | *4-Ajout de la fonction <code>EXPUNGE</code>. | ||
+ | *5-Ajout de la fonction <code> CHECK </code>. | ||
+ | *6-Vérification que le mot de passe soit différent d'un "string" vide. | ||
+ | |||
+ | |||
+ | |||
+ | *'''1) Fonction <code>Lect_Flags</code>''' : | ||
+ | |||
+ | Pour pouvoir "return" plusieurs variables et ne pas avoir à écrire une fonction par flag lu, j'ai ajouté un <code>struct : nombre_flags</code> | ||
+ | |||
+ | <code>typedef struct{ | ||
+ | |||
+ | int Recent; | ||
+ | |||
+ | int Deleted; | ||
+ | |||
+ | }nombre_flags;</code> | ||
+ | |||
+ | Ainsi je peux renvoyer la structure. | ||
+ | |||
+ | <code>return nombre_flag;</code> | ||
+ | |||
+ | |||
+ | |||
+ | *'''2) Fonction Sup_ligne''' : | ||
+ | |||
+ | La fonction Sup_ligne permet de supprimer une ligne dans les les fichiers <code> .status </code>. | ||
+ | |||
+ | <code>int Sup_Ligne(char * user,char * Flag, int num_mail)</code> | ||
+ | |||
+ | Elle a besoin du nom de l'utilisateur connecté, le flag à supprimer ( <code>RECENT</code>, <code>SEEN</code>, <code>ANSWERED</code>, <code>FLAGGED</code>, <code>DELETED</code>) et le numéro du message à supprimer. | ||
+ | |||
+ | Son fonctionnement est assez simple, je recopie toutes les lignes dans un autre fichier temporaire sauf celle que je souhaite supprimer. | ||
+ | |||
+ | Ensuite je supprime le premier fichier avec la fonction <code>remove</code> | ||
+ | |||
+ | <code>remove(Chemin)</code> | ||
+ | |||
+ | Puis je renomme le fichier temporaire avec le même nom que le fichier initial | ||
+ | |||
+ | <code>rename(Chemintemp,Chemin);</code> | ||
+ | |||
+ | |||
+ | |||
+ | *'''3) Amélioration de la fonction <code>SELECT</code>''' : | ||
+ | |||
+ | Une fois la fonction <code>SELECT</code> exécutée il faut enlever les flags <code>RECENT</code> dans les <code> .status </code>. Or pour cela il faut se rappeler dans qu'elle <code>.status</code> il y a un flag <code>RECENT</code> | ||
+ | |||
+ | Pour faire cela, dans la fonction <code>Lect_Flags</code> je rajoute un tableau : | ||
+ | |||
+ | <code>int *tab_sup</code> dans ce tableau à chaque fois qu'on trouve le flag passé en paramètre dans un fichier <code> .status </code> je récupère le numéro du mail et je le stocke dans le tableau et j’incrémente. | ||
+ | |||
+ | Ainsi lorsque je veux supprimer tout les Flags correspondant il me suffit de parcourir le tableau et de lancer ma fonction <code>int Sup_Ligne(char * user,char * Flag, int num_mail)</code> | ||
+ | |||
+ | Avec <code>int num_mail</code> le numéro du mail à supprimer. | ||
+ | |||
+ | <code> | ||
+ | for (i=0;i<nombre_flag_t.Recent;i++) | ||
+ | |||
+ | { | ||
+ | |||
+ | Sup_Ligne(user,Flag,tab_sup[i]); | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | *'''4) Fonction <code>EXPUNGE</code>''' : | ||
+ | |||
+ | Pour la fonction <code>EXPUNGE</code> sur le même principe dans la fonction <code>Lect_Flags</code> je rajoute un tableau qui vérifie dans les <code> .status </code> les flag <code>DELETED</code> | ||
+ | |||
+ | et je stocke le numéro de mail dans un tableau. | ||
+ | |||
+ | for (i=0;i<nombre_flag_t.Deleted;i++) | ||
+ | <code> | ||
+ | { | ||
+ | sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.status",user,tab_sup2[i]); | ||
+ | |||
+ | remove(Chemin); | ||
+ | |||
+ | sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.txt",user,tab_sup2[i]); | ||
+ | |||
+ | remove(Chemin); | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Cela permet de suprimer les <code> .status </code> et les <code> .txt </code> | ||
+ | |||
+ | |||
+ | |||
+ | *'''5) Fonction <code>CHECK</code>''' : | ||
+ | |||
+ | La fonction <code> CHECK </code> reprend les mêmes éléments que la fonction <code> SELECT </code> | ||
+ | |||
+ | |||
+ | |||
+ | *'''6) Vérification du mdp''' : | ||
+ | |||
+ | Pour que l'utilisateur se connecte, il faut qu'il soit dans la base de données fichier <code> LOGIN.txt</code> et que sont mdp soit différent d'un "string" vide sinon il a un message d'erreur. | ||
+ | |||
+ | |||
+ | <code> if ((login_ok==1) && (strcmp(arg2, "") != 0) </code> | ||
+ | |||
+ | |||
+ | Pour la semaine 5 j'ai pour objectif de finir les 3 dernières fonctions <code>COPY</code>, <code>FETCH</code> et <code>STORE</code> . | ||
+ | |||
+ | |||
+ | '''SEMAINE 5 et 6 :''' | ||
+ | |||
+ | ---- | ||
+ | Durant la semaine 5 et 6 j'ai avancé sur les fonctions Store et Copy et amélioré le code (version 5) : | ||
+ | |||
+ | *1-Fonction Store | ||
+ | *2-Fonction ENUM_nb | ||
+ | *3-Fonction Copy | ||
+ | *4-Fonction Ajout ligne | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | *'''1) Fonction Store''' : | ||
+ | |||
+ | La fonction Store permet d'ajouter ou de supprimer un Flag des fichier .status | ||
+ | |||
+ | Pour faire cela le client doit envoyer un message de ce type: | ||
+ | |||
+ | <code> A001 STORE 5</code> (numéro du mail) <code> +Flags</code> (ou -Flags)<code>\Deleted </code>(ou \Flagged,\Answered...) | ||
+ | |||
+ | Dans ma fonction, je prends en compte le numéro du mail ensuite si l’argument est de type +Flags(ajouter flags) ou -Flags(supprimer flags) et quel type de flags il faut ajouter ou supprimer (\Flagged,\Answered...) | ||
+ | |||
+ | Ensuite j’appelle la fonction sup_ligne vue précédemment pour supprimer le flag correspondant. | ||
+ | |||
+ | ou Ajout_ligne que je détaillerai par la suite. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | *'''2) Fonction ENUM_nb''' : | ||
+ | |||
+ | Pour les fonctions <code>COPY</code> et <code>FETCH</code> le client envoie un argument de type <code>1:5</code> pour dire qu'il veut soit copier soit fetch les mails (1,2,3,4,5) | ||
+ | |||
+ | La fonction Enum permet de recupérer l'argument <code>1:5</code> et d’énumérer dans un tableau <code>tab_enum</code> et d'avoir dans chaque case du tableau <code>{"1","2","3","4","5"} </code> | ||
+ | |||
+ | Cela permet pour la fonction <code>COPY</code> et <code>FETCH</code> d'avoir le bon indice de fichier. | ||
+ | |||
+ | |||
+ | *'''3) Fonction Copy''' : | ||
+ | |||
+ | La fonction <code>COPY</code> se présente de cette façon: | ||
+ | |||
+ | <code>A001 COPY 1:5</code> et <code>INBOX2</code> (INBOX2 mailbox où il faut copier les mails 1 à 5. | ||
+ | |||
+ | Comme le dit la RFC 1176 si la mailbox n'existe pas, il faut la créer. Pour cela je vérifie si elle existe | ||
+ | |||
+ | <code>if (stat(Chemin2_status, &st) == -1) </code> | ||
+ | |||
+ | Ensuite dans une Boucle <code>for</code> je parcours le tableau <code>tab_enum</code> pour récupérer les numéros de mail a copié. | ||
+ | |||
+ | |||
+ | *'''4) Fonction Ajout ligne''' : | ||
+ | |||
+ | La Fonction ajout ligne permet d'ajouter un flags dans un .status | ||
+ | |||
+ | <code>Ajt_Ligne(char *user, char *Flag, char *num_mail)</code> | ||
+ | |||
+ | Elle prend en argument le nom de l'utilisateur, le Flags à copier et le numéro du mail à copier. | ||
+ | |||
+ | |||
+ | |||
+ | '''SEMAINE 7 :''' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Durant la semaine 7 j'avance sur les fonctions FETCH, Analyse arguments, et Lect_Mail pour lire les mails, ainsi que lockf (version 6) : | ||
+ | |||
+ | |||
+ | *1-Fonction FETCH | ||
+ | *2-Fonction Lect_Mail | ||
+ | *3-Fonction Analyse arguments | ||
+ | *4-Fonction Pose_verrou | ||
+ | *5-Fonction Enleve_verrou | ||
+ | |||
+ | |||
+ | *'''1) Fonction FETCH''' : | ||
+ | |||
+ | |||
+ | |||
+ | La fonction FETCH est la fonction principale du projet elle permet d'envoyer le contenu du mail au client en fonction de ce qu'il demande. | ||
+ | |||
+ | Il y a en tout 9 choix possibles pour les clients. | ||
+ | |||
+ | |||
+ | Le client peut sélectionner chacun des éléments ci-dessous | ||
+ | |||
+ | FLAGS // '''ALL''' // '''FAST''' | ||
+ | |||
+ | INTERNALDATE // '''ALL''' // '''FAST''' | ||
+ | |||
+ | RFC822.SIZE // '''ALL''' // '''FAST''' | ||
+ | |||
+ | ENVELOPE // '''ALL''' | ||
+ | |||
+ | RFC822.HEADER // '''RFC822''' | ||
+ | |||
+ | RFC822.TEXT // '''RFC822''' | ||
+ | |||
+ | s'il sélectionne '''ALL''' alors ça équivaut à sélectionner les éléments ( FLAGS,INTERNALDATE,RFC822.SIZE,ENVELOPE) | ||
+ | |||
+ | s'il sélectionne '''FAST''' alors ça équivaut à sélectionner les éléments ( FLAGS,INTERNALDATE,RFC822.SIZE) | ||
+ | |||
+ | s'il sélectionne '''RFC822''' alors ça équivaut à sélectionner les éléments (RFC822.HEADER,RFC822.TEXT) | ||
+ | |||
+ | Il peut tous les sélectionner indépendamment par exemple : | ||
+ | |||
+ | A001 FETCH 1:2 RFC822.SIZE | ||
+ | |||
+ | Il aura alors la taille des mails 1 et 2. | ||
+ | |||
+ | Une fois que j'ai bien compris le fonctionnement, j'ai déclaré une structure avec dedans les 6 <code>int</code> (les 3 autres "ALL,FAST,RFC822" étant seulement une composition des 6 éléments à renvoyer) | ||
+ | |||
+ | <code> | ||
+ | typedef struct | ||
+ | { | ||
+ | int iEnvelope; | ||
+ | int iFlags; | ||
+ | int iRfc_header; | ||
+ | int iRfc_text; | ||
+ | int iRfc_size; | ||
+ | int iInternaldate; | ||
+ | } fetch_t; | ||
+ | </code> | ||
+ | |||
+ | |||
+ | Dans ma fonction <code>Fetch</code> je fais une vérification de l'argument si par exemple c'est '''ALL''' alors je mets à '''1''' les <code>int</code> <code>iFlags,iInternaldate,iRfc_size,iEnvelope</code> | ||
+ | |||
+ | et j'envoie ma structure à la fonction Lect_mail qui permet en fonction des <code>int mis à 1</code> de renvoyer au client l’élément du mail correspondant. | ||
+ | |||
+ | |||
+ | *'''2) Fonction Lect_Mail''' : | ||
+ | |||
+ | La fonction Lect_Mail permet de renvoyer au client l’élément du mail en fonction de l'argument 2 de la commande FETCH. | ||
+ | |||
+ | Par exemple si la commande est : | ||
+ | |||
+ | <code>'''A001 FETCH 1:3 RFC822'''</code> | ||
+ | |||
+ | les <code>int '''iRfc_header''' et '''iRfc_text'''</code> seront égaux à 1 | ||
+ | |||
+ | alors dans une boucle for qui énumère les messages 1 à 3 | ||
+ | |||
+ | on renvoie le header du mail suivi de son contenu. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | '''FLAGS''' (renvoie les flags contenus dans les .status) | ||
+ | |||
+ | |||
+ | On parcourt le fichier .status correspondant au mail et on concatène le contenu du flag dans un char nommé flag_recu. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | '''INTERNALDATE''' (renvoie la date du mail) | ||
+ | |||
+ | On récupère la date du mail avec la fonction <code>stat</code> qui renvoient des informations à propos d'un fichier | ||
+ | |||
+ | et on la concatène dans un char nommé internaldate. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | '''RFC822.SIZE''' (renvoie la taille en nombre de caractères) | ||
+ | |||
+ | On fait une lecture du nombre de caractères du mail et on la concatène dans un char nommé rfc822_size. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | '''ENVELOPE''' (on renvoie certains éléments de l’entête du mail ) | ||
+ | |||
+ | Pour récupérer uniquement le contenu de l’entête, je vérifie dans chaque ligne du mail avec la fonction <code>strstr</code> si un élément caractérise l'entête. | ||
+ | |||
+ | par exemple :"Date:", "Subject", "From:", "Sender:" sont des éléments qui caractérisent l’entête. | ||
+ | |||
+ | Et s'il y a un élément d’entête on concatène le contenu de la ligne du fichier dans un char nommé contenu_Envelope. | ||
+ | |||
+ | |||
+ | element_Envelope[9][MAX_LIGNE] = {"Date:", "Subject", "From:", "Sender:", "cc:", "In-Reply-To:", "Message-ID:", "Reply-To:", "To:"}; | ||
+ | |||
+ | |||
+ | |||
+ | '''RFC822.HEADER''' (on renvoie les éléments de l’entête du mail ) | ||
+ | |||
+ | Même fonctionnement que '''ENVELOPE''' sauf qu'il y a plus d'éléments qu'on vérifie | ||
+ | |||
+ | element_Header[14][MAX_LIGNE] = {"Date:", "Subject", "From:", "Sender:", "cc:", "In-Reply-To:", "Message-ID:", "Mail-From:", "ReSent-Date:", "ReSent-To:", "ReSent-To:", "ReSent-Message-ID:", "Reply-To:", "To:"}; | ||
+ | |||
+ | on concatène le contenu de la ligne du fichier dans un char nommé contenu_Header | ||
+ | |||
+ | |||
+ | |||
+ | '''RFC822.TEXT''' (on renvoie le contenu du mail ) | ||
+ | |||
+ | Pour récupérer le contenu du mail sans l'entête je vérifie toutes les lignes où il n'y a pas d'entête et je les récupère. | ||
+ | |||
+ | On concatène le contenu de la ligne du fichier dans un char nommé contenu_Texte | ||
+ | |||
+ | |||
+ | Ensuite on concatène tous les éléments en fonction des <code>int mis à 1</code> | ||
+ | |||
+ | puis on renvoie le contenu au client avec un fwrite. | ||
+ | |||
+ | |||
+ | *'''3) Fonction Analyse Argument''' : | ||
+ | |||
+ | La fonction Analyse argument permet de donner le nom du répertoire racine des boites aux lettres par défaut, c'est "/var/spool/mail" | ||
+ | |||
+ | elle est lancée dans le <code>main</code> | ||
+ | |||
+ | étant donné que le chemin sera le même pour tous les clients, je recopie le contenu que la fonction renvoie, à savoir le chemin des boites aux lettres | ||
+ | |||
+ | dans une variables globale nommée <code>chemin_spool</code> | ||
+ | |||
+ | S'il n’y a pas d'argument alors le chemin par défaut sera "/var/spool/mail" | ||
+ | |||
+ | pour mes essais, mes messages étant dans un autre répertoire alors j'utilise la commande : | ||
+ | |||
+ | <code>"sudo ./station --spool ../Messages"</code> | ||
+ | |||
+ | |||
+ | *'''4) Fonction Pose_verrou''' : | ||
+ | |||
+ | La fonction Pose_verrou utilise la fonction lockf qui permet de poser un verrou sur un fichier pour éviter que 2 clients ouvrent le même fichier en même temps. | ||
+ | |||
+ | A chaque ouverture de fichier, j’appelle la Fonction Pose_verrou. | ||
+ | |||
+ | <code>int lockf(int fd, int cmd, 0);</code> | ||
+ | |||
+ | Elle a besoin d'un file descriptor, une commande(F_LOCK,F_ULOCK,F_TLOCK) pour verrouiller le fichier, le déverrouiller ou tester la présence de verrou. | ||
+ | |||
+ | Je passe en argument à ma fonction le chemin du fichier à verrouiller. | ||
+ | |||
+ | Dans ma fonction, je réalise un <code>open</code> du fichier pour obtenir le file descriptor. | ||
+ | |||
+ | Ensuite je vérifie si le fichier est verrouillé. | ||
+ | <code> | ||
+ | while ((fd_test = lockf(fd, F_TEST, 0)) == -1) | ||
+ | |||
+ | { | ||
+ | |||
+ | printf("wainting unlock %d fd: %d\n", i, fd_test); | ||
+ | |||
+ | sleep(10); | ||
+ | |||
+ | i++; | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Je vérifie si le fichier est verrouillé. S'il est verrouillé, je sleep 2 secondes (pour attendre que le client qui a posé le verrou l’enlève). | ||
+ | |||
+ | Si au bout de 10 tentatives nous n'arrivons toujours pas à lire le fichier, alors on fait un TIME-OUT et on sort de la fonction. | ||
+ | |||
+ | S'il n'est pas verrouillé, je pose le verrou par la suite | ||
+ | |||
+ | et je retourne le file descriptor qui sera ensuite utilisé par la fonction Enleve_verrou. | ||
+ | |||
+ | |||
+ | *'''5) Fonction Enleve_verrou''' : | ||
+ | |||
+ | |||
+ | Elle permet d'enlever le verrou, elle prend en argument un chemin, un FILE* et un file descriptor. | ||
+ | |||
+ | Elle permet d'enlever le verrou et de fermer le fichier ouvert précédemment. | ||
+ | |||
+ | |||
+ | |||
+ | ---- | ||
+ | ---- | ||
+ | |||
+ | '''CONCLUSION''' | ||
+ | |||
+ | ---- | ||
+ | ---- | ||
+ | |||
+ | |||
+ | |||
+ | Ce projet s’est révélé très enrichissant dans la mesure où il a consisté en une approche concrète du métier d’ingénieur. En effet,il m'a permis d’étudier plusieurs "requests for comments (RFC)". | ||
+ | |||
+ | Notamment la '''RFC 1176''' qui décrit le protocole IMAP2. J'ai par ailleurs eu l'occasion de découvrir le fonctionnement de la RFC822 qui décrit le standard pour les formats des messages textuels d'ARPA sur Internet. | ||
+ | |||
+ | De plus, toutes les fonctions ont été correctement configurées, les essais ont été faits avec "netcat". Toutefois l'essai n'a pas pu être fait sur Thunderbird, faute de configuration (IMAP2); de même que le | ||
+ | |||
+ | tableau de fonctions. Plusieurs prototypes ont été testés pour ce dernier, mais aucun ne s'est avéré concluant. | ||
== Documents == | == Documents == | ||
+ | [[Fichier:IMAP_v1.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v2.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v3.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v4.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v5.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v5_2.zip]] | ||
+ | |||
+ | [[Fichier:IMAP_v6.zip]] Version du 28/08 manques encore des éléments.. | ||
+ | |||
+ | [[Fichier:IMAP_v7.zip]] VERSION FINALE |
Version actuelle datée du 30 août 2019 à 15:51
Sommaire
Présentation du projet
Contexte
La validation du semestre S8 dépend de la validation de cette épreuve complémentaire.
Objectif
Le but est de développer un système de messagerie personnelle très léger. Vous développerez plus particulièrement un serveur IMAP fonctionnant en IPv4 et en IPv6.
Description du projet
Vous développerez ce serveur IMAP sur le même modèle que le projet système réseau, en particulier concernant l'aspect structuré et multi-threadé.
Quelques directives spécifiques au serveur IMAP :
- Pas de gestion de l'identification. Le serveur n'écoute que sur l'interface loopback. Donc seul un utilisateur ayant accès à la machine pourra utiliser le serveur IMAP. Pour un accès à distance, un tunnel créé par l'option
-L
de l'utilitairessh
sera utilisé. Dans la phase d'identification du client IMAP seul le nom d'utilisateur sera exploité pour retrouver les messages. Le mot de passe sera lu sans effectuer de contrôle. - Vous utiliserez le même format de stockage des messages que celui décrit dans l'épreuve complémentaire dont le sujet est le développement d'un serveur SMTP. Les drapeaux des messages sont stockés dans le fichier d'extension
.status
en toutes lettres et un seul par ligne. Lors de la manipulation d'un message (ou de ses drapeaux) en lecture ou en écriture vous bloquerez les fichiers avec la fonction systèmelockf
. Prévoyez une option-s
ou--spool
permettant de donner le nom du répertoire racine des boites aux lettres (/var/spool/mail
par défaut). - Vous implanterez un serveur IMAP version 2 tel que défini par la RFC 1176. Vous implanterez en particulier les commandes
NOP
,LOGIN
,LOGOUT
,SELECT
,CHECK
,EXPUNGE
,COPY
,FETCH
etSTORE
. Tenez-vous en aux 5 drapeaux systèmes.
Validation de l'épreuve
L'épreuve est validée si les sources sont lisibles et si le serveur supporte un test. Le test en question consistera à créer une arborescence de messages, à lancer le serveur IMAP et à vérifier avec l'application thunderbird
si le serveur IMAP se comporte comme attendu par le client.
Matériel nécessaire
Aucun.
Planning prévisionnel
Travail effectué
SEMAINE 1 :
Durant la première semaine j'ai fait des recherches pour prendre en main le projet, comprendre le fonctionnement du protocole IMAP qui est un protocole qui permet d'accéder à ses courriers électroniques directement sur les serveurs.
- J'ai cherché à comprendre la fonctionnement des fonctions
NOP
,LOGIN
,LOGOUT
,SELECT
,CHECK
,EXPUNGE
,COPY
,FETCH
etSTORE
- Et aussi le fonctionnement des 5 drapeaux systèmes
RECENT
,SEEN
,ANSWERED
,FLAGGED
,DELETED
. - Ensuite, en reprenant et modifiant le serveur TCP qui a été crée pour le premier projet S&R. J'ai commencé à faire la réalisation du serveur IMAP2.
Le client se connecte et j'arrive a obtenir le TAG (A001,A002..) ainsi que la fonction (LOGIN, SELECT, FETCH...) et les arguments (ex: login mdp).
Il faut maintenant faire le traitement des information obtenu.
Pour la suite je vais commencer les fonctions "simples" du type LOGIN et LOGOUT.
SEMAINE 2 & 3 :
note : durant la semaine 2 et 3 j'ai eu des déplacements professionnels et personnels je n'ai pas pu beaucoup avancer le projet IMAP2..
Pendant cette période j'ai ajouté la fonction LOGIN
et LOGOUT
:
- La fonction
LOGIN
: Pour la fonctionLOGIN
je suis parti du principe que le serveur a une base de données avec tous les utilisateurs,
pour vérifier si l'utilisateur existe bien dans la base de données j'ai créé un fichier LOGIN.txt
qui contient tous les utilisateurs.
Lorsqu' un utilisateur met son identifiant pour se connecter, je vérifie qu'il soit bien dans la base de données (vérification du ficher LOGIN.txt
) s'il existe je lui renvoi : "A001 OK User logged in"
sinon je renvoie un message d'erreur.
- La fonction
LOGOUT
: Une fois l'utilisateur connecté il a accès aux fonctionsNOOP
,SELECT
,CHECK
,EXPUNGE
,COPY
,FETCH
,STORE
et enfinLOGOUT
.
Tant qu'il ne se déconnecte pas avec la fonction LOGOUT
, il reste connecté. Une fois qu'il se déconnecte je lui renvoie : "A007 OK Logout complete"
- La fonction
NOOP
: Pour cette fonction, lorsque le client m'envoie unNOOP
, je lui réponds un simple :"OK NOOP"
. il faudra que je rajoute le bon tag devant.
- La fonction
SELECT
: Pour la fonctionSELECT
j'ai développé deux fonctions :Nombre_mail
,Lect_Flags
La fonction Nombre_mail
parcourt le répertoire de l'utilisateur connecte (passé en paramètre de la fonction) et elle retourne le nombre de mail dans son répertoire.
La fonction Lect_Flags
permet de lire les flags dans chacun des .status
de chaque message. Etant donné que les messages sont stockés sous la forme d'un fichier de nom numérique
représentant l'ordre de réception des messages, il m'a suffit de faire une boucle FOR
qui s’incrémente et j'utilise l'incrémentation pour passer d'un .status à l'autre .
sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.status",user,i);
le %s
c'est l'utilisateur connecté et le %d
l'incrément de la boucle FOR qui parcourt le nombre de mails passés en paramètres.
Pour la semaine 4 je vais continuer à optimiser les fonctions actuelles et développer la fonction CHECK
.
SEMAINE 4 :
Durant la semaine 4 j'ai avancé sur plusieurs fonctions (version 4) :
- 1-Modification de la fonction
Lect_Flags
pour renvoyer plusieurs paramètre . - 2-Fonction Sup_ligne qui permet de supprimer une ligne dans les fichiers
.status
. - 3-Amélioration de la fonction
SELECT
qui permet maintenant de supprimer les flagsRECENT
une fois la commande effectuée. - 4-Ajout de la fonction
EXPUNGE
. - 5-Ajout de la fonction
CHECK
. - 6-Vérification que le mot de passe soit différent d'un "string" vide.
- 1) Fonction
Lect_Flags
:
Pour pouvoir "return" plusieurs variables et ne pas avoir à écrire une fonction par flag lu, j'ai ajouté un struct : nombre_flags
typedef struct{
int Recent;
int Deleted;
}nombre_flags;
Ainsi je peux renvoyer la structure.
return nombre_flag;
- 2) Fonction Sup_ligne :
La fonction Sup_ligne permet de supprimer une ligne dans les les fichiers .status
.
int Sup_Ligne(char * user,char * Flag, int num_mail)
Elle a besoin du nom de l'utilisateur connecté, le flag à supprimer ( RECENT
, SEEN
, ANSWERED
, FLAGGED
, DELETED
) et le numéro du message à supprimer.
Son fonctionnement est assez simple, je recopie toutes les lignes dans un autre fichier temporaire sauf celle que je souhaite supprimer.
Ensuite je supprime le premier fichier avec la fonction remove
remove(Chemin)
Puis je renomme le fichier temporaire avec le même nom que le fichier initial
rename(Chemintemp,Chemin);
- 3) Amélioration de la fonction
SELECT
:
Une fois la fonction SELECT
exécutée il faut enlever les flags RECENT
dans les .status
. Or pour cela il faut se rappeler dans qu'elle .status
il y a un flag RECENT
Pour faire cela, dans la fonction Lect_Flags
je rajoute un tableau :
int *tab_sup
dans ce tableau à chaque fois qu'on trouve le flag passé en paramètre dans un fichier .status
je récupère le numéro du mail et je le stocke dans le tableau et j’incrémente.
Ainsi lorsque je veux supprimer tout les Flags correspondant il me suffit de parcourir le tableau et de lancer ma fonction int Sup_Ligne(char * user,char * Flag, int num_mail)
Avec int num_mail
le numéro du mail à supprimer.
for (i=0;i<nombre_flag_t.Recent;i++)
{
Sup_Ligne(user,Flag,tab_sup[i]);
}
- 4) Fonction
EXPUNGE
:
Pour la fonction EXPUNGE
sur le même principe dans la fonction Lect_Flags
je rajoute un tableau qui vérifie dans les .status
les flag DELETED
et je stocke le numéro de mail dans un tableau.
for (i=0;i<nombre_flag_t.Deleted;i++)
{
sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.status",user,tab_sup2[i]);
remove(Chemin);
sprintf(Chemin,"../Messages/Utilisateur/%s/INBOX/%d.txt",user,tab_sup2[i]);
remove(Chemin); }
Cela permet de suprimer les .status
et les .txt
- 5) Fonction
CHECK
:
La fonction CHECK
reprend les mêmes éléments que la fonction SELECT
- 6) Vérification du mdp :
Pour que l'utilisateur se connecte, il faut qu'il soit dans la base de données fichier LOGIN.txt
et que sont mdp soit différent d'un "string" vide sinon il a un message d'erreur.
if ((login_ok==1) && (strcmp(arg2, "") != 0)
Pour la semaine 5 j'ai pour objectif de finir les 3 dernières fonctions COPY
, FETCH
et STORE
.
SEMAINE 5 et 6 :
Durant la semaine 5 et 6 j'ai avancé sur les fonctions Store et Copy et amélioré le code (version 5) :
- 1-Fonction Store
- 2-Fonction ENUM_nb
- 3-Fonction Copy
- 4-Fonction Ajout ligne
- 1) Fonction Store :
La fonction Store permet d'ajouter ou de supprimer un Flag des fichier .status
Pour faire cela le client doit envoyer un message de ce type:
A001 STORE 5
(numéro du mail) +Flags
(ou -Flags)\Deleted
(ou \Flagged,\Answered...)
Dans ma fonction, je prends en compte le numéro du mail ensuite si l’argument est de type +Flags(ajouter flags) ou -Flags(supprimer flags) et quel type de flags il faut ajouter ou supprimer (\Flagged,\Answered...)
Ensuite j’appelle la fonction sup_ligne vue précédemment pour supprimer le flag correspondant.
ou Ajout_ligne que je détaillerai par la suite.
- 2) Fonction ENUM_nb :
Pour les fonctions COPY
et FETCH
le client envoie un argument de type 1:5
pour dire qu'il veut soit copier soit fetch les mails (1,2,3,4,5)
La fonction Enum permet de recupérer l'argument 1:5
et d’énumérer dans un tableau tab_enum
et d'avoir dans chaque case du tableau {"1","2","3","4","5"}
Cela permet pour la fonction COPY
et FETCH
d'avoir le bon indice de fichier.
- 3) Fonction Copy :
La fonction COPY
se présente de cette façon:
A001 COPY 1:5
et INBOX2
(INBOX2 mailbox où il faut copier les mails 1 à 5.
Comme le dit la RFC 1176 si la mailbox n'existe pas, il faut la créer. Pour cela je vérifie si elle existe
if (stat(Chemin2_status, &st) == -1)
Ensuite dans une Boucle for
je parcours le tableau tab_enum
pour récupérer les numéros de mail a copié.
- 4) Fonction Ajout ligne :
La Fonction ajout ligne permet d'ajouter un flags dans un .status
Ajt_Ligne(char *user, char *Flag, char *num_mail)
Elle prend en argument le nom de l'utilisateur, le Flags à copier et le numéro du mail à copier.
SEMAINE 7 :
Durant la semaine 7 j'avance sur les fonctions FETCH, Analyse arguments, et Lect_Mail pour lire les mails, ainsi que lockf (version 6) :
- 1-Fonction FETCH
- 2-Fonction Lect_Mail
- 3-Fonction Analyse arguments
- 4-Fonction Pose_verrou
- 5-Fonction Enleve_verrou
- 1) Fonction FETCH :
La fonction FETCH est la fonction principale du projet elle permet d'envoyer le contenu du mail au client en fonction de ce qu'il demande.
Il y a en tout 9 choix possibles pour les clients.
Le client peut sélectionner chacun des éléments ci-dessous
FLAGS // ALL // FAST
INTERNALDATE // ALL // FAST
RFC822.SIZE // ALL // FAST
ENVELOPE // ALL
RFC822.HEADER // RFC822
RFC822.TEXT // RFC822
s'il sélectionne ALL alors ça équivaut à sélectionner les éléments ( FLAGS,INTERNALDATE,RFC822.SIZE,ENVELOPE)
s'il sélectionne FAST alors ça équivaut à sélectionner les éléments ( FLAGS,INTERNALDATE,RFC822.SIZE)
s'il sélectionne RFC822 alors ça équivaut à sélectionner les éléments (RFC822.HEADER,RFC822.TEXT)
Il peut tous les sélectionner indépendamment par exemple :
A001 FETCH 1:2 RFC822.SIZE
Il aura alors la taille des mails 1 et 2.
Une fois que j'ai bien compris le fonctionnement, j'ai déclaré une structure avec dedans les 6 int
(les 3 autres "ALL,FAST,RFC822" étant seulement une composition des 6 éléments à renvoyer)
typedef struct
{
int iEnvelope;
int iFlags;
int iRfc_header;
int iRfc_text;
int iRfc_size;
int iInternaldate;
} fetch_t;
Dans ma fonction Fetch
je fais une vérification de l'argument si par exemple c'est ALL alors je mets à 1 les int
iFlags,iInternaldate,iRfc_size,iEnvelope
et j'envoie ma structure à la fonction Lect_mail qui permet en fonction des int mis à 1
de renvoyer au client l’élément du mail correspondant.
- 2) Fonction Lect_Mail :
La fonction Lect_Mail permet de renvoyer au client l’élément du mail en fonction de l'argument 2 de la commande FETCH.
Par exemple si la commande est :
A001 FETCH 1:3 RFC822
les int iRfc_header et iRfc_text
seront égaux à 1
alors dans une boucle for qui énumère les messages 1 à 3
on renvoie le header du mail suivi de son contenu.
FLAGS (renvoie les flags contenus dans les .status)
On parcourt le fichier .status correspondant au mail et on concatène le contenu du flag dans un char nommé flag_recu.
INTERNALDATE (renvoie la date du mail)
On récupère la date du mail avec la fonction stat
qui renvoient des informations à propos d'un fichier
et on la concatène dans un char nommé internaldate.
RFC822.SIZE (renvoie la taille en nombre de caractères)
On fait une lecture du nombre de caractères du mail et on la concatène dans un char nommé rfc822_size.
ENVELOPE (on renvoie certains éléments de l’entête du mail )
Pour récupérer uniquement le contenu de l’entête, je vérifie dans chaque ligne du mail avec la fonction strstr
si un élément caractérise l'entête.
par exemple :"Date:", "Subject", "From:", "Sender:" sont des éléments qui caractérisent l’entête.
Et s'il y a un élément d’entête on concatène le contenu de la ligne du fichier dans un char nommé contenu_Envelope.
element_Envelope[9][MAX_LIGNE] = {"Date:", "Subject", "From:", "Sender:", "cc:", "In-Reply-To:", "Message-ID:", "Reply-To:", "To:"};
RFC822.HEADER (on renvoie les éléments de l’entête du mail )
Même fonctionnement que ENVELOPE sauf qu'il y a plus d'éléments qu'on vérifie
element_Header[14][MAX_LIGNE] = {"Date:", "Subject", "From:", "Sender:", "cc:", "In-Reply-To:", "Message-ID:", "Mail-From:", "ReSent-Date:", "ReSent-To:", "ReSent-To:", "ReSent-Message-ID:", "Reply-To:", "To:"};
on concatène le contenu de la ligne du fichier dans un char nommé contenu_Header
RFC822.TEXT (on renvoie le contenu du mail )
Pour récupérer le contenu du mail sans l'entête je vérifie toutes les lignes où il n'y a pas d'entête et je les récupère.
On concatène le contenu de la ligne du fichier dans un char nommé contenu_Texte
Ensuite on concatène tous les éléments en fonction des int mis à 1
puis on renvoie le contenu au client avec un fwrite.
- 3) Fonction Analyse Argument :
La fonction Analyse argument permet de donner le nom du répertoire racine des boites aux lettres par défaut, c'est "/var/spool/mail"
elle est lancée dans le main
étant donné que le chemin sera le même pour tous les clients, je recopie le contenu que la fonction renvoie, à savoir le chemin des boites aux lettres
dans une variables globale nommée chemin_spool
S'il n’y a pas d'argument alors le chemin par défaut sera "/var/spool/mail"
pour mes essais, mes messages étant dans un autre répertoire alors j'utilise la commande :
"sudo ./station --spool ../Messages"
- 4) Fonction Pose_verrou :
La fonction Pose_verrou utilise la fonction lockf qui permet de poser un verrou sur un fichier pour éviter que 2 clients ouvrent le même fichier en même temps.
A chaque ouverture de fichier, j’appelle la Fonction Pose_verrou.
int lockf(int fd, int cmd, 0);
Elle a besoin d'un file descriptor, une commande(F_LOCK,F_ULOCK,F_TLOCK) pour verrouiller le fichier, le déverrouiller ou tester la présence de verrou.
Je passe en argument à ma fonction le chemin du fichier à verrouiller.
Dans ma fonction, je réalise un open
du fichier pour obtenir le file descriptor.
Ensuite je vérifie si le fichier est verrouillé.
while ((fd_test = lockf(fd, F_TEST, 0)) == -1)
{
printf("wainting unlock %d fd: %d\n", i, fd_test);
sleep(10);
i++;
}
Je vérifie si le fichier est verrouillé. S'il est verrouillé, je sleep 2 secondes (pour attendre que le client qui a posé le verrou l’enlève).
Si au bout de 10 tentatives nous n'arrivons toujours pas à lire le fichier, alors on fait un TIME-OUT et on sort de la fonction.
S'il n'est pas verrouillé, je pose le verrou par la suite
et je retourne le file descriptor qui sera ensuite utilisé par la fonction Enleve_verrou.
- 5) Fonction Enleve_verrou :
Elle permet d'enlever le verrou, elle prend en argument un chemin, un FILE* et un file descriptor.
Elle permet d'enlever le verrou et de fermer le fichier ouvert précédemment.
CONCLUSION
Ce projet s’est révélé très enrichissant dans la mesure où il a consisté en une approche concrète du métier d’ingénieur. En effet,il m'a permis d’étudier plusieurs "requests for comments (RFC)".
Notamment la RFC 1176 qui décrit le protocole IMAP2. J'ai par ailleurs eu l'occasion de découvrir le fonctionnement de la RFC822 qui décrit le standard pour les formats des messages textuels d'ARPA sur Internet.
De plus, toutes les fonctions ont été correctement configurées, les essais ont été faits avec "netcat". Toutefois l'essai n'a pas pu être fait sur Thunderbird, faute de configuration (IMAP2); de même que le
tableau de fonctions. Plusieurs prototypes ont été testés pour ce dernier, mais aucun ne s'est avéré concluant.
Documents
Fichier:IMAP v6.zip Version du 28/08 manques encore des éléments..
Fichier:IMAP v7.zip VERSION FINALE