Update domain name to kpe.io
[clsql.git] / db-postgresql-socket / postgresql-socket-api.lisp
index 31756efc8350aec8c21a2e370a36bb652e237d9b..40dc86a780d46d88c1a146b7ecbced867cc6eaee 100644 (file)
@@ -7,9 +7,7 @@
 ;;;; Authors:  Kevin M. Rosenberg based on original code by Pierre R. Mai
 ;;;; Created:  Feb 2002
 ;;;;
-;;;; $Id$
-;;;;
-;;;; This file, part of CLSQL, is Copyright (c) 2002-2004 by Kevin M. Rosenberg
+;;;; This file, part of CLSQL, is Copyright (c) 2002-2010 by Kevin M. Rosenberg
 ;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
 ;;;;
 ;;;; CLSQL users are granted the rights to distribute and use this software
 
 (in-package #:postgresql-socket)
 
+;; KMR: 2011-06-12
+;; FIXME: The file has code specific to sb-unicode and CCL
+;; to assume UTF8 encoded strings.
+;; Best fix would be to use the user-specified encoding that is now
+;; stored in the database object and use the UFFI 2.x encoding functions
+;; to convert strings to/from octet vectors. This allows encoding
+;; other than UTF8 and also works on all CL implementations that
+;; support wide character strings
+
 (uffi:def-enum pgsql-ftype
     ((:bytea 17)
      (:int2 21)
@@ -107,13 +114,19 @@ socket interface"
 (defun send-socket-value-string (socket value)
   (declare (type stream socket)
            (type string value))
-  #-sb-unicode
+  #-(or sb-unicode ccl)
   (loop for char across value
-        for code = (char-code char)
-        do (write-byte code socket)
-        finally (write-byte 0 socket))
+     for code = (char-code char)
+     do (write-byte code socket)
+     finally (write-byte 0 socket))
+  #+ccl
+  (write-sequence (ccl:encode-string-to-octets
+                   value :external-format :utf-8) socket)
+  #+ccl
+  (write-byte 0 socket)
   #+sb-unicode
-  (write-sequence (sb-ext:string-to-octets value :null-terminate t) socket)
+  (write-sequence (sb-ext:string-to-octets value :null-terminate t)
+                  socket)
   nil)
 
 (defun send-socket-value-limstring (socket value limit)
@@ -152,24 +165,33 @@ socket interface"
   (declare (type stream socket))
   (read-byte socket))
 
+
 (defun read-socket-value-string (socket)
   (declare (type stream socket))
-  #-sb-unicode
+  #-(or sb-unicode ccl)
   (with-output-to-string (out)
     (loop for code = (read-byte socket)
-          until (zerop code)
-          do (write-char (code-char code) out)))
+       until (zerop code)
+       do (write-char (code-char code) out)))
+  #+ccl
+  (let ((bytes (make-array 64
+                           :element-type '(unsigned-byte 8)
+                           :adjustable t
+                           :fill-pointer 0)))
+    (loop for code = (read-byte socket)
+       until (zerop code)
+       do (vector-push-extend code bytes))
+    (ccl:decode-string-from-octets bytes :external-format :utf-8))
   #+sb-unicode
   (let ((bytes (make-array 64
                            :element-type '(unsigned-byte 8)
                            :adjustable t
                            :fill-pointer 0)))
     (loop for code = (read-byte socket)
-          until (zerop code)
+       until (zerop code)
           do (vector-push-extend code bytes))
     (sb-ext:octets-to-string bytes)))
 
-
 (defmacro define-message-sender (name (&rest args) &rest clauses)
   (let ((socket-var (gensym))
         (body nil))
@@ -212,15 +234,30 @@ socket interface"
   (int32 pid)
   (int32 key))
 
+(defun read-bytes (socket length)
+  "Read a byte array of the given length from a stream."
+  (declare (type stream socket)
+           (type fixnum length)
+           (optimize (speed 3) (safety 0)))
+  (let ((result (make-array length :element-type '(unsigned-byte 8))))
+    (read-sequence result socket)
+    result))
 
 (defun read-socket-sequence (stream length &optional (allow-wide t))
   (declare (stream stream)
            (optimize (speed 3) (safety 0)))
-  #-sb-unicode
+  #-(or sb-unicode ccl)
   (let ((result (make-string length)))
     (dotimes (i length result)
       (declare (fixnum i))
       (setf (char result i) (code-char (read-byte stream)))))
+  #+ccl
+  (let ((bytes (make-array length :element-type '(unsigned-byte 8))))
+    (declare (type (simple-array (unsigned-byte 8) (*)) bytes))
+    (read-sequence bytes stream)
+    (if allow-wide
+       (ccl:decode-string-from-octets bytes :external-format :utf-8)
+       (map 'string #'code-char bytes)))
   #+sb-unicode
   (let ((bytes (make-array length :element-type '(unsigned-byte 8))))
     (declare (type (simple-array (unsigned-byte 8) (*)) bytes))
@@ -458,10 +495,16 @@ troubles."
                                :database database :user user
                                :password (or password ""))))
 
-(defun encrypt-md5 (plaintext salt)
-  (string-downcase
-   (format nil "~{~2,'0X~}"
-           (coerce (md5sum-sequence (concatenate 'string plaintext salt)) 'list))))
+(defun byte-sequence-to-hex-string (sequence)
+  (string-downcase (format nil "~{~2,'0X~}" (coerce sequence 'list))))
+
+(defun encrypt-password-md5 (password user salt)
+  (let ((pass1 (byte-sequence-to-hex-string
+               (md5::md5sum-string (concatenate 'string password user)))))
+    (byte-sequence-to-hex-string
+     (md5:md5sum-sequence (concatenate '(vector (unsigned-byte 8))
+                                  (map '(vector (unsigned-byte 8)) #'char-code pass1)
+                                  salt)))))
 
 (defun reopen-postgresql-connection (connection)
   "Reopen the given PostgreSQL connection.  Closes any existing
@@ -503,10 +546,11 @@ connection, if it is still open."
                          (postgresql-connection-password connection) salt)))
                      (force-output socket))
                     (5
-                     (let ((salt (read-socket-sequence socket 4 nil)))
-                       (let* ((pwd2 (encrypt-md5 (postgresql-connection-password connection)
-                                                 (postgresql-connection-user connection)))
-                              (pwd (encrypt-md5 pwd2 salt)))
+                     (let ((salt (read-bytes socket 4)))
+                       (let ((pwd (encrypt-password-md5
+                                  (postgresql-connection-password connection)
+                                  (postgresql-connection-user connection)
+                                  salt)))
                          (send-encrypted-password-message
                           socket
                           (concatenate 'string "md5" pwd))))