cl-ipp

Internet Printing Protocol (IPP) for Common Lisp
git clone https://logand.com/git/cl-ipp.git/
Log | Files | Refs

commit e4d1c94177fea508d6388f1fbb3dcd69864389d5
Author: Tomas Hlavaty <tom@logand.com>
Date:   Thu,  1 Aug 2013 00:05:44 +0200

initial sketch

Diffstat:
A.gitignore | 1+
Acl-ipp.asd | 38++++++++++++++++++++++++++++++++++++++
Aipp.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")