[cl-smtp-cvs] CVS cl-smtp

CVS User jidzikowski jidzikowski at common-lisp.net
Mon Jan 21 09:39:26 UTC 2013


Update of /project/cl-smtp/cvsroot/cl-smtp
In directory tiger.common-lisp.net:/tmp/cvs-serv20343

Modified Files:
	CHANGELOG cl-smtp.lisp tests.lisp 
Log Message:
check message/html-message for non ascii characters, 
when found non ascii characters send messge/html-message
encoded quoted-printable



--- /project/cl-smtp/cvsroot/cl-smtp/CHANGELOG	2010/09/08 15:02:32	1.20
+++ /project/cl-smtp/cvsroot/cl-smtp/CHANGELOG	2013/01/21 09:39:26	1.21
@@ -1,3 +1,10 @@
+Version 20130118
+2013.01.18
+Add string-has-non-ascii, rfc2045-q-encode-string-to-stream,
+to send quoted-printable messages
+Change write-rfc8822-message
+Change cl-smtp.lisp, tests.lisp, CHANGELOG
+
 Version 20100908.2
 2010.09.08
 Add write-rfc8822-message, to write a rfc8822 compatible mail.
--- /project/cl-smtp/cvsroot/cl-smtp/cl-smtp.lisp	2010/09/08 15:02:32	1.20
+++ /project/cl-smtp/cvsroot/cl-smtp/cl-smtp.lisp	2013/01/21 09:39:26	1.21
@@ -56,10 +56,19 @@
       (mask str))
     resultstr))
 
-(defun string-to-base64-string (str)
-  (declare (ignorable str))
-  #+allegro (excl:string-to-base64-string str :wrap-at-column nil)
-  #-allegro (cl-base64:string-to-base64-string str))
+(defun string-to-base64-string (str &key (external-format :utf-8)
+                                (columns 80))
+  (let ((exformat (flex:make-external-format external-format)))
+  #+allegro (excl:usb8-array-to-base64-string 
+             (flex:string-to-octets str :external-format exformat)
+             :wrap-at-column columns)
+  #-allegro (cl-base64:usb8-array-to-base64-string 
+             (flex:string-to-octets str :external-format exformat)
+             :columns columns)))
+
+(defun string-has-non-ascii (str)
+  (loop for c across str
+     when (< 127 (char-code c)) do (return t)))
 
 (defun rfc2045-q-encode-string (str &key (external-format :utf-8))
   (let ((line-has-non-ascii nil)
@@ -75,7 +84,7 @@
               (loop for byte across (flex:string-to-octets 
                                      (make-string 1 :initial-element c)
                                      :external-format exformat)
-                 do (format s "=~2,'0X" byte)))
+                 do (format s "~:@(=~2,'0X~)" byte)))
              (t 
               (when line-has-non-ascii
                 (format s "?=")
@@ -84,6 +93,42 @@
       (when line-has-non-ascii
         (format s "?=")))))
 
+(defun rfc2045-q-encode-string-to-stream (str stream 
+                                          &key (external-format :utf-8) 
+                                          (columns 74))
+  (let ((exformat (flex:make-external-format external-format))
+        (last-line-break 0)
+        (len (length str)))
+    (loop for c across str
+          for n from 0 to len 
+          for column = (- n last-line-break)
+          for nc = (when (< (+ n 1) len) (elt str (+ n 1)))
+       do
+         (when (>= column columns)
+           (write-char #\= stream)
+           (write-blank-line stream)
+           (setf last-line-break n))
+         (cond
+           ((char= c #\NewLine)
+            (setf last-line-break n)
+            (write-char c stream))
+           ((or (char= c #\Space)
+                (char= c #\Tab))
+            (if (char= nc #\NewLine)
+                (format stream "~:@(=~2,'0X~)" (char-code c))
+                (write-char c stream)))
+           ((or (< 127 (char-code c))
+                (> 33 (char-code c))
+                (char= c #\=))
+            (loop for byte across (flex:string-to-octets 
+                                     (make-string 1 :initial-element c)
+                                     :external-format exformat)
+                 do (format stream "~:@(=~2,'0X~)" byte)))
+           (t
+            (write-char c stream))
+           ))
+    ))
+
 (defun substitute-return-newline (str)
   "Replace all occurences of \r\n in STR with spaces"
   (let ((resultstr ""))
@@ -178,7 +223,8 @@
 (defun send-email (host from to subject message 
 		   &key ssl (port (if (eq :tls ssl) 465 25)) cc bcc reply-to extra-headers
 		   html-message display-name authentication
-		   attachments (buffer-size 256) envelope-sender (external-format :utf-8))
+		   attachments (buffer-size 256) envelope-sender 
+                   (external-format :utf-8) local-hostname)
   (send-smtp host from (check-arg to "to") subject (mask-dot message)
 	     :port port :cc (check-arg cc "cc") :bcc (check-arg bcc "bcc")
 	     :reply-to reply-to 
@@ -192,7 +238,8 @@
 			      256)
 	     :envelope-sender (or envelope-sender from)
              :external-format external-format
-	     :ssl ssl))
+	     :ssl ssl
+             :local-hostname (or local-hostname (usocket::get-host-name))))
 
 (defun send-smtp (host from to subject message
                   &key ssl (port (if (eq :tls ssl) 465 25)) cc bcc
@@ -403,6 +450,9 @@
                               display-name attachments buffer-size
                               (external-format :utf-8))
   (let* ((boundary (make-random-boundary))
+         (message-transfer-encoding (when (string-has-non-ascii message)
+                                      "quoted-printable"))
+                                      
          (html-boundary (if (and attachments html-message)
                             (make-random-boundary)
                             boundary))
@@ -415,7 +465,8 @@
                        :cc cc
                        :reply-to reply-to
                        :display-name display-name 
-                       :extra-headers extra-headers :subject subject)
+                       :extra-headers extra-headers :subject subject
+                       :external-format external-format)
     (when (or attachments html-message)
       (send-multipart-headers stream
                               :attachment-boundary (when attachments boundary) 
@@ -436,31 +487,52 @@
              (write-blank-line stream)
              (generate-message-header 
               stream :boundary html-boundary :content-type content-type 
+              :content-transfer-encoding message-transfer-encoding
               :content-disposition "inline" :include-blank-line? nil)))
           (attachments 
            (generate-message-header 
             stream :boundary boundary 
             :content-type content-type :content-disposition "inline"
+            :content-transfer-encoding message-transfer-encoding
             :include-blank-line? nil))
           (html-message
            (generate-message-header 
             stream :boundary html-boundary :content-type content-type 
+            :content-transfer-encoding message-transfer-encoding
             :content-disposition "inline"))
           (t 
-           (generate-message-header stream :content-type content-type
-                                    :include-blank-line? nil)))
+           (generate-message-header 
+            stream :content-type content-type
+            :content-transfer-encoding message-transfer-encoding
+            :include-blank-line? nil)))
+    (write-blank-line stream)
+    (if message-transfer-encoding
+        (progn
+          (print-debug (format nil "to server body quoted-printable: ~A"
+                               message))
+          (rfc2045-q-encode-string-to-stream message stream 
+                                             :external-format external-format))
+        (write-to-smtp stream message))
     (write-blank-line stream)
-    (write-to-smtp stream message)
     (write-blank-line stream)
     ;;---------- Send  Html text if needed -------------------------
     (when html-message
-      (generate-message-header 
-       stream :boundary html-boundary 
-       :content-type (format nil "text/html; charset=~S" 
-                             (string-upcase (symbol-name external-format)))
-       :content-disposition "inline")
-      (write-to-smtp stream html-message)
-      (send-end-marker stream html-boundary))
+      (let ((non-ascii-p (string-has-non-ascii html-message)))
+        (generate-message-header 
+         stream :boundary html-boundary 
+         :content-type (format nil "text/html; charset=~S" 
+                               (string-upcase (symbol-name external-format)))
+         :content-transfer-encoding (when non-ascii-p "quoted-printable")
+         :content-disposition "inline")
+        (if non-ascii-p
+            (progn
+              (print-debug 
+               (format nil "to server html-message quoted-printable: ~A"
+                       html-message))
+              (rfc2045-q-encode-string-to-stream 
+               html-message stream :external-format external-format))
+            (write-to-smtp stream html-message))
+        (send-end-marker stream html-boundary)))
     ;;---------- Send Attachments -----------------------------------
     (when attachments
       (dolist (attachment attachments)
--- /project/cl-smtp/cvsroot/cl-smtp/tests.lisp	2010/06/21 09:06:27	1.2
+++ /project/cl-smtp/cvsroot/cl-smtp/tests.lisp	2013/01/21 09:39:26	1.3
@@ -14,6 +14,51 @@
 (defun get-component-pathname ()
   (asdf:component-pathname (asdf:find-system "cl-smtp")))
 
+
+
+(define-cl-smtp-test "rfc2045-q-encode-string-to-stream-1" ()
+  (let* ((str "öüäÖÜÄß")
+         (qstr (with-output-to-string (s)
+                 (rfc2045-q-encode-string-to-stream 
+                  str s :external-format :utf-8))))
+    (assert qstr)
+    (assert (string-equal qstr "=C3=B6=C3=BC=C3=A4=C3=96=C3=9C=C3=84=C3=9F"))))
+
+(define-cl-smtp-test "rfc2045-q-encode-string-to-stream-2" ()
+  (let* ((str "öüäÖÜÄß")
+         (qstr (with-output-to-string (s)
+                 (rfc2045-q-encode-string-to-stream 
+                  str s :external-format :latin-1))))
+    (assert qstr)
+    (assert (string-equal qstr "=F6=FC=E4=D6=DC=C4=DF"))))
+
+(define-cl-smtp-test "rfc2045-q-encode-string-to-stream-3" ()
+  (let* ((str "check if #\= encoded")
+         (qstr (with-output-to-string (s)
+                 (rfc2045-q-encode-string-to-stream 
+                  str s :external-format :latin-1))))
+    (assert qstr)
+    (assert (string-equal qstr "check if #\=3D encoded"))))
+
+(define-cl-smtp-test "rfc2045-q-encode-string-to-stream-4" ()
+  (let* ((str "Müde vom Durchwandern öder Letternwüsten, voll leerer Hirngeburten, in anmaaßendsten Wortnebeln ; überdrüssig ästhetischer Süßler wie grammatischer Wässerer ; entschloß ich mich : Alles, was je schrieb, in Liebe und Haß, als immerfort mitlebend zu behandeln !")
+         (qstr (with-output-to-string (s)
+                 (rfc2045-q-encode-string-to-stream 
+                  str s :external-format :latin-1 :columns 64))))
+    (assert qstr)
+    (assert (string-equal qstr "M=FCde vom Durchwandern =F6der Letternw=FCsten, voll leerer Hirngeburt=
+en, in anmaa=DFendsten Wortnebeln ; =FCberdr=FCssig =E4sthetischer S=FC=DFle=
+r wie grammatischer W=E4sserer ; entschlo=DF ich mich : Alles, was j=
+e schrieb, in Liebe und Ha=DF, als immerfort mitlebend zu behandel=
+n !"
+))))
+
+(define-cl-smtp-test "string-has-non-ascii-1" ()
+  (assert (string-has-non-ascii "test Ü ende")))
+
+(define-cl-smtp-test "string-has-non-ascii-2" ()
+  (assert (not (string-has-non-ascii "test ende"))))
+
 (define-cl-smtp-test "rfc2045-q-encode-string-utf-8" ()
   (let* ((str "öüäÖÜÄß")
          (qstr (rfc2045-q-encode-string str :external-format :utf-8)))





More information about the Cl-smtp-cvs mailing list