commit e4d1c94177fea508d6388f1fbb3dcd69864389d5
Author: Tomas Hlavaty <tom@logand.com>
Date: Thu, 1 Aug 2013 00:05:44 +0200
initial sketch
Diffstat:
A | .gitignore | | | 1 | + |
A | cl-ipp.asd | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
A | ipp.lisp | | | 245 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 284 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/cl-ipp.asd b/cl-ipp.asd
@@ -0,0 +1,38 @@
+;;; -*- lisp; -*-
+
+;;; Copyright (C) 2013 Tomas Hlavaty <tom@logand.com>
+;;;
+;;; Permission is hereby granted, free of charge, to any person
+;;; obtaining a copy of this software and associated documentation
+;;; files (the "Software"), to deal in the Software without
+;;; restriction, including without limitation the rights to use, copy,
+;;; modify, merge, publish, distribute, sublicense, and/or sell copies
+;;; of the Software, and to permit persons to whom the Software is
+;;; furnished to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be
+;;; included in all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;;; DEALINGS IN THE SOFTWARE.
+
+(defpackage :cl-ipp-system
+ (:use :asdf :cl))
+
+(in-package :cl-ipp-system)
+
+(asdf:defsystem :cl-ipp
+ :description "cl-ipp -- Internet Printing Protocol (IPP) for Common Lisp."
+ :version ""
+ :author "Tomas Hlavaty"
+ :maintainer "Tomas Hlavaty"
+ :licence "MIT"
+ :depends-on ()
+ :serial t
+ :components ((:file "ipp")))
diff --git a/ipp.lisp b/ipp.lisp
@@ -0,0 +1,245 @@
+;;; Copyright (C) 2013 Tomas Hlavaty <tom@logand.com>
+;;;
+;;; Permission is hereby granted, free of charge, to any person
+;;; obtaining a copy of this software and associated documentation
+;;; files (the "Software"), to deal in the Software without
+;;; restriction, including without limitation the rights to use, copy,
+;;; modify, merge, publish, distribute, sublicense, and/or sell copies
+;;; of the Software, and to permit persons to whom the Software is
+;;; furnished to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be
+;;; included in all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;;; DEALINGS IN THE SOFTWARE.
+
+(defpackage :ipp
+ (:use :cl))
+
+(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)
+ (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))
+ x))
+
+(defun make-data-stream (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))))))))
+
+(defun tag (x)
+ (let ((tags '((#x01 . :operation-attributes-tag)
+ (#x02 . :job-attributes-tag)
+ (#x03 . :end-of-attributes-tag)
+ (#x04 . :printer-attributes-tag)
+ (#x05 . :unsupported-attributes-tag)
+ (#x10 . :unsupported)
+ (#x12 . :unknown)
+ (#x13 . :no-value)
+ (#x21 . :integer)
+ (#x22 . :boolean)
+ (#x23 . :enum)
+ (#x30 . :octetString)
+ (#x31 . :dateTime)
+ (#x32 . :resolution)
+ (#x33 . :rangeOfInteger)
+ (#x35 . :textWithLanguage)
+ (#x36 . :nameWithLanguage)
+ (#x41 . :textWithoutLanguage)
+ (#x42 . :nameWithoutLanguage)
+ (#x44 . :keyword)
+ (#x45 . :uri)
+ (#x46 . :uriScheme)
+ (#x47 . :charset)
+ (#x48 . :naturalLanguage)
+ (#x49 . :mimeMediaType))))
+ (etypecase x
+ (integer (cdr (assoc x tags)))
+ (keyword (car (rassoc x tags))))))
+
+;;(tag #x45)
+;;(tag :uri)
+
+(defun attribute-tag (attribute)
+ (cdr (assoc attribute '((:attributes-charset . :charset)
+ (:attributes-natural-language . nil)
+ (:printer-uri . :uri)
+ (:requesting-user-name . nil)
+ (:job-name . nil)
+ (:ipp-attribute-fidelity . nil)
+ (:document-name . nil)
+ (:document-format . nil)
+ (:document-natural-language . nil)
+ (:compression . nil)
+ (:job-k-octets . nil)
+ (:job-impressions . nil)
+ (:job-media-sheets . nil)))))
+
+;;(attribute-tag :printer-uri)
+
+(defun attribute-name (attribute)
+ (format nil "~(~a~)" attribute))
+
+;;(attribute-name :attributes-charset)
+
+(defun write-group (group control-stream)
+ (destructuring-bind (group-id &rest plist) group
+ (when plist ;; TODO exists not null v in plist
+ (write-octet group-id control-stream)
+ (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))))))
+
+(defun operation-code (operation)
+ (cdr (assoc operation '((:print-job 1 0 #x0002)
+ (:validate-job 1 0 #x0004)
+ (:create-job 1 1 #x0005)
+ (:send-document 1 1 #x0006)
+ (:cancel-job 1 0 #x0008)
+ (:get-job-attributes 1 0 #x0009)
+ (:get-jobs 1 0 #x000a)
+ (:get-printer-attributes 1 0 #x000b)
+ (:hold-job 1 1 #x000c)
+ (:release-job 1 1 #x000d)
+ (:restart-job 1 1 #x000e)
+ (:pause-printer 1 0 #x0010)
+ (:resume-printer 1 0 #x0011)
+ (:purge-jobs 1 0 #x0012)
+ (:set-job-attributes 1 1 #x0014)
+ (:create-printer-subscription 1 2 #x0016)
+ (:create-job-subscription 1 2 #x0017)
+ (:get-subscription-attributes 1 2 #x0018)
+ (:get-subscriptions 1 2 #x0019)
+ (:renew-subscription 1 2 #x001a)
+ (:cancel-subscription 1 2 #x001b)
+ (:get-notifications 1 2 #x001c)
+ (:enable-printer 1 2 #x0022)
+ (:disable-printer 1 2 #x0023)
+ (:cups-get-default 1 0 #x4001)
+ (:cups-get-printers 1 0 #x4002)
+ (:cups-add-modify-printer 1 0 #x4003)
+ (:cups-delete-printer 1 0 #x4004)
+ (:cups-get-classes 1 0 #x4005)
+ (:cups-add-modify-class 1 0 #x4006)
+ (:cups-delete-class 1 0 #x4007)
+ (:cups-accept-jobs 1 0 #x4008)
+ (:cups-reject-jobs 1 0 #x4009)
+ (:cups-set-default 1 0 #x400a)
+ (:cups-get-devices 1 1 #x400b)
+ (:cups-get-ppds 1 1 #x400c)
+ (:cups-move-job 1 1 #x400d)
+ (:cups-authenticate-job 1 2 #x400e)
+ (:cups-get-ppd 1 3 #x400f)
+ (:cups-get-document 1 4 #x4027)))))
+
+;;(operation-code :print-job)
+
+(defun write-ipp (control-stream request-id operation groups data-stream)
+ (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)
+ `(: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)))
+
+(defun ipp (control-stream request-id operation groups data-stream)
+ (write-ipp control-stream request-id operation groups data-stream)
+ (read-ipp control-stream))
+
+(defun print-job (control-stream
+ request-id
+ data-stream
+ attributes-charset
+ attributes-natural-language
+ printer-uri
+ &key
+ requesting-user-name
+ job-name
+ ipp-attribute-fidelity
+ document-name
+ document-format
+ document-natural-language
+ compression
+ job-k-octets
+ job-impressions
+ job-media-sheets)
+ (ipp control-stream
+ request-id
+ :print-job
+ `((,(tag :operation-attributes-tag)
+ :attributes-charset ,attributes-charset
+ :attributes-natural-language ,attributes-natural-language
+ :printer-uri ,printer-uri)
+ (,(tag :job-attributes-tag)
+ :requesting-user-name ,requesting-user-name
+ :job-name ,job-name
+ :ipp-attribute-fidelity ,ipp-attribute-fidelity
+ :document-name ,document-name
+ :document-format ,document-format
+ :document-natural-language ,document-natural-language
+ :compression ,compression
+ :job-k-octets ,job-k-octets
+ :job-impressions ,job-impressions
+ :job-media-sheets ,job-media-sheets))
+ data-stream))
+
+;;(print-job control-stream 1 data-stream "utf-8" "en_GB" "ipp://localhost:631/printers/myprinter")