/
lj-acct.el
230 lines (194 loc) · 7.91 KB
/
lj-acct.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
;;; lj-acct.el --- LiveJournal account handling code for ljupdate
;; Copyright (C) 2002, 2003, 2004, 2005 Edward O'Connor <ted@oconnor.cx>
;; Author: Edward O'Connor <ted@oconnor.cx>
;; Keywords: convenience
;; This file is part of ljupdate, a LiveJournal client for Emacs.
;; ljupdate is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
;; {at your option} any later version.
;; ljupdate is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING, or type `C-h C-c'. If
;; not, write to the Free Software Foundation at this address:
;; Free Software Foundation
;; 51 Franklin Street, Fifth Floor
;; Boston, MA 02110-1301
;; USA
;;; Commentary:
;;
;;; History:
;;
;;; Code:
(require 'lj-custom)
;; backing store
(defvar lj-acct-hash nil
"Hash in which internal account information is stored.")
(defun lj-make-acct-hash ()
"Create a new value for variable `lj-acct-hash'."
(make-hash-table :test 'equal))
(defun lj-acct-hash ()
"Return the hash table in which internal account information is stored.
Will attempt to load our cached configuration if it is available."
(when (null lj-acct-hash)
(lj-cache-load))
(or lj-acct-hash
(setq lj-acct-hash (lj-make-acct-hash))))
;; sever/user property getters/setters
(defun lj-servers ()
"Return a list of LiveJournal servers that we know about."
(let ((servers '()))
(maphash (lambda (server server-hash)
(push server servers))
(lj-acct-hash))
(nreverse servers)))
(defun lj-server-get (server property)
"Fetch the value of SERVER's PROPERTY."
(let ((server-hash (gethash server (lj-acct-hash))))
(when server-hash
(gethash property server-hash))))
(defun lj-users (server)
"Return a list of users on SERVER whose accounts we can use."
(let ((server-hash (gethash server (lj-acct-hash)))
(users '()))
(when server-hash
(maphash (lambda (user user-hash)
(when (and (stringp user)
(hash-table-p user-hash))
(push user users)))
server-hash)
users)))
(defun lj-server-put (server property value)
"Set SERVER' value of PROPERTY to VALUE."
(let ((server-hash (gethash server (lj-acct-hash))))
(unless server-hash
(setq server-hash (make-hash-table :test 'equal))
(puthash server server-hash (lj-acct-hash)))
(puthash property value server-hash)))
(defun lj-server-rem (server property)
"Remove SERVER's PROPERTY."
(let ((server-hash (gethash server (lj-acct-hash))))
(when server-hash
(remhash property server-hash))))
(defun lj-user-get (server username property)
"Fetch SERVER's value of USERNAME's PROPERTY."
(let ((user-hash (lj-server-get server username)))
(when user-hash
(gethash property user-hash))))
(defun lj-user-put (server username property value)
"Set SERVER's value of USERNAME's PROPERTY to VALUE."
(let ((user-hash (lj-server-get server username)))
(unless user-hash
(setq user-hash (make-hash-table :test 'equal))
(lj-server-put server username user-hash))
(puthash property value user-hash)))
(defun lj-user-rem (server username property)
"Remove SERVER's USERNAME's PROPERTY."
(let ((user-hash (lj-server-get server username)))
(when user-hash
(remhash property user-hash))))
;; serialization / deserialization routines
(defun lj-hash-from-alist (alist)
"Return a new hash table with the same mappings as in ALIST."
(let ((hash (make-hash-table :test 'equal)))
(mapcar (lambda (element)
(puthash (car element) (cdr element) hash))
alist)
hash))
(defun lj-alist-from-hash (hash)
"Return a new alist with the same mapping as in HASH."
(let ((alist '()))
(maphash (lambda (k v)
(push (cons k v) alist))
hash)
alist))
;; loading and saving cache
(defun lj-cache-file (&optional filename)
"Return the absolute path to FILENAME.
If FILENAME is nil, returns the absolute path to the file named
\"cache\" in `lj-cache-dir'."
(if filename
(expand-file-name filename)
(expand-file-name "cache" lj-cache-dir)))
(defun lj-cache-load (&optional filename)
"Load server and user information out of cache FILENAME.
We use our default cache location if FILENAME is nil."
(setq filename (lj-cache-file filename))
(when (file-readable-p filename)
(with-temp-buffer
(insert-file-contents filename)
(eval-buffer))))
(defvar lj-cache-format 1
"Version of the cache file format.")
(defun lj-cache-save-forms ()
"Return Lisp forms which would restore this ljupdate config if evalled."
(let ((forms '()))
(push '(setq lj-cache-format 1) forms)
(push '(setq lj-acct-hash (lj-make-acct-hash)) forms)
(maphash (lambda (server server-hash)
(push `(lj-server-put ,server :mood-max
,(or (lj-server-get server :mood-max)
"0"))
forms)
(push `(lj-server-put ,server :moods
',(lj-server-get server :moods))
forms)
(maphash (lambda (username user-hash)
(when (stringp username)
;; handle users
(mapc (lambda (field)
(let ((val (lj-user-get server username field)))
(when val
(push `(lj-user-put
,server ,username ,field
;; Conservatively quoting everything
',val)
forms))))
'(:name :access :pics :friends-groups))
(let ((pass (lj-user-get server username :password)))
(when (and pass lj-cache-login-information)
(push `(lj-user-put
,server ,username :password
,pass)
forms)))))
server-hash))
(lj-acct-hash))
(nreverse forms)))
(defun lj-make-directory (directory &optional parents modes)
"Create DIRECTORY.
If PARENTS is non-null, create any parent directories as necessary.
If MODES is null, 0700 are used."
(let ((umask (default-file-modes)))
(unwind-protect
(progn
(set-default-file-modes (or modes ?\700))
(make-directory directory parents))
(set-default-file-modes umask))))
(defun lj-cache-save (&optional filename)
"Save server and user information out to cache FILENAME.
We use our default cache location if FILENAME is nil."
(setq filename (lj-cache-file filename))
(let ((dir (file-name-directory filename)))
(unless (file-exists-p dir)
(lj-make-directory dir t))
(unless (file-directory-p dir)
(error "File `%s' is not a directory" dir)))
(unless (file-writable-p filename)
(error "Unable to write to `%s'" filename))
(find-file filename nil)
(delete-region (point-min) (point-max))
(insert ";; -*- emacs-lisp -*-\n"
";; ljupdate configuration cache file\n")
(let ((standard-output (current-buffer)))
(mapc (lambda (form)
(prin1 form)
(terpri))
(lj-cache-save-forms)))
(save-buffer)
(kill-buffer (current-buffer)))
(add-hook 'kill-emacs-hook 'lj-cache-save)
(provide 'lj-acct)
;;; lj-acct.el ends here