· 8 years ago · Oct 17, 2017, 12:14 PM
1;;; neopastebin.el --- pastebin.com interface to emacs
2
3;;; Copyright (C) 2013 by Daniel Hilst <danielhilst at gmail.com>
4
5;;; This program is free software; you can redistribute it and/or modify
6;;; it under the terms of the GNU General Public License as published by
7;;; the Free Software Foundation; either version 2, or (at your option)
8;;; any later version.
9
10;;; This program is distributed in the hope that it will be useful,
11;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13;;; GNU General Public License for more details.
14
15;;; You should have received a copy of the GNU General Public License
16;;; along with this program; see the file COPYING. If not, write to the
17;;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18;;; Boston, MA 02110-1301 USA
19
20;;; Besides being a new interface, some parts were borrowed from old
21;;; interface so I think is fair put the names here.
22;;; Copyright (C) 2008 by Nic Ferrier <nic@ferrier.me.uk>
23;;; Copyright (C) 2010 by Ivan Korotkov <twee@tweedle-dee.org>
24;;; Copyright (C) 2012 by Filonenko Michael <filonenko.mikhail@gmail.com>
25
26;;;
27;;; USAGE:
28;;;
29;;; LOGIN
30;;; ~~~~~
31;;;
32;;; Puts this on your .emacs file
33;;;
34;;; ((pastebin-create-login :dev-key "YOUR DEV KEY"
35;;; :username "YOUR USER NAME")
36;;;
37;;; Login will only ocurr when you try to paste something or
38;;; list your pastes. So is save to put this on your .emacs file
39;;; without need to wait emacs connect to pastebin on each startup.
40;;;
41;;;
42;;; -*- SECURITY DISCLAIMER -*-
43;;;
44;;; Password will be asked first time you login. It will also ask for store password
45;;; on disk. This will be saved on ~/.emacs.d/pastebin-data/pass by default.
46;;;
47;;; !!! YOUR PASSWORD ITS SAVED ON PLAIN TEXT !!!
48;;;
49;;; Since pastebin uses http instead of https, your password can be gathered from sniffers
50;;; on network, so you are not safe in any case, this is pastebin.com api and is insecure.
51;;; Use at own risk!
52;;;
53;;;
54;;; LISTING
55;;; ~~~~~~~
56;;;
57;;; M-x pastebin-list-buffer-refresh -> Fetch and list pastes on "list buffer"
58;;;
59;;; After logged you can list your pastes with command `pastebin-list-buffer-refresh', just
60;;; type pastebin-l and press TAB.
61;;;
62;;; Here is a list of keybinds from list buffer
63;;;
64;;; RET -> fetch paste and switch to it
65;;; r -> refresh list and list buffer
66;;; d -> delete paste
67;;; t -> order by title
68;;; D -> order by date
69;;; f -> order by format
70;;; k -> order by key
71;;; p -> order by private
72;;;
73;;;
74;;; CREATING NEW PASTE
75;;; ~~~~~~~~~~~~~~~~~~
76;;;
77;;; M-x pastebin-new -> will create a new paste from current buffer
78;;;
79;;; The name of the paste is given from current buffer name
80;;; The format from buffers major mode
81;;; Prefix argument makes private
82;;;
83
84;;;
85;;; Naming convention:
86;;;
87;;; pastebin-- prefix for internal stuff
88;;; pastebin- prefix for user interface and customs
89;;;
90;;;
91;;; @TODO list:
92;;;
93;;; - pastebin minor mode
94;;; - Must save files on pastebin with keybinds. I'm
95;;; wondering if setting pastebin.com as an abstract storage
96;;; is a good idea. If so, C-x C-s should save pastebin buffers
97;;; to pastebin.com without question. Elisp files manual chapter would help-me
98;;; to do that http://www.gnu.org/software/emacs/manual/html_node/elisp/Files.html#Files
99;;; - Keybind for printing url on mini buffer
100;;;
101;;; DEPENDENCIES:
102;;;
103;;; eieio.el
104;;; wid-edit
105;;;
106
107(require 'eieio)
108(require 'wid-edit)
109
110(defgroup pastebin nil
111 "Pastebin -- pastebin.com client"
112 :tag "Pastebin"
113 :group 'tools)
114
115;; Customs
116
117(defcustom pastebin-default-paste-list-limit 100
118 "The number of pastes to retrieve by default"
119 :type 'number
120 :group 'pastebin)
121
122(defcustom pastebin-post-request-login-url "http://pastebin.com/api/api_login.php"
123 "Login url"
124 :type 'string
125 :group 'pastebin)
126
127(defcustom pastebin-post-request-paste-url "http://pastebin.com/api/api_post.php"
128 "Paste url"
129 :type 'string
130 :group 'pastebin)
131
132(defcustom pastebin-data-dir "~/.emacs.d/pastebin-data"
133 "Diretory to keep data"
134 :type 'string
135 :group 'pastebin)
136
137;; Global variables
138
139(defvar pastebin--mode-map nil
140 "The pastebin keymap.")
141(unless pastebin--mode-map
142 (setq pastebin--mode-map (make-sparse-keymap))
143 (define-key pastebin--mode-map (kbd "C-x p u") 'pastebin-show-url))
144
145(define-minor-mode pastebin-mode
146 "Pastebin buffer mode, used to upload pastes automatically with S-C-x C-s"
147 :lighter " pastebin"
148 :group 'pastebin
149 :keymap pastebin--mode-map)
150
151(defvar pastebin--type-assoc
152 '((actionscript-mode . " actionscript")
153 (ada-mode . "ada")
154 (asm-mode . "asm")
155 (sh-mode . "bash")
156 (autoconf-mode . "bash")
157 (bibtex-mode . "bibtex")
158 (cmake-mode . "cmake")
159 (c-mode . "c")
160 (c++-mode . "cpp")
161 (cobol-mode . "cobol")
162 (conf-colon-mode . "properties")
163 (conf-javaprop-mode . "properties")
164 (conf-mode . "ini")
165 (conf-space-mode . "properties")
166 (conf-unix-mode . "ini")
167 (conf-windows-mode . "ini")
168 (cperl-mode . "perl")
169 (csharp-mode . "csharp")
170 (css-mode . "css")
171 (delphi-mode . "delphi")
172 (diff-mode . "diff")
173 (ebuild-mode . "bash")
174 (eiffel-mode . "eiffel")
175 (emacs-lisp-mode . "lisp")
176 (erlang-mode . "erlang")
177 (erlang-shell-mode . "erlang")
178 (espresso-mode . "javascript")
179 (fortran-mode . "fortran")
180 (glsl-mode . "glsl")
181 (gnuplot-mode . "gnuplot")
182 (graphviz-dot-mode . "dot")
183 (haskell-mode . "haskell")
184 (html-mode . "html4strict")
185 (idl-mode . "idl")
186 (inferior-haskell-mode . "haskell")
187 (inferior-octave-mode . "octave")
188 (inferior-python-mode . "python")
189 (inferior-ruby-mode . "ruby")
190 (java-mode . "java")
191 (js2-mode . "javascript")
192 (jython-mode . "python")
193 (latex-mode . "latex")
194 (lisp-mode . "lisp")
195 (lisp-interaction-mode . "lisp")
196 (lua-mode . "lua")
197 (makefile-mode . "make")
198 (makefile-automake-mode . "make")
199 (makefile-gmake-mode . "make")
200 (makefile-makepp-mode . "make")
201 (makefile-bsdmake-mode . "make")
202 (makefile-imake-mode . "make")
203 (matlab-mode . "matlab")
204 (nxml-mode . "xml")
205 (oberon-mode . "oberon2")
206 (objc-mode . "objc")
207 (ocaml-mode . "ocaml")
208 (octave-mode . "matlab")
209 (pascal-mode . "pascal")
210 (perl-mode . "perl")
211 (php-mode . "php")
212 (plsql-mode . "plsql")
213 (po-mode . "gettext")
214 (prolog-mode . "prolog")
215 (python-2-mode . "python")
216 (python-3-mode . "python")
217 (python-basic-mode . "python")
218 (python-mode . "python")
219 (ruby-mode . "ruby")
220 (scheme-mode . "lisp")
221 (shell-mode . "bash")
222 (smalltalk-mode . "smalltalk")
223 (sql-mode . "sql")
224 (tcl-mode . "tcl")
225 (visual-basic-mode . "vb")
226 (xml-mode . "xml")
227 (yaml-mode . "properties")
228 (text-mode . "text"))
229 "Alist composed of major-mode names and corresponding pastebin highlight formats.")
230
231(defvar pastebin--default-user nil
232 "The default user begin used")
233
234(defvar pastebin--local-buffer-paste nil
235 "Every pastebin buffer has a paste object associated with it")
236(make-variable-buffer-local 'pastebin--local-buffer-paste)
237
238(defvar pastebin--list-buffer-user nil
239 "Every pastebin list buffer has a user object associated with it")
240(make-variable-buffer-local 'pastebin--list-buffer-user)
241
242(defvar pastebin--list-map
243 (let ((map (make-sparse-keymap)))
244 (define-key map (kbd "d") 'pastebin-delete-paste-at-point)
245 (define-key map (kbd "r") 'pastebin-list-buffer-refresh)
246 (define-key map (kbd "f") 'pastebin-list-buffer-refresh-sort-by-format)
247 (define-key map (kbd "t") 'pastebin-list-buffer-refresh-sort-by-title)
248 (define-key map (kbd "k") 'pastebin-list-buffer-refresh-sort-by-key)
249 (define-key map (kbd "D") 'pastebin-list-buffer-refresh-sort-by-date)
250 (define-key map (kbd "p") 'pastebin-list-buffer-refresh-sort-by-private)
251 map)
252 "Key map for pastebin list buffer")
253
254(defconst pastebin--raw-paste-url "http://pastebin.com/raw.php?i="
255 "Concatenate this with paste key to get the raw paste")
256
257;;
258;; EIEIO Layer
259;;
260
261;; PASTE-USER class
262
263(defclass pastebin--paste-user ()
264 ((dev-key :initarg :dev-key "Your developer key from http://pastebin.com/api")
265 (usr-key :initarg :usr-key "Your user key, retrived from pastebin")
266 (password :initarg :password "Your password, clear text honey!!")
267 (username :initarg :username "Your username")
268 (paste-list :initarg :paste-list "The list of pastes for this user")
269 (list-buffer :initarg :list-buffer "Done by do-list-buffer")
270 (sort-by :initarg :sort-by "Order to sort :paste-list")
271 )
272 "Class representing a pastebin.com user")
273
274(defmethod is-logged ((user pastebin--paste-user))
275 "Return true if user is logged in"
276 (slot-boundp user :usr-key))
277
278(defmethod fetch-list-xml ((user pastebin--paste-user))
279 "Fetch the list of pastes as xml, and return that buffer"
280 (let* ((params (concat "api_dev_key=" (oref user dev-key)
281 "&api_user_key=" (oref user usr-key)
282 "&api_results_limit=" (format "%d" pastebin-default-paste-list-limit)
283 "&api_option=list")))
284 (with-current-buffer (pastebin--url-retrieve-synchronously pastebin-post-request-paste-url
285 "POST"
286 params)
287 (pastebin--strip-CRs)
288 (goto-char (point-min))
289 (current-buffer)
290 )))
291
292(defmethod refresh-paste-list ((user pastebin--paste-user))
293 "Set/Refresh paste-list attr to the list of paste objects retrieved from pastebin.com"
294 (oset user :paste-list nil)
295 (with-current-buffer (fetch-list-xml user)
296 (goto-char (point-min))
297 (let ((i (point-min))
298 plist)
299 (while (re-search-forward "</paste>" nil t)
300 (let ((paste-sexp (xml-parse-region i (point)))
301 p)
302 (setq i (point))
303 (condition-case err
304 (progn
305 (setq p (pastebin--sexp-to-paste paste-sexp))
306 (oset p :user user)
307 (oset p :last-fetched (float-time))
308 (setq plist (append plist (list p))))
309 (wrong-type-argument
310 (error "Error while creating paste object on `refresh-paste-list' %s" err)
311 (debug)))
312 )
313 (oset user :paste-list plist)
314 )
315 )
316 )
317 )
318
319(defmacro pastebin--sort-by-string-attr (user attr)
320 "sort :paste-list by `attr' in reverse order"
321 `(progn
322 (unless (keywordp ,attr)
323 (error "pastebin--sort-by-stirng-attr attr is not a keyword"))
324
325 (unless (member ,attr '(:key :title :format_long :format_short :url :date :private))
326 (error "pastebin--sort-by-string-attr attr is not in '(:key :title :format_long :format_short :url)"))
327
328 (oset ,user :paste-list (sort (oref ,user :paste-list) (lambda (p1 p2)
329 (string< (downcase (oref p1 ,attr))
330 (downcase (oref p2 ,attr))))))
331 )
332 )
333
334(defmethod do-list-buffer ((user pastebin--paste-user))
335 "Create a buffer with a list of pastes and return it
336Some keybinds are setted"
337 (unless (is-logged user)
338 (error "do-list-buffer called with unloged user"))
339
340 (unless (slot-boundp user :list-buffer)
341 (oset user :list-buffer (format "Pastebin %s pastes" (oref user :username))))
342
343 (unless (get-buffer (oref user :list-buffer))
344 (generate-new-buffer (oref user :list-buffer))
345 (message "%s buffer created" (oref user :list-buffer)))
346
347 (let ((inhibit-read-only t)
348 old-point)
349 (with-current-buffer (get-buffer (oref user :list-buffer))
350
351 (setq old-point (point))
352
353 (erase-buffer)
354
355 (widget-minor-mode 1)
356 (use-local-map pastebin--list-map)
357
358 (setq pastebin--list-buffer-user user)
359
360 (widget-insert (format "%5.5s | %-8.8s | %-32.32s | %-7.7s | %-30.30s\n"
361 "VIEW" "ID" "TITLE" "FORMAT" "DATE"))
362 (dolist (paste (oref user :paste-list))
363 (widget-create 'link
364 :notify (lambda (wid &rest ignore)
365 (pastebin--fetch-paste-at-point))
366 :paste paste
367 :follow-link t
368 :value (format "%4.4s | %-8.8s | %-32.32s | %-7.7s | %-20.20s"
369 (cond
370 ((string= (oref paste :private) "0")
371 "PUBL")
372 ((string= (oref paste :private) "1")
373 "ULST")
374 ((string= (oref paste :private) "2")
375 "PRIV")
376 (t
377 "_ERR"))
378 (oref paste :key)
379 (or (oref paste :title) "")
380 (oref paste :format_short)
381 (format-time-string "%c" (seconds-to-time (string-to-number (oref paste :date))))
382 )
383 )
384
385 (widget-insert "\n")
386 )
387 (widget-setup)
388 (goto-char (or old-point (point-min)))
389 (current-buffer)
390 ) ;; (with-current-buffer (get-buffer (oref user :list-buffer))
391 ) ;; (let ((inhibit-read-only t)
392 )
393
394(defmethod login ((user pastebin--paste-user))
395 "Given user and password login and sets usr-key"
396 (if (slot-boundp user :usr-key)
397 (oref user :usr-key)
398 (let* ((params (concat "api_dev_key=" (oref user :dev-key)
399 "&api_user_name=" (url-hexify-string (oref user :username))
400 "&api_user_password=" (url-hexify-string (oref user :password)))))
401
402 (with-current-buffer (pastebin--url-retrieve-synchronously pastebin-post-request-login-url
403 "POST"
404 params)
405 (oset user :usr-key (buffer-substring-no-properties (point-min) (point-max)))))))
406
407(defmethod paste-new ((user pastebin--paste-user) &optional unlisted)
408 "Upload a new paste to pastebin.com"
409 (let* ((ptitle (buffer-name))
410 (pbuffer (current-buffer))
411 (pprivate (if unlisted "1" "0"))
412 (params (concat "api_dev_key=" (oref user :dev-key)
413 "&api_user_key=" (oref user :usr-key)
414 "&api_paste_name=" (url-hexify-string ptitle)
415 "&api_paste_format=" (url-hexify-string (pastebin--get-format-string-from-major-mode))
416 "&api_paste_code=" (url-hexify-string (with-current-buffer pbuffer
417 (buffer-string)))
418 "&api_option=paste"
419 "&api_paste_private=" pprivate)))
420 (with-current-buffer (pastebin--url-retrieve-synchronously pastebin-post-request-paste-url
421 "POST"
422 params)
423 (current-buffer))))
424
425
426
427;; PASTE CLASS
428
429(defclass pastebin--paste ()
430 ((key :initarg :key)
431 (date :initarg :date)
432 (title :initarg :title)
433 (size :initarg :size)
434 (expire_date :initarg :expire_date)
435 (private :initarg :private)
436 (format_long :initarg :format_long)
437 (format_short :initarg :format_short)
438 (url :initarg :url)
439 (buffer :initarg :buffer)
440 (last-fetched :initarg :last-fetched)
441 (user :initarg :user :type pastebin--paste-user)
442 (hits :initarg :hits))
443 "Class representing a paste from pastebin.com
444The contents of paste are not stored. Instead the method
445`paste-fetch' fetch and retrieve the buffer with paste contents")
446
447(defmethod get-mode ((p pastebin--paste))
448 "return the mode from `pastebin--type-assoc'"
449 (if (slot-boundp p :format_short)
450 (car (rassoc (oref p :format_short) pastebin--type-assoc))
451 (error "No format short for paste %s with key %s" (oref p :title) (oref p :key))))
452
453(defmethod fetch-and-process ((p pastebin--paste))
454 "Fetch buffer a do needed processing before switching to it"
455 (with-current-buffer (paste-fetch p)
456 (switch-to-buffer (current-buffer))))
457
458(defmethod paste-fetch ((p pastebin--paste))
459 "Fetch the raw content from paste and return buffer containing"
460 (let* ((content-buf (pastebin--url-retrieve-synchronously (concat pastebin--raw-paste-url (oref p key))
461 "GET"
462 ""))
463 (inhibit-read-only t)
464 (pbuf (if (and (slot-boundp p :buffer)
465 (buffer-live-p (oref p :buffer)))
466 (oref p :buffer)
467 (oset p :buffer (get-buffer-create (oref p :title))))))
468 (with-current-buffer pbuf
469 (erase-buffer)
470 (insert-buffer-substring content-buf)
471 (pastebin--strip-paste-CRs)
472 (funcall (get-mode p))
473 (setq pastebin--local-buffer-paste p) ;; buffer local
474 (pastebin-mode 1)
475 (current-buffer))))
476
477(defmethod paste-delete ((p pastebin--paste))
478 "Detele paste from pastebin.com"
479 (unless (and (slot-boundp p :user)
480 (slot-boundp p :key)
481 (slot-boundp (oref p :user) :dev-key)
482 (slot-boundp (oref p :user) :usr-key))
483 (error "paste-delete called with ubound slot object"))
484
485 (let* ((params (concat "api_dev_key=" (oref (oref p :user) :dev-key)
486 "&api_user_key=" (oref (oref p :user) :usr-key)
487 "&api_paste_key=" (oref p :key)
488 "&api_option=delete")))
489 (with-current-buffer (pastebin--url-retrieve-synchronously pastebin-post-request-paste-url
490 "POST"
491 params)
492 (buffer-string)) ;; Pastebin send somthing like paste xxx deleted
493 ))
494
495;; Local functions and helpers
496
497(defun pastebin--get-format-string-from-major-mode ()
498 "Returns the format string from major mode
499Error if major-mode is nil"
500 (unless major-mode
501 (error "pastebin--get-format-string-from-major-mode called with nil major-mode"))
502 (or (cdr (assoc major-mode pastebin--type-assoc))
503 "text"))
504
505(defun pastebin--sexp-get-attr-h (paste-sexp attr &optional onerror)
506 "Return the attribute `attr' from `paste-sexp'
507If onerror is given (should be a string) is used when no such attribute
508is found.
509Attributes are described here: http://pastebin.com/api#9
510`attr' must be a symbol
511Ex: (pastebin-paste-get-attr some-paste-sexp 'paste_tittle)"
512 (unless (symbolp attr)
513 (error "attr should be a symbol"))
514 (when (and onerror
515 (not (stringp onerror)))
516 (error "onerror should be a string"))
517 (let ((a (or (car (last (assoc attr (nthcdr 2 (car paste-sexp)))))
518 onerror)))
519 (unless a
520 (error "No attribute %s on paste sexp '%s'" attr paste-sexp))
521 (format "%s" a)))
522
523(defun pastebin--strip-paste-CRs (&optional buffer)
524 "Get rid of CR
525I use this after fetching a paste to get rid of annoying ^M"
526 (let ((buffer (or buffer (current-buffer))))
527 (with-current-buffer buffer
528 (goto-char (point-min))
529 (while (re-search-forward "\r" nil t)
530 (replace-match ""))
531 buffer)))
532
533(defun pastebin--strip-CRs (&optional buffer)
534 "Get rid of CRLF
535I need this for xml-parse-region reponse without getting
536a lot of spaces and CRLF on pastes sexps. See `pastebin--sexp-to-paste'"
537 (let ((buffer (or buffer (current-buffer))))
538 (with-current-buffer buffer
539 (goto-char (point-min))
540 (while (re-search-forward "\r\n" nil t)
541 (replace-match ""))
542 buffer)))
543
544(defun pastebin--strip-http-header (&optional buffer)
545 "Given a buffer with an HTTP response, remove the header and return the buffer
546If no buffer is given current buffer is used"
547 (let ((buffer (or buffer (current-buffer))))
548 (with-current-buffer buffer
549 (goto-char (point-min))
550 (re-search-forward "\n\n")
551 (kill-region (point-min) (point))
552 buffer)))
553
554(defun pastebin--get-paste-at-point ()
555 "Get the paste at point at current-buffer"
556 (let ((wid (widget-at)))
557 (if (not wid)
558 (error "No paste at point")
559 (widget-get wid :paste))))
560
561(defun pastebin--fetch-paste-at-point ()
562 "Fetch and switch to paste at point"
563 (let ((p (pastebin--get-paste-at-point)))
564 (fetch-and-process p)))
565
566(defun pastebin--sexp-to-paste (paste-sexp)
567 "Given and sexp returned from `xml-parse-region' on pastebin.com response, constructs and return a pastebin--paste object.
568See `fetch-list-xml' for more information"
569 (unless (consp paste-sexp)
570 (error "pastebin--sexp-to-paste called without cons type"))
571 (condition-case err
572 (pastebin--paste (concat "paste@" (pastebin--sexp-get-attr-h paste-sexp 'paste_key))
573 :key (pastebin--sexp-get-attr-h paste-sexp 'paste_key)
574 :date (pastebin--sexp-get-attr-h paste-sexp 'paste_date)
575 :title (pastebin--sexp-get-attr-h paste-sexp 'paste_title "UNTITLED")
576 :size (pastebin--sexp-get-attr-h paste-sexp 'paste_size)
577 :expire_date (pastebin--sexp-get-attr-h paste-sexp 'paste_expire_date)
578 :private (pastebin--sexp-get-attr-h paste-sexp 'paste_private)
579 :format_long (pastebin--sexp-get-attr-h paste-sexp 'paste_format_long)
580 :format_short (pastebin--sexp-get-attr-h paste-sexp 'paste_format_short)
581 :url (pastebin--sexp-get-attr-h paste-sexp 'paste_url)
582 )
583 ((debug error)
584 (error "Cant construct paste from sexp %s\nError: %s" paste-sexp err))))
585
586(defun pastebin--store-password (passwd)
587 "Stores password on `pastebin-data-dir'/pass"
588 (pastebin--mkdatadir)
589 (with-temp-buffer
590 (insert passwd)
591 (write-file (concat pastebin-data-dir "/pass"))))
592
593(defun pastebin--read-password-from-file ()
594 "Read password from `pastebin-data-dir'/pass"
595 (with-temp-buffer
596 (goto-char (point-min))
597 (insert-file-contents-literally (concat pastebin-data-dir "/pass"))
598 (buffer-string)))
599
600(defun pastebin--password-file-exists-p ()
601 "return t if pastebin-data-dir exists"
602 (file-exists-p (concat pastebin-data-dir "/pass")))
603
604(defun pastebin--mkdatadir ()
605 "Create the `pastebin-data-dir'"
606 (ignore-errors
607 (make-directory pastebin-data-dir t)))
608
609(defun pastebin--ask-for-password (prompt)
610 "Ask user for a password and if want to store it"
611 (let* ((lexical-binding t)
612 (p (read-passwd prompt)))
613 (when (yes-or-no-p "Store password on disk? ")
614 (pastebin--store-password p))
615 p))
616
617(defun pastebin--url-retrieve-synchronously (url method params)
618 "Retrieve a buffer from pastebin, raising an error if an error ocurr"
619 (unless (stringp url)
620 (error "pastebin--url-retrieve-synchronously `url' need to be a string"))
621
622 (unless (stringp method)
623 (error "pastebin--url-retrieve-synchronously `method' need to be a string"))
624
625 (unless (stringp params)
626 (error "pastebin--url-retrieve-synchronously `params' need to be a string"))
627
628 (let* ((inhibit-read-only t)
629 (url-request-method method)
630 (url-request-extra-headers
631 '(("Content-Type" . "application/x-www-form-urlencoded")))
632 (url-request-data params)
633 (content-buf (url-retrieve-synchronously url)))
634 (unless (pastebin--http-200-p content-buf) ;; check HTTP header
635 (when debug-on-error
636 (with-current-buffer (get-buffer-create "*pastebin-debug*")
637 (erase-buffer)
638 (goto-char (point-min))
639 (insert "Bad HTTP response below\n")
640 (insert-buffer-substring content-buf)))
641 (error (concat
642 "pastebin--url-retrieve-synchronously HTTP Bad response (not 200) on header\n"
643 (if debug-on-error
644 "See header at *pastebin-debug*"
645 "")))) ;; header is OK ...
646 (with-current-buffer content-buf
647 (goto-char (point-min))
648 (pastebin--strip-http-header)
649 (pastebin--error-if-bad-response (current-buffer)) ;; two `with-current-buffer' on same buffer :-/ slow
650 )
651 content-buf ;; return the buffer
652 ))
653
654(defun pastebin--error-if-bad-response (buf)
655 "Raises a error if is a bad response from pastebin"
656 (unless (or (bufferp buf)
657 (stringp buf))
658 (error "pastebin--bad-presponse-p `buf' need be a buffer or a string"))
659
660 (with-current-buffer buf
661 (if (or
662 (save-excursion
663 (re-search-forward "Bad API request," nil t))
664 (save-excursion
665 (re-search-forward "No pastes found." nil t))
666 (save-excursion
667 (re-search-forward "URL Post limit, maximum pastes per 24h reached" nil t)))
668 (error "Pastebin bad response: %s" (buffer-string))
669 nil)))
670
671
672(defun pastebin--http-200-p (header-buf)
673 "Search for HTTP 200 status on header-str - a buffer"
674 (unless (bufferp header-buf)
675 (error "pastebin--http-200-p: `header-buf' need be a buffer :/"))
676
677 (with-current-buffer header-buf
678 (save-excursion
679 (goto-char (point-min))
680 (re-search-forward "HTTP.*200 OK" nil t))))
681
682(defun pastebin--get-pst-url (buf)
683 "Return url string from buf"
684 (unless (and (or (bufferp buf)
685 (stringp buf))
686 (get-buffer buf))
687 (error (concat "pastebin--get-pst-url `buf' need\n"
688 "be a existing buffer or buffer name as string")))
689
690 (with-current-buffer buf
691 (save-excursion
692 (buffer-substring-no-properties (point-min) (point-max)))))
693
694;; User interface
695
696(defun pastebin-show-url ()
697 "On a buffer from a fetched paste, show the url o echo area"
698 (interactive)
699 (if pastebin--local-buffer-paste
700 (message (format "Paste URL: %s" (oref pastebin--local-buffer-paste :url)))
701 (message (format "Current buffer is not a paste buffer"))))
702
703(defun pastebin-list-buffer-refresh ()
704 "Refresh the list buffer screen
705Operates on current buffer"
706 (interactive)
707 (unless (is-logged pastebin--default-user)
708 (login pastebin--default-user))
709 (refresh-paste-list pastebin--default-user)
710 (switch-to-buffer (do-list-buffer pastebin--default-user))
711 (message "%d pastes fetched!" (length (oref pastebin--default-user :paste-list)))
712 )
713
714
715(defun pastebin-list-buffer-refresh-sort-by-title ()
716 (interactive)
717 (pastebin--sort-by-string-attr pastebin--default-user :title)
718 (switch-to-buffer (do-list-buffer pastebin--default-user))
719 )
720
721(defun pastebin-list-buffer-refresh-sort-by-format ()
722 (interactive)
723 (pastebin--sort-by-string-attr pastebin--default-user :format_short)
724 (switch-to-buffer (do-list-buffer pastebin--default-user))
725 )
726
727(defun pastebin-list-buffer-refresh-sort-by-key ()
728 (interactive)
729 (pastebin--sort-by-string-attr pastebin--default-user :key)
730 (switch-to-buffer (do-list-buffer pastebin--default-user))
731 )
732
733(defun pastebin-list-buffer-refresh-sort-by-date ()
734 (interactive)
735 (pastebin--sort-by-string-attr pastebin--default-user :date)
736 (oset pastebin--default-user :paste-list (reverse (oref pastebin--default-user :paste-list)))
737 (switch-to-buffer (do-list-buffer pastebin--default-user))
738 )
739
740(defun pastebin-list-buffer-refresh-sort-by-private ()
741 (interactive)
742 (pastebin--sort-by-string-attr pastebin--default-user :private)
743 (switch-to-buffer (do-list-buffer pastebin--default-user))
744 )
745
746(defun pastebin-delete-paste-at-point ()
747 "Delete the paste at point"
748 (interactive)
749 (let* ((lexical-binding t)
750 (p (pastebin--get-paste-at-point)))
751 (when (y-or-n-p (format "Do you really want to delete paste %s from %s\n"
752 (oref p :title)
753 (format-time-string "%c" (seconds-to-time (string-to-number (oref p :date))))))
754 (message "%s" (paste-delete (pastebin--get-paste-at-point))))
755 (pastebin-list-buffer-refresh)))
756
757(defun pastebin-new (p)
758 "Create a new paste from buffer"
759 (interactive "P")
760 (unless (is-logged pastebin--default-user)
761 (login pastebin--default-user))
762 (save-excursion
763 (goto-char (point-min))
764 (pastebin-mode 1)
765 (let* ((lexical-binding t)
766 (pbuf (paste-new pastebin--default-user p))
767 (url (pastebin--get-pst-url pbuf))
768 (x-select-enable-clipboard t)
769 (link-point (re-search-forward "http://[A-Za-z0-9_-]+\.[A-Za-z0-9]+" nil t)))
770 (kill-new url)
771 (message "URL: %s%s" url
772 (if link-point
773 (concat (format "\nYour buffer contains an link at line %d\n" (line-number-at-pos link-point))
774 (format "please visit link above and fill the captcha"))
775 "")))))
776
777(defun pastebin-create-login (&rest args)
778 "Create a login data. The effective login will be done when needed
779NOTE: `args' is a keryword list using :username and :dev-key that should
780be strings"
781 ;; Keyword arguments work arround
782 ;; I want to get rid of cl dependence here
783 (let* ((lexical-bind t)
784 username
785 dev-key
786 password)
787 (while args
788 (cond ((eq (car-safe args) :username)
789 (setq username (car-safe (cdr-safe args))))
790 ((eq (car-safe args) :dev-key)
791 (setq dev-key (car-safe (cdr-safe args))))
792 ) ;; (cond ..
793 (setq args (cdr-safe args)))
794 ;; Function body
795 (unless (and username dev-key)
796 (error "pastebin-login argument missing. (dev-key or username)"))
797 (let ((lexical-binding t)
798 (p (if (pastebin--password-file-exists-p)
799 (pastebin--read-password-from-file)
800 (pastebin--ask-for-password "Pastebin password: "))))
801 (setq pastebin--default-user (pastebin--paste-user username
802 :username username
803 :dev-key dev-key
804 :password p)))
805 (message "User %s created, login is on demand. Have a nice day!" username)
806 ) ;; (let* ((lexical-bind t)
807 ) ;; (defun pastebin-create-login &rest args)
808
809;; Setup minor mode keymap
810(or (assq 'pastebin-mode minor-mode-map-alist)
811 (setq minor-mode-map-alist (cons (cons 'pastebin-mode pastebin--mode-map)
812 minor-mode-map-alist)))
813
814(provide 'neopastebin)
815
816;;; END of neopastebin