next up previous contents index
Next: Exemple d'utilisation Up: Protocole FTP Previous: Protocole FTP   Contents   Index


Bibliothèque NET:FTP

La bibliothèque NET d'OpenScheme définit un certain nombre de fonctions relatives au réseau. Parmi celles-ci, on trouve les fonctions FTP suivantes. Elles seront utilisées en partie pour réaliser un petit client FTP en Scheme.

Le programme commence par déclarer les bibliothèques qui seront nécessaires : la bibliothèque réseau contenant les fonctionnalités FTP et la bibliothèque pour manipuler les chaînes de caractères sont utilisées :

; déclaration des bibliothèques utilisées
(use 'net)
(use 'str)

Nous définissons ensuite une fonction permettant d'afficher l'invite du programme ; le numéro de la commande sera aussi affiché entre crochets :

; affichage de l'invite
(define prompt (let ([n 0])
                 (lambda ()
                   (format #t "OsmFtp[~a]> " n)
                   (port-flush)
                   (set! n (+ n 1)))))

La fonction suivante lit une commande entrée à partir du prompt. On utilise la commande read-line qui lit une chaîne de caractère terminée par un saut à la ligne ou la fin de fichier. La fonction str:separate va séparer la chaîne en une liste de mots :

; lecture d'une commande de l'utilisateur
(define (get)
  (let ([v (read-line)])
    (str:separate
     (if (eof-object? v)
         "quit"
         v))))

La fonction suivante permet de vérifier que la connexion est établie avant de transmettre toute commande au serveur FTP :

; Vérification de la connexion
(define (check-ftp ftp)
  (if ftp
      #t
      (begin
       (format #t "Erreur: Connexion non ouverte\n")
       #f)))

La fonction suivante exécutera la commande open hôte. En cas de succès, une nouvelle connexion est retournée. La valeur #f est retournée en cas d'échec. Les fonctions Scheme réalisant les commandes FTP reçoivent en argument une liste de mots et elles retournent une connexion FTP. En général, la connexion FTP qu'elles retournent est identique à celle qu'elles ont reçue, à part pour les fonctions associées à la commande open (ci-dessous), à la commande quit et à la commande close. La commande open enchaîne automatiquement avec la commande user pour l'identification de l'utilisateur :

; ouvre une connexion FTP. Exécute aussi la commande user
(define (command.open ftp args)
  (if (pair? args)
      ; ouverture d'une connexion
      (let ([ftp (net:ftp:open (car args))])
        (if ftp
            (begin
              ; affichage de l'identification 
              ; du serveur FTP
              (format #t
                      "Connexion etablie:\n~a\n"
                      (net:ftp:ident ftp))
              ; identification de l'utilisateur
              (command.user ftp (cdr args)))
            (format #t
                    "Erreur: impossible d'ouvrir la connexion\n"))
        ftp)
       #f))

La fonction suivante permet à l'utilisateur de s'identifier auprès d'un serveur FTP :

; Identification de l'utilisateur
(define (command.user ftp args)
  (if (check-ftp ftp)
    ; lecture du nom du clavier ou des arguments
    (let ([name (if (null? args)
                    (begin
                     (display "Nom : ")
                     (port-flush)
                     (read-line))
                   (car args))])
      (if (and (string? name)
               (> (string-length name) 0))
        ; lecture du mot de passe
        (let ([pass (if (or (null? args)
                            (null? (cdr args)))
                      (begin
                       (display "Mot de passe : ")
                       (port-flush)
                       (read-line))
                      (cadr args))])
          (if (net:ftp:login ftp name
                             (if (string? pass)
                               pass
                               ""))
           (display "Login: ok\n")
           (display "Login: échec\n"))))))
  ftp)

La commande suivante affiche la liste des fichiers contenus dans le répertoire courant ou dans le répertoire donné en argument du serveur FTP. La fonction est une itération sur le résultat de net:ftp:first qui retourne un nom de fichier et positionne un pointeur dans la connexion FTP. La fonction net:ftp:next déplacera ce pointeur et retournera le nom suivant, ou #f s'il n'y a plus de noms de fichiers :

; affiche la liste des noms de fichiers du répertoire
; donnée en argument ou du répertoire courant de la
; connexion FTP
(define (command.ls ftp args)
  (if (check-ftp ftp)
    (let loop ([s (net:ftp:first ftp 
                                 (if (null? args)
                                    "."
                                    (car args)))])
     (if s
         (begin
           (format #t "~a " s)
           (loop (net:ftp:next ftp)))
          (display #\newline))))
  ftp)

Cette fonction est identique à la précédente, mais elle affiche la liste des noms de fichiers avec des informations supplémentaires :

; affiche la liste longue des noms de fichiers du répertoire
; donnée en argument ou du répertoire courant de la
; connexion FTP
(define (command.ll ftp args)
  (if (check-ftp ftp)
    (let loop ([s (net:ftp:long ftp 
                                (if (null? args)
                                    "."
                                    (car args)))])
      (if s
          (begin
            (format #t "~a\n" s)
            (loop (net:ftp:next ftp)))
          (display #\newline))))
  ftp)

La fonction suivante permet de changer de répertoire courant sur le serveur FTP :

; changement du répertoire courant
(define (command.cd ftp args)
  (if (check-ftp ftp)
    (if (pair? args)
      (net:ftp:chdir ftp (car args))))
  ftp)

La fonction suivante permet de récupérer une liste de fichiers à partir du serveur FTP :

; récupération de fichiers à partir du serveur
(define (command.get ftp args)
  (if (check-ftp ftp)
    (for-each (lambda (file)
               (call-with-output-file
                file
                (lambda (port)
                 (net:ftp:get ftp file port))))
              args))
  ftp)

Cette fonction permet d'envoyer une liste de fichiers locaux sur le serveur FTP, dans son répertoire courant :

; envoie de fichiers sur le serveur
(define (command.put ftp args)
  (if (check-ftp ftp)
      (for-each (lambda (file)
                  (call-with-input-file
                   file
                   (lambda (port)
                     (net:ftp:put ftp file portb))))
                args))
  ftp)

La commande suivante ferme une connexion FTP :

; fermeture de la connexion
(define (command.close ftp args)
  (if (check-ftp ftp)
      (begin
        (net:ftp:close ftp)
        (display "Connexion fermée\n")))
  #f)

Cette fonction permet de quitter le client FTP. Si la connexion n'est pas fermée, elle la termine aussi. Par convention, le client se termine lorsqu'une commande retourne le symbole 'quit :

; quitte le client en retournant le symbole 'quit
(define (command.quit ftp args)
  (if ftp (net:ftp:close ftp))
  (display "quit\n")
  'quit)

La fonction suivante est le programme principal qui démarre le client FTP. On commence par construire une liste de listes qui permettra d'associer nom de commande FTP, commande Scheme et aide en ligne dans la variable commandes. La seule commande qui ne soit pas associée à une fonction Scheme est help qui est associée au symbole help ; ceci sera traité dans l'analyse des commandes :

; programme principal
(define (ftp-client)
  ; petit message
  (display "Osm ftp client\n")
  
  ; commandes reconnues par le client
  ; Très facilement extensible
  (define commandes
    (list [list "open"
                command.open
                " [hôte]       : connexion à hôte"]
          [list "user"
                command.user
                " [nom [pass]] : identification"]
          [list "ls"
                command.ls
                " [chemin]     : listing court"]
          [list "ll"
                command.ll
                " [chemin]     : listing long"]
          [list "cd"
                command.cd
                " chemin       : changement de répertoire"]
          [list "get"
                command.get
                " fich fich ...: obtenir des fichiers"]
          [list "put"
                command.put
                " fich fich ...: envoyer des fichiers"]
          [list "close"
                command.close
                "              : fermer une connexion"]
          [list "quit"
                command.quit
                "              : quitter the client"]
          [list "help"
                'help
                "              : cette aide"]))

La fonction suivante fait partie du programme principal. Elle affiche l'aide en ligne du client FTP en affichant toutes les commandes disponibles ainsi que leur syntaxe :

  ; affiche l'aide du client
  (define (help ftp)
    (for-each (lambda (c)
                (format #t
                        "~a~a\n"
                        (car c)
                        (caddr c)))
              commandes)
    ftp)

Enfin, le programme principal entre dans une boucle alternant affichage de l'invite (prompt), lecture de la commande (get), recherche dans la table (assoc), et exécution. La recherche dans la table est faite en utilisant la fonction assoc qui recherche une association dans une liste. Si la recherche échoue ou retourne l'association concernant l'aide (symbole help), la fonction help est exécutée. Sinon, la fonction associée à la commande est invoquée avec la connexion ftp et la liste des arguments ; son résultat est utilisé pour définir la valeur du paramètre ftp de l'itération suivante :

  ; traitement des commandes de l'utilisateur
  (let loop ([ftp #f])
   (if (not (eq? ftp 'quit))
    (begin
     ; affichage de l'invite
     (prompt)

     ; lecture de la commande
     (let* ([cmd (get)]
            [op  (assoc (car cmd) commandes)])

      ; exécution de la commande et rebouclage
      (loop (if (or (not op)
                    (eq? (cadr op) 'help))
             (help ftp)
             ((cadr op) ftp (cdr cmd)))))))))

; démarrage du client FTP
(ftp-client)

Le client FTP est maintenant terminé. Il offre les principales fonctionnalités que l'on peut en attendre. Cependant, il est très facilement extensible : on pourra ajouter des fonctions issues directement de la bibliothèque NET:FTP. On pourra le doter d'une interface semi-graphique en utilisant la bibliothèque OC ou OOC (console) permettant notamment d'obtenir une ligne de commande avec édition et/ou historique ou graphique en utilisant OK ou OOK (graphique).


next up previous contents index
Next: Exemple d'utilisation Up: Protocole FTP Previous: Protocole FTP   Contents   Index
© 1993 to 2001 Erian Concept