utils.lisp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. (in-package :cl-user)
  2. (defpackage #:timeliner.utils
  3. (:use :cl :cl-mongo :sqlite)
  4. (:export
  5. #:ts->ms
  6. #:ms->ts
  7. #:$between
  8. #:make-keyword
  9. #:starts-with
  10. #:aget
  11. #:doc->plist
  12. #:load-chrome-cookie-jar
  13. #:today
  14. #:save-events))
  15. (in-package :timeliner.utils)
  16. (defun ts->ms (ts)
  17. (+ (* 1000 (local-time:timestamp-to-unix ts))
  18. (floor (local-time:nsec-of ts) 1000000)))
  19. (defun ms->ts (ms)
  20. (multiple-value-bind (unix msec) (floor ms 1000)
  21. (local-time:unix-to-timestamp unix :nsec (* msec 1000000))))
  22. (defun today ()
  23. (local-time:adjust-timestamp (local-time:now)
  24. (set :hour 0)
  25. (set :minute 0)
  26. (set :sec 0)
  27. (set :nsec 0)))
  28. (defun save-events (events)
  29. (dolist (e events)
  30. (db.insert "events" e)))
  31. (defgeneric $between (a from to)
  32. (:documentation "cl-mongo between query"))
  33. (defmethod $between (a from to)
  34. (kv ($>= a from) ($< a to)))
  35. (defmethod $between (a (from local-time:timestamp) (to local-time:timestamp))
  36. ($between a
  37. (cl-mongo::make-bson-time (ts->ms from))
  38. (cl-mongo::make-bson-time (ts->ms to))))
  39. (defun make-keyword (name) (values (intern (string-upcase name) "KEYWORD")))
  40. (defun starts-with (str with)
  41. (string= (subseq str 0 (min (length str)
  42. (length with)))
  43. with))
  44. (defmacro aget (key alist)
  45. `(cdr (assoc ,key ,alist :test #'equal)))
  46. (defvar *decode-doc-to* :plist "Document object type")
  47. (defgeneric decode-element (element)
  48. (:documentation "Convert cl-mongo element to preferred type"))
  49. (defmethod decode-element (element)
  50. element)
  51. (defmethod decode-element ((element document))
  52. (case *decode-doc-to*
  53. (:plist (doc->plist element))))
  54. (defmethod decode-element ((element cl-mongo::bson-time))
  55. (ms->ts (cl-mongo::raw element)))
  56. (defun doc->plist (doc)
  57. (let ((*decode-doc-to* :plist)
  58. result)
  59. (loop for key in (get-keys doc)
  60. do (setf (getf result (make-keyword key))
  61. (decode-element (get-element key doc))))
  62. result))
  63. (defmethod yason:encode ((object document) &optional (stream *standard-output*))
  64. (let ((hash (cl-mongo::elements object)))
  65. (unless (cl-mongo::_local object)
  66. (setf (gethash "_id" hash)
  67. (crypto:byte-array-to-hex-string (doc-id object))))
  68. (yason:encode hash stream)))
  69. (defmethod yason:encode ((object cl-mongo::bson-time) &optional (stream *standard-output*))
  70. (yason:encode (cl-mongo::raw object) stream))
  71. ;; Google Chrome Cookies loading
  72. (defvar *chrome-cookie-password*
  73. (crypto:ascii-string-to-byte-array "peanuts")
  74. "Password for PBKDF2")
  75. (defvar *chrome-cookie-salt*
  76. (crypto:ascii-string-to-byte-array "saltysalt")
  77. "Salt for PBKDF2")
  78. (defvar *chrome-cookie-iterations* 1 "PBKDF2 iteration count")
  79. (defvar *chrome-cookie-key-length* 16 "PBKDF2 key length")
  80. (defun chrome-make-cookie-cipher ()
  81. (crypto:make-cipher 'crypto:aes :mode 'crypto:cbc
  82. :key (crypto:derive-key
  83. (crypto:make-kdf 'crypto:pbkdf2 :digest 'crypto:sha1)
  84. *chrome-cookie-password*
  85. *chrome-cookie-salt*
  86. *chrome-cookie-iterations*
  87. *chrome-cookie-key-length*)
  88. :initialization-vector (crypto:ascii-string-to-byte-array
  89. " ")))
  90. (defvar *chrome-cookie-file*
  91. (merge-pathnames #P".config/google-chrome/Default/Cookies"
  92. (user-homedir-pathname)))
  93. (defparameter +temp-cookie-file+ #P"cookies.sqlite")
  94. (define-condition bad-encoding-error (simple-error) ())
  95. (defun chrome-cookie-decode (value)
  96. (when (mismatch (subseq value 0 3)
  97. (mapcar #'char-code (coerce "v10" 'list)))
  98. (error 'bad-encoding-verion))
  99. (crypto:decrypt-in-place (chrome-make-cookie-cipher) value :start 3)
  100. (coerce (mapcar #'code-char (subseq (coerce value 'list)
  101. 3 (- (length value)
  102. (elt value (1- (length value)))))) 'string))
  103. (defun load-chrome-cookie-jar (domain)
  104. ;; Workaround chrome sometimes locking cookies database
  105. (cl-fad:copy-file *chrome-cookie-file* +temp-cookie-file+ :overwrite t)
  106. (sqlite:with-open-database (db +temp-cookie-file+)
  107. (make-instance 'drakma:cookie-jar
  108. :cookies (loop
  109. for (name host-key value path expires secure-p http-only-p encrypted)
  110. in (sqlite::execute-to-list
  111. db (format nil "select name, host_key, value, path, expires_utc, secure, httponly, encrypted_value from cookies where host_key like '%~A%'" domain))
  112. collect (make-instance 'drakma:cookie
  113. :name name
  114. :domain host-key
  115. :value (if (string= value "")
  116. (chrome-cookie-decode encrypted)
  117. value)
  118. :path path
  119. :expires (if (equal expires 0) nil (floor expires 1000))
  120. :securep (equal secure-p 1)
  121. :http-only-p (equal http-only-p 1))))))