commit 127b2e86a6e7325c899760e996f3d81391e6ee73
parent e4d1c94177fea508d6388f1fbb3dcd69864389d5
Author: Tomas Hlavaty <tom@logand.com>
Date:   Wed,  7 Aug 2013 01:30:48 +0200
print-job works via wget
Diffstat:
| M | ipp.lisp |  |  | 282 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- | 
1 file changed, 206 insertions(+), 76 deletions(-)
diff --git a/ipp.lisp b/ipp.lisp
@@ -25,47 +25,62 @@
 
 (in-package :ipp)
 
-(defun read-octet (stream)
-  (if (functionp stream)
-      (funcall stream 'read-octet)
-      (read-byte stream)))
-
-(defun copy-stream (in out)
-  (handler-case (loop (write-byte (read-octet in) out))
-    (end-of-file ())))
-
-(defun read-ushort (stream)
-  (logior (ash (read-octet stream) 8)
-          (read-octet stream)))
-
-(defun read-dword (stream)
-  (logior (ash (read-octet stream) 24)
-          (ash (read-octet stream) 16)
-          (ash (read-octet stream) 8)
-          (read-octet stream)))
-
-(defun read-octets (stream n)
+(defvar *input-stream*)
+(defvar *output-stream*)
+(defvar *ipp-client*)
+
+(defun read-octet ()
+  (let ((s *input-stream*))
+    (etypecase s
+      (stream (read-byte s))
+      (function (let ((x (funcall s)))
+                  (assert (<= 0 x 256))
+                  x)))))
+
+(defun write-octet (x)
+  (assert (<= 0 x #. (1- (expt 2 8))))
+  (let ((s *output-stream*))
+    (etypecase s
+      (stream (write-byte x s))
+      (function (funcall s x)))))
+
+(defun read-ushort ()
+  (logior (ash (read-octet) 8) (read-octet)))
+
+(defun write-ushort (x)
+  (assert (<= 0 x #.(1- (expt 2 16))))
+  (write-octet (ash x -8))
+  (write-octet (logand #xff x)))
+
+(defun read-dword ()
+  (logior (ash (read-ushort) 16) (read-ushort)))
+
+(defun write-dword (x)
+  (assert (<= 0 x #.(1- (expt 2 32))))
+  (write-ushort (ash x -16))
+  (write-ushort (logand #xffff x)))
+
+(defun copy-stream ()
+  (when *input-stream*
+    (handler-case (loop (write-octet (read-octet)))
+      (end-of-file ()))))
+
+(defun read-octets (n)
   (let ((x (make-array n :element-type '(unsigned-byte 8) :initial-element 0)))
-    (if (functionp stream)
-        (let ((i 0))
-          (handler-case (do ()
-                            ((<= n i))
-                          (setf (aref x i) (read-octet stream))
-                          (incf i))
-            (end-of-file () i)))
-        (read-sequence x stream))
+    (read-sequence x *input-stream*)
     x))
 
-(defun make-data-stream (x)
+(defun write-octets (x)
   (etypecase x
-    (stream (lambda () (read-byte x nil nil)))
-    (list (lambda () (pop x)))
-    (vector (let ((n (length x))
-                  (i 0))
-              (lambda ()
-                (when (< i n)
-                  (prog1 (aref x i)
-                    (incf i))))))))
+    (stream (let ((*input-stream* x)) (copy-stream)))
+    (list (mapc 'write-octet x))
+    (vector (map 'vector 'write-octet x))))
+
+(defun string-to-octets (x) ;; TODO encoding
+  (ccl:encode-string-to-octets x))
+
+(defun octets-to-string (x) ;; TODO encoding
+  (ccl:decode-string-from-octets x))
 
 (defun tag (x)
   (let ((tags '((#x01 . :operation-attributes-tag)
@@ -102,14 +117,14 @@
 
 (defun attribute-tag (attribute)
   (cdr (assoc attribute '((:attributes-charset . :charset)
-                          (:attributes-natural-language . nil)
+                          (:attributes-natural-language . :naturalLanguage)
                           (:printer-uri . :uri)
-                          (:requesting-user-name . nil)
-                          (:job-name . nil)
-                          (:ipp-attribute-fidelity . nil)
+                          (:requesting-user-name . :nameWithoutLanguage)
+                          (:job-name . :nameWithoutLanguage)
+                          (:ipp-attribute-fidelity . :boolean)
                           (:document-name . nil)
                           (:document-format . nil)
-                          (:document-natural-language . nil)
+                          (:document-natural-language . :naturalLanguage)
                           (:compression . nil)
                           (:job-k-octets . nil)
                           (:job-impressions . nil)
@@ -122,20 +137,56 @@
 
 ;;(attribute-name :attributes-charset)
 
-(defun write-group (group control-stream)
+(defun read-attribute ()
+  (let ((tag (read-octet)))
+    (if (member tag (mapcar 'tag ;; TODO optimize
+                            '(:operation-attributes-tag
+                              :job-attributes-tag
+                              :end-of-attributes-tag
+                              :printer-attributes-tag
+                              :unsupported-attributes-tag)))
+        tag
+        `(,tag ;;(tag (attribute-tag k))
+          ,(octets-to-string (read-octets (read-ushort)))
+          , (case tag ;; TODO handle more cases
+              ((33 35)
+               (assert (= 4 (read-ushort)))
+               (read-dword))
+              (t (octets-to-string (read-octets (read-ushort)))))))))
+
+(defun read-groups ()
+  (let ((sentinel (tag :end-of-attributes-tag)) ;; TODO optimize
+        (x (read-octet)))
+    (loop
+       until (= sentinel x)
+       collect `(,x
+                 ,@(loop
+                      while (consp (setq x (read-attribute)))
+                      collect x)))))
+
+#+nil
+(with-open-file (*input-stream* "response.dat" :element-type '(unsigned-byte 8))
+  (read-ipp 314))
+
+(defun write-group (group)
   (destructuring-bind (group-id &rest plist) group
-    (when plist ;; TODO exists not null v in plist
-      (write-octet group-id control-stream)
+    (when (loop
+             for (k v) on plist by #'cddr
+             when v
+             do (return t))
+      (write-octet group-id)
       (loop
          for (k v) on plist by #'cddr
          when v
          do (let ((%k (string-to-octets (attribute-name k)))
                   (%v (string-to-octets v)))
-              (write-octet (tag (attribute-tag k)) control-stream)
-              (write-octet (length %k) control-stream)
-              (write-octets %k control-stream)
-              (write-ushort (length %v) control-stream)
-              (write-octets %v control-stream))))))
+              ;; TODO additional value (when v is list)
+              (write-octet (tag (attribute-tag k)))
+              (write-ushort (length %k))
+              (write-octets %k)
+              (write-ushort (length %v))
+              (write-octets %v))))))
+
 
 (defun operation-code (operation)
   (cdr (assoc operation '((:print-job 1 0 #x0002)
@@ -181,33 +232,47 @@
 
 ;;(operation-code :print-job)
 
-(defun write-ipp (control-stream request-id operation groups data-stream)
+(defun write-ipp (data-file request-id operation groups)
   (destructuring-bind (major minor code) (operation-code operation)
-    (write-octet major control-stream)
-    (write-octet minor control-stream)
-    (write-ushort code control-stream)
-    (write-dword request-id control-stream)
-    (dolist (group groups)
-      (write-group group control-stream))
-    (write-octet (tag :end-of-attributes-tag) control-stream)
-    (when data-stream
-      (copy-stream data-stream control-stream))))
-
-(defun read-ipp (control-stream)
+    (write-octet major)
+    (write-octet minor)
+    (write-ushort code)
+    (write-dword request-id)
+    (mapc 'write-group groups)
+    (write-octet (tag :end-of-attributes-tag))
+    (when data-file
+      (with-open-file (*input-stream* data-file :element-type '(unsigned-byte 8))
+        (copy-stream)))))
+
+(defun read-ipp (request-id)
   `(:ipp-response
-    :major ,(read-octet control-stream)
-    :minor ,(read-octet control-stream)
-    :code ,(read-ushort control-stream)
-    :request-id ,(read-dword control-stream)
-    :groups ,(read-groups control-stream)))
+    :major ,(read-octet)
+    :minor ,(read-octet)
+    :code ,(read-ushort)
+    :request-id , (let ((x (read-dword)))
+                    (assert (= x request-id))
+                    x)
+    :groups ,(read-groups)))
 
-(defun ipp (control-stream request-id operation groups data-stream)
-  (write-ipp control-stream request-id operation groups data-stream)
-  (read-ipp control-stream))
+#+nil
+(with-open-file (*input-stream* "response.dat" :element-type '(unsigned-byte 8))
+  (read-ipp 314))
 
-(defun print-job (control-stream
+(defun ipp (request-file response-file data-file request-id operation groups)
+  (with-open-file (*output-stream* request-file
+                                   :element-type '(unsigned-byte 8)
+                                   :direction :output
+                                   :if-exists :supersede
+                                   :if-does-not-exist :create)
+    (write-ipp data-file request-id operation groups))
+  (funcall *ipp-client* request-file response-file)
+  (with-open-file (*input-stream* response-file :element-type '(unsigned-byte 8))
+    (read-ipp request-id)))
+
+(defun print-job (request-file
+                  response-file
+                  data-file
                   request-id
-                  data-stream
                   attributes-charset
                   attributes-natural-language
                   printer-uri
@@ -222,7 +287,9 @@
                     job-k-octets
                     job-impressions
                     job-media-sheets)
-  (ipp control-stream
+  (ipp request-file
+       response-file
+       data-file
        request-id
        :print-job
        `((,(tag :operation-attributes-tag)
@@ -239,7 +306,70 @@
            :compression ,compression
            :job-k-octets ,job-k-octets
            :job-impressions ,job-impressions
-           :job-media-sheets ,job-media-sheets))
-       data-stream))
+           :job-media-sheets ,job-media-sheets))))
+
+(defpackage :ipp.wget
+  (:use :cl))
+
+(in-package :ipp.wget)
+
+(defun wget (url &key request-file response-file content-type)
+  (ccl:run-program
+   "wget"
+   `("-q"
+     ,@ (when request-file
+          `("--post-file" ,request-file))
+     ,@ (when response-file
+          `("-O" ,response-file))
+     ,@ (when content-type
+          `("--header" ,(format nil "Content-Type:~a" content-type)))
+     ,url)))
+
+;;(wget "http://localhost:631/printers/" :response-file "/tmp/a.html")
+;; wget|curl|lisp-http-client driver/backend
+
+(defun make-ipp-client (printer-uri)
+  (lambda (request-file response-file)
+    (wget printer-uri
+          :request-file request-file
+          :response-file response-file
+          :content-type "application/ipp")))
+
+#+nil
+(let* ((url "http://localhost:631/printers/Virtual_PDF_Printer")
+       (ipp::*ipp-client* (make-ipp-client url)))
+  (ipp::print-job "request2.dat" "response2.dat" "test.txt" 314 "utf-8" "en" url
+                  :requesting-user-name "tomas"))
+
+(defpackage :ipp.curl
+  (:use :cl))
+
+(in-package :ipp.curl)
+
+(defun curl (url &key request-file response-file content-type)
+  (ccl:run-program
+   "curl"
+   `("-s"
+     ,@ (when request-file
+          `("-d" ,(format nil "@~a" request-file)))
+     ,@ (when response-file
+          `("-o" ,response-file))
+     ,@ (when content-type
+          `("-H" ,(format nil "Content-Type:~a" content-type)))
+     ,url)))
+
+;;(curl "http://localhost:631/printers/" :response-file "/tmp/a.html")
+;; curl|curl|lisp-http-client driver/backend
+
+(defun make-ipp-client (printer-uri)
+  (lambda (request-file response-file)
+    (curl printer-uri
+          :request-file request-file
+          :response-file response-file
+          :content-type "application/ipp")))
 
-;;(print-job control-stream 1 data-stream "utf-8" "en_GB" "ipp://localhost:631/printers/myprinter")
+#+nil ;; TODO fix Bad Request response
+(let* ((url "http://localhost:631/printers/Virtual_PDF_Printer")
+       (ipp::*ipp-client* (make-ipp-client url)))
+  (ipp::print-job "request2.dat" "response2.dat" "test.txt" 314 "utf-8" "en" url
+                  :requesting-user-name "tomas"))