bot.scm
(module bot mzscheme
        (require (lib "url.ss" "net"))
        (require (lib "uri-codec.ss" "net"))
        (require (planet "roos.scm" ("oesterholt" "roos.plt" 1 3)))
        (provide form-data
                 (all-from (planet "roos.scm" ("oesterholt" "roos.plt" 1 3))) 
                 (all-from (lib "url.ss" "net"))
                 )
         
        ;;;; multipart/form-data
        (def-class 
         (roos-doc (sp "This class can be used to post forms using the " (s% "multipart/form-data") " content-type."
                       "It can be used to act as a posting client, to post forms to websites, e.g. a MediaWiki."
                       )
                   (sp (s== "Synopsys"))
                   (sp (sverb "(require (planet \"bot.scm\" (\"oesterholt\" \"webbot.plt\" 1 0))"
                              "(define F (form-data))"
                              "(-> F form-add 'name \"A name\")"
                              "(-> F form-add 'file (build-path \"my-file.txt\"))"
                              "(let ((p (-> F form-post (string->url \"http://test.form.org/post-it.php\"))))"
                              "   (display (read-string 1000000 p))"
                              "   (close-input-port p))"
                              "(exit)")))
         (this (form-data))
         (supers)
         ;;;;     private
         (private
          (define _form      '())
          (define _boundary  "87689sadfgioawgfaout786TUGDQO@ggdfyudfsgajsguygOI*USW")
          
           (define (read-in path)
             (define (rd port)
               (let ((r (read-string 1024 port)))
                 (if (eq? r eof)
                     (begin
                       (close-input-port port)
                       "")
                     (string-append r (rd port)))))
             (rd (open-input-file path)))
          )
         ;;;;     public
         (public
          ;;;;        form-add
          ((define (sp "This member can be used to add a (form) field to be posted. It automatically does the "
                       "right thing given it's input value. If value is of type " (s% 'path?) " the form will "
                       "add the file to be uploaded; otherwise the string representation of the input value "
                       "is taken (using " (s% "format") ".")
                   (sp (s% "Input parameters: ") (s% 'name:<symbol>) ", " (s% 'value:<anytype>) ".")
                   (sp (s% "Returns         : ") (s% 'this)))
            (form-add name value)
              (set! _form (cons (cons name (format "~a" value)) _form))
              this)
          
          ;;;;        form-clear
          ((define (sp "Clears the form. New fields can be added.")
                   (sp (s% "Returns         : ") (s% 'this)))
             (form-clear)
               (set! _form '())
               this)
          
          ;;;;        form->bytes
          ((define (sp "Converts the form to a 'bytes' representation.")
                   (sp (s% "Returns         : ") "The converted form"))
             (form->bytes) 
               (values (list (string-append "Content-type: multipart/form-data, boundary=" _boundary)
                             )
                       (string->bytes/utf-8
                        (apply string-append
                               (map (lambda (field-value)
                                      (string-append (format "--~a~%" _boundary)
                                                     (if (path? (cdr field-value))
                                                         (string-append
                                                          (format "content-disposition: form-data; name=\"~a\"; filename=\"~a\"~%"
                                                                  (symbol->string (car field-value))
                                                                  (call-with-values (lambda () (split-path (cdr field-value)))
                                                                                    (lambda (p n x) n)))
                                                          (format "Content-Transfer-Encoding: binary~%")
                                                          (format "~%")
                                                          (read-in (cdr field-value)))
                                                         (string-append
                                                          (format "content-disposition: form-data; name=\"~a\"~%" (symbol->string (car field-value)))
                                                          (format "~%")
                                                          (format "~a~%" (cdr field-value))))))
                                    _form)))))
          ;;;;        form-post
          ((define (sp "Posts the form to the given " (s% 'url))
                   (sp (s% "Input parameters: ") (s% 'url:<url>) " (see the " (s% "net") " collection)")
                   (sp (s% "Returns         : ") (s% "<input-port>") " (can be used to read the results of the post")) 
            (form-post url)
              (call-with-values (lambda () (-> this form->bytes))
                                (lambda (content-types data)
                                  (post-pure-port url data content-types))))
          )
         ;;;;     constructor
         ((constructor (sp "Takes no arguments")))
         )
        
        
        );;; module