Merge lp:~tsyrus/pastebinit/pastebinthat-gpg into lp:pastebinit

Proposed by Terence Syrus
Status: Work in progress
Proposed branch: lp:~tsyrus/pastebinit/pastebinthat-gpg
Merge into: lp:pastebinit
Diff against target: 1589 lines (+775/-500)
22 files modified
README (+16/-13)
pastebin.d/cxg.de.conf (+4/-7)
pastebin.d/dpaste.com.conf (+17/-0)
pastebin.d/fpaste.org.conf (+1/-1)
pastebin.d/lpaste.net.conf (+2/-3)
pastebin.d/p.defau.lt.conf (+2/-5)
pastebin.d/paste.debian.net.conf (+1/-1)
pastebin.d/paste.drizzle.org.conf (+6/-9)
pastebin.d/paste.kde.org.conf (+10/-14)
pastebin.d/paste.openstack.org.conf (+6/-9)
pastebin.d/paste.pound-python.org.conf (+1/-1)
pastebin.d/paste.ubuntu.com.conf (+1/-1)
pastebin.d/paste.ubuntu.org.cn.conf (+1/-1)
pastebin.d/paste2.org.conf (+1/-1)
pastebin.d/pastebin.com.conf (+4/-5)
pastebin.d/pastebin.mate-desktop.org.conf (+7/-9)
pastebin.d/pb.daviey.com.conf (+4/-5)
pastebin.d/slexy.org.conf (+2/-3)
pastebin.d/sprunge.us.conf (+3/-5)
pastebinit (+537/-402)
test.py (+135/-0)
test.sh (+14/-5)
To merge this branch: bzr merge lp:~tsyrus/pastebinit/pastebinthat-gpg
Reviewer Review Type Date Requested Status
Pastebinit Developers Pending
Review via email: mp+243920@code.launchpad.net

Description of the change

Includes pastebinthat and pastebinthat-review...

support for GPG signing and encrypting, support for https, support for reviewing keys and content before pasting

To post a comment you must log in.
Revision history for this message
Stéphane Graber (stgraber) wrote :

Looks like this is based on the pastebinit rewrite/rework from your other branch, mark this work in progress until we can sort out the other one.

Unmerged revisions

226. By Terence Syrus <email address hidden>

more cleanup

225. By Terence Syrus <email address hidden>

minor cleanup

224. By Terence Syrus <email address hidden>

finished first round of gpg support changes

223. By Terence Syrus <email address hidden>

added support for signing pastes

222. By Terence Syrus <email address hidden>

added review option

221. By Terence Syrus <email address hidden>

variable renaming and cleanup

220. By Terence Syrus <email address hidden>

variable renaming

219. By Terence Syrus <email address hidden>

minor code cleanup

218. By Terence Syrus <email address hidden>

removed debug

217. By Terence Syrus <email address hidden>

removed unused method

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2009-12-30 13:35:24 +0000
3+++ README 2014-12-07 21:54:13 +0000
4@@ -28,10 +28,25 @@
5 this domain name should not contain
6 possible sub-domains in use.
7
8- regexp - a regular expression that matches the
9+ id_regexp - a regular expression that matches the
10 basename, as well as any sub-domains that
11 may be in use.
12
13+ page - used to specify a page from which to
14+ post data. It is the actual URL of
15+ the pastebin's form.
16+
17+ result_regexp - used to specify a regexp to execute
18+ on the resulting page after posting.
19+ This is useful to deal with special
20+ pastebins that don't redirect you
21+ to the new post's URL.
22+
23+ result_type - the type of the result (not required)
24+ id: The result_regexp matches an id.
25+ The basename will be prepended to the result
26+ url: The result_regexp matches a full url.
27+
28
29 THE FORMAT SECTION
30
31@@ -75,18 +90,6 @@
32 jabberid - contains the jabberid for the poster
33 as set at the command line.
34
35-Two additional special parameters are available for use:
36-
37- page - used to specify a page from which to
38- post data. It is the actual URL of
39- the pastebin's form.
40-
41- regexp - used to specify a regexp to execute
42- on the resulting page after posting.
43- This is useful to deal with special
44- pastebins that don't redirect you
45- to the new post's URL.
46-
47 Add any other fields in use for the specific pastebin you are setting up may be
48 added to the "[format]" section using the same syntax.
49
50
51=== modified file 'pastebin.d/cxg.de.conf'
52--- pastebin.d/cxg.de.conf 2012-06-13 16:41:21 +0000
53+++ pastebin.d/cxg.de.conf 2014-12-07 21:54:13 +0000
54@@ -1,14 +1,11 @@
55 [pastebin]
56 basename = cxg.de
57-regexp = http://cxg.de
58+id_regexp = http://cxg.de
59+result_type = url
60+result_regexp = href="(http://cxg.de/.*)"
61+page = /paste.php
62
63 [format]
64 user = desc
65 content = pastetext
66 format = lang
67-page = page
68-regexp = regexp
69-
70-[defaults]
71-page = /paste.php
72-regexp = href="http://cxg.de/(.*)"
73
74=== added file 'pastebin.d/dpaste.com.conf'
75--- pastebin.d/dpaste.com.conf 1970-01-01 00:00:00 +0000
76+++ pastebin.d/dpaste.com.conf 2014-12-07 21:54:13 +0000
77@@ -0,0 +1,17 @@
78+[pastebin]
79+basename = dpaste.com
80+id_regexp = http://dpaste.com
81+page = /api/v1/
82+
83+[format]
84+user = poster
85+content = content
86+title = title
87+format = language
88+hold = hold
89+
90+[defaults]
91+# Empty means "Plain".
92+format =
93+# Keep it for 30 days after last access. Otherwise only for 7 days.
94+hold = on
95
96=== modified file 'pastebin.d/fpaste.org.conf'
97--- pastebin.d/fpaste.org.conf 2014-01-06 20:58:13 +0000
98+++ pastebin.d/fpaste.org.conf 2014-12-07 21:54:13 +0000
99@@ -1,6 +1,6 @@
100 [pastebin]
101 basename = fpaste.org
102-regexp = http://fpaste.org
103+id_regexp = http://fpaste.org
104
105 [format]
106 user = paste_user
107
108=== modified file 'pastebin.d/lpaste.net.conf'
109--- pastebin.d/lpaste.net.conf 2014-01-06 22:59:43 +0000
110+++ pastebin.d/lpaste.net.conf 2014-12-07 21:54:13 +0000
111@@ -1,6 +1,7 @@
112 [pastebin]
113 basename = lpaste.net
114-regexp = http://lpaste.net
115+id_regexp = http://lpaste.net
116+page = new
117
118 [format]
119 public = public
120@@ -10,10 +11,8 @@
121 channel = channel
122 content = paste
123 email = email
124-page = page
125
126 [defaults]
127-page = new
128 public = Public
129 channel =
130 email =
131
132=== modified file 'pastebin.d/p.defau.lt.conf'
133--- pastebin.d/p.defau.lt.conf 2012-06-13 16:41:21 +0000
134+++ pastebin.d/p.defau.lt.conf 2014-12-07 21:54:13 +0000
135@@ -1,10 +1,7 @@
136 [pastebin]
137 basename = p.defau.lt
138-regexp = http://p.defau.lt
139+id_regexp = http://p.defau.lt
140+page = /submit.php
141
142 [format]
143 content = code
144-page = page
145-
146-[defaults]
147-page = /submit.php
148
149=== modified file 'pastebin.d/paste.debian.net.conf'
150--- pastebin.d/paste.debian.net.conf 2012-06-13 16:41:21 +0000
151+++ pastebin.d/paste.debian.net.conf 2014-12-07 21:54:13 +0000
152@@ -1,6 +1,6 @@
153 [pastebin]
154 basename = paste.debian.net
155-regexp = http://paste.debian.net
156+id_regexp = https?://paste.debian.net
157
158 [format]
159 user = poster
160
161=== modified file 'pastebin.d/paste.drizzle.org.conf'
162--- pastebin.d/paste.drizzle.org.conf 2012-06-13 16:41:21 +0000
163+++ pastebin.d/paste.drizzle.org.conf 2014-12-07 21:54:13 +0000
164@@ -1,18 +1,15 @@
165 [pastebin]
166 basename = paste.drizzle.org
167-regexp = http://paste.drizzle.org
168+id_regexp = http://paste.drizzle.org
169+target_url = http://paste.drizzle.org/show/
170+result_type = id
171+result_regexp = \"data\": \"([^"]*)
172+post_format = json
173+page = /json/?method=pastes.newPaste
174
175 [format]
176 lang = language
177-page = page
178 content = code
179-post_format = post_format
180-regexp = regexp
181-target_url = target_url
182
183 [defaults]
184-page = /json/?method=pastes.newPaste
185-post_format = json
186 lang =
187-regexp = \"data\": \"([^"]*)
188-target_url = http://paste.drizzle.org/show/
189
190=== modified file 'pastebin.d/paste.kde.org.conf'
191--- pastebin.d/paste.kde.org.conf 2012-06-13 16:41:21 +0000
192+++ pastebin.d/paste.kde.org.conf 2014-12-07 21:54:13 +0000
193@@ -1,22 +1,18 @@
194 [pastebin]
195 basename = paste.kde.org
196-regexp = http://paste.kde.org
197+id_regexp = http://paste.kde.org
198+result_type = id
199+result_regexp = <id>(.*)</id>
200+page = /api/xml/create
201
202 [format]
203-user = paste_user
204-format = paste_lang
205-content = paste_data
206-password = paste_password
207-expire = paste_expire
208-private = paste_private
209-api = api_submit
210-mode = mode
211-regexp = regexp
212+format = language
213+content = data
214+expire = expire
215+private = private
216+title = title
217+password = password
218
219 [defaults]
220-private = 0
221 format = text
222 expire = 86400
223-api = 1
224-mode = xml
225-regexp = <id>(.*)</id>
226
227=== modified file 'pastebin.d/paste.openstack.org.conf'
228--- pastebin.d/paste.openstack.org.conf 2012-06-13 17:15:59 +0000
229+++ pastebin.d/paste.openstack.org.conf 2014-12-07 21:54:13 +0000
230@@ -1,18 +1,15 @@
231 [pastebin]
232 basename = paste.openstack.org
233-regexp = http://paste.openstack.org
234+id_regexp = http://paste.openstack.org
235+result_type = id
236+result_regexp = \"data\": \"([^"]*)
237+target_url = http://paste.openstack.org/show/
238+post_format = json
239+page = /json/?method=pastes.newPaste
240
241 [format]
242 lang = language
243-page = page
244 content = code
245-post_format = post_format
246-regexp = regexp
247-target_url = target_url
248
249 [defaults]
250-page = /json/?method=pastes.newPaste
251-post_format = json
252 lang =
253-regexp = \"data\": \"([^"]*)
254-target_url = http://paste.openstack.org/show/
255
256=== modified file 'pastebin.d/paste.pound-python.org.conf'
257--- pastebin.d/paste.pound-python.org.conf 2012-06-13 16:41:21 +0000
258+++ pastebin.d/paste.pound-python.org.conf 2014-12-07 21:54:13 +0000
259@@ -1,6 +1,6 @@
260 [pastebin]
261 basename = paste.pound-python.org
262-regexp = http://paste.pound-python.org
263+id_regexp = http://paste.pound-python.org
264
265 [format]
266 content = code
267
268=== modified file 'pastebin.d/paste.ubuntu.com.conf'
269--- pastebin.d/paste.ubuntu.com.conf 2012-06-13 16:41:21 +0000
270+++ pastebin.d/paste.ubuntu.com.conf 2014-12-07 21:54:13 +0000
271@@ -1,6 +1,6 @@
272 [pastebin]
273 basename = paste.ubuntu.com
274-regexp = http://paste.ubuntu.com
275+id_regexp = http://paste.ubuntu.com
276
277 [format]
278 user = poster
279
280=== modified file 'pastebin.d/paste.ubuntu.org.cn.conf'
281--- pastebin.d/paste.ubuntu.org.cn.conf 2012-06-13 16:41:21 +0000
282+++ pastebin.d/paste.ubuntu.org.cn.conf 2014-12-07 21:54:13 +0000
283@@ -1,6 +1,6 @@
284 [pastebin]
285 basename = paste.ubuntu.org.cn
286-regexp = http://paste.ubuntu.org.cn
287+id_regexp = http://paste.ubuntu.org.cn
288
289 [format]
290 user = poster
291
292=== modified file 'pastebin.d/paste2.org.conf'
293--- pastebin.d/paste2.org.conf 2014-01-06 23:16:01 +0000
294+++ pastebin.d/paste2.org.conf 2014-12-07 21:54:13 +0000
295@@ -1,6 +1,6 @@
296 [pastebin]
297 basename = paste2.org
298-regexp = http://paste2.org
299+id_regexp = http://paste2.org
300
301 [format]
302 title = description
303
304=== modified file 'pastebin.d/pastebin.com.conf'
305--- pastebin.d/pastebin.com.conf 2012-06-13 17:15:59 +0000
306+++ pastebin.d/pastebin.com.conf 2014-12-07 21:54:13 +0000
307@@ -1,6 +1,9 @@
308 [pastebin]
309 basename = pastebin.com
310-regexp = http://((([a-zA-Z0-9\-_\.]*)(pastebin\.com)))
311+id_regexp = http://((([a-zA-Z0-9\-_\.]*)(pastebin\.com)))
312+result_type = url
313+result_regexp = (.*)
314+page = /api/api_post.php
315
316 [format]
317 content = api_paste_code
318@@ -10,9 +13,7 @@
319 expiry = api_paste_expire_date
320 format = api_paste_format
321 email = api_paste_email
322-page = page
323 submit = submit
324-regexp = regexp
325 api_dev_key = api_dev_key
326 api_option = api_option
327
328@@ -25,5 +26,3 @@
329 email =
330 api_dev_key = 253ce2f0a45140ee0a44ca99aa492260
331 api_option = paste
332-page = /api/api_post.php
333-regexp = (.*)
334
335=== modified file 'pastebin.d/pastebin.mate-desktop.org.conf'
336--- pastebin.d/pastebin.mate-desktop.org.conf 2014-01-06 20:40:29 +0000
337+++ pastebin.d/pastebin.mate-desktop.org.conf 2014-12-07 21:54:13 +0000
338@@ -1,14 +1,12 @@
339 [pastebin]
340 basename = pastebin.mate-desktop.org
341-regexp = http://pastebin.mate-desktop.org
342+page = /api/json/create
343+id_regexp = http://pastebin.mate-desktop.org
344+result_type = id
345+result_regexp = "id": "([^"]*)"
346+post_format = json
347
348 [format]
349-content = text
350+content = data
351 user = name
352-format = lang
353-page = page
354-regexp = regexp
355-
356-[defaults]
357-page = /api/create
358-regexp = (.*)
359+format = language
360
361=== modified file 'pastebin.d/pb.daviey.com.conf'
362--- pastebin.d/pb.daviey.com.conf 2012-06-13 17:15:59 +0000
363+++ pastebin.d/pb.daviey.com.conf 2014-12-07 21:54:13 +0000
364@@ -1,16 +1,15 @@
365 [pastebin]
366 basename = pb.daviey.com
367-regexp = http://pb.daviey.com
368+id_regexp = http://pb.daviey.com
369+result_regexp = "/(.*/)".*
370+result_type = id
371+page = api/?_call=new
372
373 [format]
374 user = author
375 content = content
376-page = page
377 expire_options = expires
378 title = title
379-regexp = regexp
380
381 [defaults]
382-page = api/?_call=new
383-regexp = "/(.*/)".*
384 expire_options = 7889232
385
386=== modified file 'pastebin.d/slexy.org.conf'
387--- pastebin.d/slexy.org.conf 2012-06-13 17:15:59 +0000
388+++ pastebin.d/slexy.org.conf 2014-12-07 21:54:13 +0000
389@@ -1,12 +1,12 @@
390 [pastebin]
391 basename = slexy.org
392-regexp = http://slexy.org
393+id_regexp = http://slexy.org
394+page = /index.php/submit
395
396 [format]
397 user = author
398 content = raw_paste
399 title = desc
400-page = page
401 language = language
402 permissions = permissions
403 linenumbers = linenumbers
404@@ -16,7 +16,6 @@
405
406 [defaults]
407 submit = Submit Paste
408-page = /index.php/submit
409 language = text
410 permissions = 0
411 comment =
412
413=== modified file 'pastebin.d/sprunge.us.conf'
414--- pastebin.d/sprunge.us.conf 2012-06-13 16:41:21 +0000
415+++ pastebin.d/sprunge.us.conf 2014-12-07 21:54:13 +0000
416@@ -1,11 +1,9 @@
417 [pastebin]
418 basename = sprunge.us
419-regexp = http://sprunge.us
420+id_regexp = http://sprunge.us
421+result_regexp=http://sprunge.us/(.*)$
422+result_type = id
423
424 [format]
425 content = sprunge
426 format = lang
427-regexp = regexp
428-
429-[defaults]
430-regexp=http://sprunge.us/(.*)$
431
432=== modified file 'pastebinit'
433--- pastebinit 2014-01-18 04:51:35 +0000
434+++ pastebinit 2014-12-07 21:54:13 +0000
435@@ -4,7 +4,7 @@
436 # Author: Stéphane Graber <stgraber@ubuntu.com>
437 # Written by Stéphane Graber <stgraber@stgraber.org>
438 # Daniel Bartlett <dan@f-box.org>
439-# Last modification : Mon Jan 6 18:46:46 EST 2014
440+# Last modification : Mon Nov 24 15:07:45 EST 2014
441
442 # This program is free software; you can redistribute it and/or modify
443 # it under the terms of the GNU General Public License as published by
444@@ -23,224 +23,52 @@
445 from __future__ import print_function
446
447 import sys
448+import os
449+import re
450+import getopt
451+import gettext
452+import socket
453+import xml.dom.minidom
454+
455 if sys.version[0] == "2":
456- from ConfigParser import SafeConfigParser
457+ from ConfigParser import SafeConfigParser, NoOptionError, NoSectionError
458 from urllib import urlencode
459 from urllib import FancyURLopener
460 else:
461- from configparser import SafeConfigParser
462+ #SafeConfigParser is now a deprecated alias to ConfigParser
463+ from configparser import \
464+ ConfigParser as SafeConfigParser, NoOptionError, NoSectionError
465 from urllib.parse import urlencode
466 from urllib.request import FancyURLopener
467
468-# Set the default pastebin
469-defaultPB = "http://pastebin.com"
470-
471-# Now try to override it with a distributor pastebin
472-try:
473- import lsb_release
474- release = lsb_release.get_distro_information()['ID'].lower()
475- if release == 'debian':
476- defaultPB = "http://paste.debian.net"
477- elif release == 'fedora':
478- defaultPB = "http://fpaste.org"
479- elif release == 'ubuntu':
480- defaultPB = "http://paste.ubuntu.com"
481-except ImportError:
482- pass
483-
484-try:
485- import getopt
486- import gettext
487- import os
488- import re
489- import socket
490- import xml.dom.minidom
491-
492- try:
493- import json
494- except ImportError:
495- json = None
496-
497- _ = gettext.gettext
498- gettext.textdomain("pastebinit")
499-
500- # Timeout after 5s
501- socket.setdefaulttimeout(5)
502-
503- # Version number to show in the usage
504- version = "1.4.1"
505- configfile = os.path.expanduser("~/.pastebinit.xml")
506-
507- # Custom urlopener to handle 401's
508- class pasteURLopener(FancyURLopener):
509- version = "Pastebinit v%s" % version
510-
511- def http_error_401(self, url, fp, errcode, errmsg, headers, data=None):
512- return None
513-
514- def preloadPastebins():
515- # Check several places for config files:
516- # - global config in /etc/pastebin.d
517- # - for source checkout, config in the checkout
518- # - user's overrides in ~/.pastebin.d
519- # Files found later override files found earlier.
520- pastebind = {}
521- for confdir in ['/usr/share/pastebin.d', '/etc/pastebin.d',
522- '/usr/local/etc/pastebin.d',
523- os.path.expanduser('~/.pastebin.d'),
524- os.path.join(
525- os.path.dirname(
526- os.path.realpath(__file__)), 'pastebin.d')]:
527- try:
528- confdirlist = os.listdir(confdir)
529- except OSError:
530- continue
531-
532- for fileitem in confdirlist:
533- if fileitem.startswith('.') or not fileitem.endswith('.conf'):
534- continue
535-
536- filename = os.path.join(confdir, fileitem)
537- instance = SafeConfigParser()
538- try:
539- instance.read(filename)
540- except UnicodeError:
541- continue
542-
543- if not instance.has_section('pastebin'):
544- print(_('%s: no section [pastebin]') % filename,
545- file=sys.stderr)
546- continue
547-
548- if not instance.has_option('pastebin', 'basename'):
549- print(_("%s: no 'basename' in [pastebin]") % filename,
550- file=sys.stderr)
551- continue
552-
553- pastebind[instance.get('pastebin', 'basename')] = instance
554- return pastebind
555-
556- # pastey.net obfuscates parent ids for replies. Rather than taking the
557- # post ID given as the parent ID, we must handle this by going to that
558- # post page and looking up what the invisible parent ID field will be
559- # set to for children.
560- def doParentFixup(website, paramname, parentid):
561- if parentid == "":
562- return ""
563- url_opener = pasteURLopener()
564- page = url_opener.open(website + '/' + parentid, None)
565- matches = re.split('<input.*?name="' + paramname + '".*?value="(.*?)"',
566- page.read())
567- if len(matches) <= 1 or re.match(parentid, matches[1]) is None:
568- # The obfuscated version didn't begin with the partial version,
569- # or unable to find the obfuscated version for some reason!
570- # Create a paste with no parent (should we throw, instead?)
571- return ""
572- return matches[1]
573-
574- #Return the parameters depending of the pastebin used
575- def getParameters(website, pastebind, content, user, jabberid, version,
576- format, parentpid, permatag, title, username,
577- password):
578- "Return the parameters array for the selected pastebin"
579- params = {}
580- for paste_name, paste_config in pastebind.items():
581- basename = paste_config.get('pastebin', 'basename')
582- if basename == website or paste_name == website:
583- website = "http://%s" % basename
584-
585- if re.search(paste_config.get('pastebin', 'regexp'), website):
586- if paste_config.has_option('pastebin', 'sizelimit'):
587- params['sizelimit'] = paste_config.get('pastebin',
588- 'sizelimit')
589-
590- for param in paste_config.options('format'):
591- paramname = paste_config.get('format', param)
592- if param == 'user':
593- params[paramname] = user
594- elif param == 'content':
595- params[paramname] = content
596- elif param == 'title':
597- params[paramname] = title
598- elif param == 'version':
599- params[paramname] = version
600- elif param == 'format':
601- params[paramname] = format
602- elif param == 'parentpid':
603- params[paramname] = doParentFixup(website, paramname,
604- parentpid)
605- elif param == 'permatag':
606- params[paramname] = permatag
607- elif param == 'username':
608- params[paramname] = username
609- elif param == 'password':
610- params[paramname] = password
611- elif param == 'jabberid':
612- params[paramname] = jabberid
613- else:
614- params[paramname] = paste_config.get('defaults', param)
615- if params:
616- return website, params
617- else:
618- print(_("Unknown website, please post a bugreport to request "
619- "this pastebin to be added (%s)") % website,
620- file=sys.stderr)
621- sys.exit(1)
622-
623- #XML Handling methods
624- def getText(nodelist):
625- rc = ""
626- for node in nodelist:
627- if node.nodeType == node.TEXT_NODE:
628- rc = rc + node.data
629- return rc
630-
631- def getNodes(nodes, title):
632- return nodes.getElementsByTagName(title)
633-
634- def getFirstNode(nodes, title):
635- return getNodes(nodes, title)[0]
636-
637- def getFirstNodeText(nodes, title):
638- return getText(getFirstNode(nodes, title).childNodes)
639-
640- # Display usage instructions
641- def Usage(fd=sys.stdout):
642- print("pastebinit v" + version, file=fd)
643- print(_("Reads on stdin for input or takes a list of filenames "
644- "as parameters"), file=fd)
645- print(_("Optional arguments (not supported by all pastebins):"),
646- file=fd)
647- print(_("\t-a <author:default is '%s'>") % user, file=fd)
648- print(_("\t-b <pastebin url:default is '%s'>") % website, file=fd)
649- print(_("\t-f <format of paste:default is '%s'>") % format, file=fd)
650- print(_("\t-h This help screen"), file=fd)
651- print(_("\t-i <input file>"), file=fd)
652- print(_("\t-l List all supported pastebins"), file=fd)
653- print(_("\t-j <jabberid for notifications:default is '%s'>") %
654- jabberid, file=fd)
655- print(_("\t-m <permatag for all versions of a post:default is blank>"),
656- file=fd)
657- print(_("\t-r <parent posts ID:defaults to none>"), file=fd)
658- print(_("\t-t <title of paste:default is blank>"), file=fd)
659- print(_("\t-u <username> -p <password>"), file=fd)
660- print(_("\t-v Print the version number"), file=fd)
661-
662- # Set defaults
663- website = defaultPB
664- user = os.environ.get('USER')
665- jabberid = ""
666- title = ""
667- permatag = ""
668- format = "text"
669- username = ""
670- password = ""
671- filenames = []
672- content = ""
673- parentpid = ""
674-
675- #Example configuration file string
676- configexample = """\
677+try:
678+ import gnupg
679+except ImportError:
680+ gnupg = None
681+
682+try:
683+ import json
684+except ImportError:
685+ json = None
686+
687+#Enables input redirection during tests
688+stdin = sys.stdin
689+stderr = sys.stderr
690+stdout = sys.stdout
691+
692+_ = gettext.gettext
693+gettext.textdomain("pastebinit")
694+
695+# Timeout after 5s
696+socket.setdefaulttimeout(5)
697+
698+# Version number to show in the usage
699+version = "1.4.1"
700+config_path = os.path.expanduser("~/.pastebinit.xml")
701+verbose = False
702+
703+#Example configuration file string
704+config_example = """\
705 <pastebinit>
706 <pastebin>http://paste.debian.net</pastebin>
707 <author>A pastebinit user</author>
708@@ -249,201 +77,508 @@
709 </pastebinit>
710 """
711
712- #Open configuration file if it exists
713- try:
714- f = open(configfile)
715- configtext = f.read()
716+
717+def get_default_pastebin():
718+ default = "http://pastebin.com"
719+ try:
720+ import lsb_release
721+ return {
722+ 'debian': "http://paste.debian.net",
723+ 'fedora': "http://fpaste.org",
724+ 'ubuntu': "http://paste.ubuntu.com",
725+ }[lsb_release.get_distro_information()['ID'].lower()]
726+ except KeyError:
727+ return default
728+ except ImportError:
729+ return default
730+
731+
732+defaults = {
733+ 'gpg_sign': False,
734+ 'gpg_encrypt': False,
735+ 'gpg_recipients': '',
736+ 'gpg_dir': os.path.expanduser("~/.gnupg"),
737+ 'gpg_key': None,
738+ 'website': get_default_pastebin(),
739+ 'user': os.environ.get('USER'),
740+ 'format': "text",
741+ 'jabberid': "",
742+ 'title': "",
743+ 'permatag': "",
744+ 'username': "",
745+ 'password': "",
746+ 'review': False,
747+ 'parentpid': "",
748+ 'verbose': False,
749+}
750+
751+
752+class PasteURLopener(FancyURLopener):
753+ """Custom urlopener to handle 401's"""
754+ version = "Pastebinit v%s" % version
755+
756+ def __init__(self, settings):
757+ self.settings = settings
758+ FancyURLopener.__init__(self)
759+
760+ def http_error_401(self, url, fp, errcode, errmsg, headers, data=None):
761+ return None
762+
763+ def format_params(self, params):
764+ if self.settings.get('format', '') == 'json':
765+ if not json:
766+ print(_("Could not find any json library."), file=stderr)
767+ sys.exit(1)
768+ return json.dumps(params)
769+ else:
770+ return urlencode(params)
771+
772+ def make_request(self, url, params):
773+ post_params = self.format_params(params)
774+
775+ if self.settings.get('format', '') == 'json':
776+ self.addheader('Content-type', 'text/json')
777+
778+ if self.settings.get('verbose', False):
779+ print("POSTing to: %s" % url, file=stderr)
780+ print("\nParams: %s" % str(post_params), file=stderr)
781+
782+ try:
783+ return self.open(url, post_params)
784+ except KeyboardInterrupt:
785+ print(_("KeyboardInterrupt caught."), file=stderr)
786+ sys.exit(1)
787+ except Exception as e:
788+ print(_("Failed to contact the server: %s") % e, file=stderr)
789+ sys.exit(1)
790+
791+
792+def usage(fd):
793+ print("pastebinit v" + version, file=fd)
794+ print(_("Reads on stdin for input or takes a list of filenames "
795+ "as parameters"), file=fd)
796+ print(_("Optional arguments (not supported by all pastebins):"), file=fd)
797+ print(_("\t-a <author:default is '%s'>") % defaults['user'], file=fd)
798+ print(_("\t-b <pastebin url:default is '%s'>") % defaults['website'],
799+ file=fd)
800+ print(_("\t-f <format of paste:default is '%s' "
801+ "(or from pastebin config)>") % defaults['format'], file=fd)
802+ print(_("\t-h This help screen"), file=fd)
803+ print(_("\t-i <input file>"
804+ "This may be used multiple times (-i file1 -i file2)"), file=fd)
805+ print(_("\t-l List all supported pastebins"), file=fd)
806+ print(_("\t-j <jabberid for notifications:default is '%s'>") %
807+ defaults['jabberid'], file=fd)
808+ print(_("\t-m <permatag for all versions of a post:default is blank>"),
809+ file=fd)
810+ print(_("\t-R Review before pasting"), file=fd)
811+ print(_("\t-r <parent posts ID:defaults to none>"), file=fd)
812+ print(_("\t-t <title of paste:default is blank>"), file=fd)
813+ print(_("\t-u <username> -p <password>"), file=fd)
814+ print(_("\t-v Print the version number"), file=fd)
815+ print(_("\t--verbose Verbose output to stderr"), file=fd)
816+
817+ print(_("Encryption (In case you don't just pipe through gpg)"), file=fd)
818+ print(_("\t-e <recipient> "
819+ "Encrypt the pasted content for the given recipient. "
820+ "This may be used multiple times (-e recip1 -e recipt2)"), file=fd)
821+ print(_("\t-s Sign the pasted content"), file=fd)
822+ print(_("\t-G <GPG home directory:default is %s>" % defaults['gpg_dir']),
823+ file=fd)
824+ print(_("\t-g <GPG key id:default is %s> "
825+ "This can be an email address or a name."
826+ "Supply -R to confirm the key." % defaults['gpg_key']), file=fd)
827+
828+
829+def preloadPastebins():
830+ """
831+ Check several places for config files:
832+ - global config in /etc/pastebin.d
833+ - for source checkout, config in the checkout
834+ - user's overrides in ~/.pastebin.d
835+ Files found later override files found earlier.
836+ """
837+ pastebind = {}
838+ for confdir in ['/usr/share/pastebin.d',
839+ '/etc/pastebin.d',
840+ '/usr/local/etc/pastebin.d',
841+ os.path.expanduser('~/.pastebin.d'),
842+ os.path.join(
843+ os.path.dirname(os.path.realpath(__file__)),
844+ 'pastebin.d'),
845+ ]:
846+ try:
847+ confdirfiles = [
848+ os.path.join(confdir, x) for x in os.listdir(confdir)
849+ if x.endswith('.conf') and not x.startswith('.')
850+ ]
851+ except OSError:
852+ continue
853+
854+ for fname in confdirfiles:
855+ parser = SafeConfigParser()
856+ try:
857+ parser.read(fname)
858+ except UnicodeError:
859+ print(_('%s: Unicode Error') % fname, file=stderr)
860+ continue
861+
862+ if not parser.has_section('pastebin'):
863+ print(_('%s: no section [pastebin]') % fname, file=stderr)
864+ continue
865+
866+ if not parser.has_option('pastebin', 'basename'):
867+ print(_("%s: no 'basename' in [pastebin]") % fname, file=stderr)
868+ continue
869+
870+ pastebind[parser.get('pastebin', 'basename')] = parser
871+ return pastebind
872+
873+
874+def doParentFixup(paramname, settings):
875+ """
876+ pastey.net obfuscates parent ids for replies. Rather than taking the
877+ post ID given as the parent ID, we must handle this by going to that
878+ post page and looking up what the invisible parent ID field will be
879+ set to for children.
880+ """
881+ website = settings['website']
882+ parentid = settings['parentid']
883+ if not parentid:
884+ return ""
885+ if not website.endswith("/"):
886+ website += "/"
887+ matches = re.split(
888+ '<input.*?name="' + paramname + '".*?value="(.*?)"',
889+ PasteURLopener(settings.get('post_format', '')).open(
890+ website + parentid.lstrip('/'), None).read()
891+ )
892+ if len(matches) <= 1 or re.match(parentid, matches[1]) is None:
893+ # The obfuscated version didn't begin with the partial version,
894+ # or unable to find the obfuscated version for some reason!
895+ # Create a paste with no parent (should we throw, instead?)
896+ return ""
897+ return matches[1]
898+
899+
900+def getPasteConfig(website):
901+ for paste_name, paste_config in preloadPastebins().items():
902+ basename = paste_config.get('pastebin', 'basename')
903+ if basename == website or paste_name == website:
904+ website = "http://%s" % basename
905+ if re.search(paste_config.get('pastebin', 'id_regexp'), website):
906+ if not website.endswith("/"):
907+ website += "/"
908+ return paste_config, website
909+
910+ print(_("Unknown website, please post a bugreport to request this "
911+ "pastebin to be added (%s)") % website,
912+ file=stderr)
913+ sys.exit(1)
914+
915+
916+def readPasteConfig(paste_config, settings):
917+ """Return the parameters array for the selected pastebin"""
918+
919+ for param in paste_config.options('pastebin'):
920+ settings[param] = paste_config.get('pastebin', param)
921+
922+ post_params = {}
923+ for param in paste_config.options('format'):
924+ paramname = paste_config.get('format', param)
925+
926+ if param == 'content':
927+ pass
928+ elif param == 'parentpid':
929+ post_params[paramname] = doParentFixup(paramname, settings)
930+ else:
931+ try:
932+ post_params[paramname] = paste_config.get('defaults', param)
933+ except NoOptionError:
934+ pass
935+ except NoSectionError:
936+ pass
937+
938+ try:
939+ post_params[paramname] = settings[param]
940+ except KeyError:
941+ pass
942+
943+ return post_params
944+
945+
946+def getText(parent_node):
947+ """Read the text from an XML node"""
948+ rc = ""
949+ for node in parent_node.childNodes:
950+ if node.nodeType == node.TEXT_NODE:
951+ rc += node.data
952+ else:
953+ rc += getText(node)
954+ return rc
955+
956+
957+def readFile(location, mode="r"):
958+ f = open(location, mode)
959+ try:
960+ return f.read()
961+ except KeyboardInterrupt:
962+ print(_("KeyboardInterrupt caught."), file=stderr)
963+ sys.exit(1)
964+ except:
965+ print(_("Unable to read from: %s") % location, file=stderr)
966+ sys.exit(1)
967+ finally:
968 f.close()
969- gotconfigxml = True
970+
971+
972+def readConfigFile(config_text):
973+ """Update settings from config XML"""
974+ try:
975+ settings = {}
976+ configxml = xml.dom.minidom.parseString(config_text)
977+ for xml_var, setting in (('pastebin', 'website'),
978+ ('author', 'user'),
979+ ('format', 'format'),
980+ ('jabberid', 'jabberid')):
981+ try:
982+ settings[setting] = getText(
983+ configxml.getElementsByTagName(xml_var)[0])
984+ except IndexError:
985+ pass
986+ return settings
987 except KeyboardInterrupt:
988- print(_("KeyboardInterrupt caught."), file=sys.stderr)
989+ print(_("KeyboardInterrupt caught."), file=stderr)
990 sys.exit(1)
991 except:
992- gotconfigxml = False
993-
994- #Parse configuration file
995- if gotconfigxml:
996+ print(_("Error parsing configuration file!"), file=stderr)
997+ print(_("Please ensure that your configuration file looks "
998+ "similar to the following:"), file=stderr)
999+ print(config_example, file=stderr)
1000+ sys.exit(1)
1001+
1002+
1003+def getOpts(args=sys.argv[1:]):
1004+ try:
1005+ optlist, arglist = getopt.getopt(
1006+ args, 'sRhvli:e:G:g:f:b:a:r:j:t:m:u:p:', ('verbose',))
1007+ except getopt.GetoptError as e:
1008+ print(_("Invalid arguments: %s!" % e) + "\n", file=stderr)
1009+ usage(stderr)
1010+ sys.exit(1)
1011+ opts = dict(optlist)
1012+ opts['-e'] = [opt[1] for opt in optlist if opt[0] == "-e"]
1013+ opts['-i'] = [opt[1] for opt in optlist if opt[0] == "-i"] + arglist
1014+ return opts
1015+
1016+
1017+def readOptions(opts):
1018+ settings = {}
1019+
1020+ # Die if support information is requested
1021+ if "-h" in opts:
1022+ usage(stdout)
1023+ sys.exit(0)
1024+ if "-v" in opts:
1025+ print("pastebinit v" + version, file=stdout)
1026+ sys.exit(0)
1027+ if "-l" in opts:
1028+ print(_("Supported pastebins:"), file=stdout)
1029+ for pastebin in sorted(preloadPastebins()):
1030+ print("- %s" % pastebin, file=stdout)
1031+ sys.exit(0)
1032+
1033+ # Configure the environment
1034+ def setFromOpt(opt, label):
1035 try:
1036- configxml = xml.dom.minidom.parseString(configtext)
1037- for variable, key in (('pastebin', 'website'), ('author', 'user'),
1038- ('format', 'format'),
1039- ('jabberid', 'jabberid')):
1040- try:
1041- value = getFirstNodeText(configxml, variable)
1042- vars()[key] = value
1043- except:
1044- pass
1045- except KeyboardInterrupt:
1046- print(_("KeyboardInterrupt caught."), file=sys.stderr)
1047- sys.exit(1)
1048- except:
1049- print(_("Error parsing configuration file!"), file=sys.stderr)
1050- print(_("Please ensure that your configuration file looks "
1051- "similar to the following:"), file=sys.stderr)
1052- print(configexample, file=sys.stderr)
1053- sys.exit(1)
1054-
1055- # Get options
1056- try:
1057- optlist, arglist = getopt.getopt(sys.argv[1:],
1058- 'hvli:f:b:a:r:j:t:m:u:p:')
1059- except KeyboardInterrupt:
1060- print(_("KeyboardInterrupt caught."), file=sys.stderr)
1061- sys.exit(1)
1062- except getopt.GetoptError:
1063- print(_("Invalid arguments!\n"), file=sys.stderr)
1064- Usage(sys.stderr)
1065- sys.exit(1)
1066-
1067- # Get the config
1068- pastebind = preloadPastebins()
1069-
1070- # Iterate through options
1071- for opt in optlist:
1072- if opt[0] == "-h":
1073- Usage()
1074- sys.exit(0)
1075- if opt[0] == "-i":
1076- filenames.append(opt[1])
1077- elif opt[0] == "-f":
1078- format = opt[1]
1079- elif opt[0] == "-b":
1080- website = opt[1]
1081- elif opt[0] == "-a":
1082- user = opt[1]
1083- elif opt[0] == "-r":
1084- parentpid = opt[1]
1085- elif opt[0] == "-j":
1086- jabberid = opt[1]
1087- elif opt[0] == "-l":
1088- print(_("Supported pastebins:"))
1089- for pastebin in sorted(pastebind):
1090- print("- %s" % pastebin)
1091- sys.exit(0)
1092- elif opt[0] == "-t":
1093- title = opt[1]
1094- elif opt[0] == "-m":
1095- permatag = opt[1]
1096- elif opt[0] == "-u":
1097- username = opt[1]
1098- elif opt[0] == "-p":
1099- password = opt[1]
1100- elif opt[0] == "-v":
1101- print("pastebinit v" + version)
1102- sys.exit(0)
1103-
1104- filenames += arglist
1105-
1106- if not filenames:
1107- filenames.append("-")
1108-
1109- contents = []
1110- for filename in filenames:
1111- # If - is specified as a filename read from stdin
1112- # otherwise load the specified files.
1113- if filename == "-":
1114- content = sys.stdin.read()
1115+ settings[label] = opts[opt]
1116+ except KeyError:
1117+ pass
1118+
1119+ setFromOpt('-b', 'website')
1120+ setFromOpt('-a', 'user')
1121+ setFromOpt('-f', 'format')
1122+ setFromOpt('-j', 'jabberid')
1123+ setFromOpt('-t', 'title')
1124+ setFromOpt('-m', 'permatag')
1125+ setFromOpt('-u', 'username')
1126+ setFromOpt('-p', 'password')
1127+ setFromOpt('-r', 'parentpid')
1128+ setFromOpt('-g', 'gpg_key')
1129+ setFromOpt('-e', 'gpg_recipients')
1130+ try:
1131+ settings['gpg_dir'] = os.path.expanduser(opts['-G'])
1132+ except KeyError:
1133+ pass
1134+ settings['verbose'] = "--verbose" in opts
1135+ settings['review'] = "-R" in opts
1136+ settings['gpg_encrypt'] = bool(opts['-e'])
1137+ settings['gpg_sign'] = '-s' in opts
1138+
1139+ settings['contents'] = [readFile(fname, "rb") for fname in opts['-i']]
1140+ if not settings['contents']:
1141+ settings['contents'].append(stdin.read())
1142+ return settings
1143+
1144+
1145+def readPageURL(page, settings):
1146+ website = settings['website']
1147+ result_type = settings.get('result_type', None)
1148+ try:
1149+ # Check if we have to apply a regexp
1150+ if result_type:
1151+ result = page.read().decode('utf-8')
1152+ result_regex = settings.get('result_regexp', None)
1153+ if result_regex:
1154+ result = re.split(result_regex, result)[1]
1155+
1156+ if result_type == 'url':
1157+ page_url = result.strip()
1158+ elif result_type == 'id':
1159+ page_url = settings.get('target_url', website) + result.strip()
1160+ else:
1161+ raise ValueError(_("Unsupported result_type: %s" % result_type))
1162 else:
1163- try:
1164- with open(filename, "rb") as fd:
1165- content = fd.read()
1166- except KeyboardInterrupt:
1167- print(_("KeyboardInterrupt caught."), file=sys.stderr)
1168- sys.exit(1)
1169- except:
1170- print(_("Unable to read from: %s") % filename, file=sys.stderr)
1171- sys.exit(1)
1172-
1173+ page_url = page.url
1174+
1175+ # You may have posted to https:, but were given an http: url
1176+ if website.startswith('https:'):
1177+ page_url = page_url.replace('http:', 'https:')
1178+ return page_url
1179+
1180+ except KeyboardInterrupt:
1181+ print(_("KeyboardInterrupt caught."), file=stderr)
1182+ sys.exit(1)
1183+ except:
1184+ print(_("Unable to read or parse the result page, it could be a "
1185+ "server timeout or a change server side, "
1186+ "try with another pastebin."), file=stderr)
1187+ sys.exit(1)
1188+
1189+
1190+def validateContents(settings):
1191+ """Check for invalid contents"""
1192+ for content in settings['contents']:
1193 if not content:
1194 print(_("You are trying to send an empty document, exiting."),
1195- file=sys.stderr)
1196- sys.exit(1)
1197-
1198- contents.append(content)
1199-
1200- for content in contents:
1201- # Get the parameter array
1202- website, params = getParameters(website, pastebind, content, user,
1203- jabberid, version, format, parentpid,
1204- permatag, title, username, password)
1205-
1206- if not website.endswith("/"):
1207- website += "/"
1208-
1209- if "sizelimit" in params:
1210- if len(content) > int(params['sizelimit']):
1211- print(_("The content you are trying to send exceeds "
1212- "the pastebin's size limit."), file=sys.stderr)
1213- sys.exit(1)
1214- else:
1215- del params['sizelimit']
1216-
1217- reLink = None
1218- tmp_page = ""
1219- if "page" in params:
1220- website += params['page']
1221- tmp_page = params['page']
1222- del params["page"]
1223- if "regexp" in params:
1224- reLink = params['regexp']
1225- del params["regexp"]
1226- if "target_url" in params:
1227- target_url = params["target_url"]
1228- del params["target_url"]
1229- else:
1230- target_url = None
1231- if 'post_format' in params:
1232- post_format = params['post_format']
1233- del params['post_format']
1234- else:
1235- post_format = 'standard'
1236-
1237- url_opener = pasteURLopener()
1238-
1239- if post_format == 'json':
1240- if json:
1241- params = json.dumps(params)
1242- url_opener.addheader('Content-type', 'text/json')
1243- else:
1244- print(_("Could not find any json library."), file=sys.stderr)
1245- sys.exit(1)
1246- else:
1247- # Convert to a format usable with the HTML POST
1248- params = urlencode(params)
1249-
1250- # Send the informations and be redirected to the final page
1251- try:
1252- page = url_opener.open(website, params)
1253- except Exception as e:
1254- print(_("Failed to contact the server: %s") % e, file=sys.stderr)
1255- sys.exit(1)
1256-
1257- try:
1258- # Check if we have to apply a regexp
1259- if reLink:
1260- if target_url:
1261- website = target_url
1262- else:
1263- website = website.replace(tmp_page, "")
1264-
1265- if reLink == '(.*)':
1266- print(page.read().decode('utf-8').strip())
1267- else:
1268- # Print the result of the regexp
1269- print(website + re.split(reLink,
1270- page.read().decode('utf-8'))[1])
1271- else:
1272- # Get the final page and show the url
1273- print(page.url)
1274- except KeyboardInterrupt:
1275- print(_("KeyboardInterrupt caught."), file=sys.stderr)
1276- sys.exit(1)
1277- except:
1278- print(_("Unable to read or parse the result page, it could be a "
1279- "server timeout or a change server side, "
1280- "try with another pastebin."), file=sys.stderr)
1281- sys.exit(1)
1282-
1283-except KeyboardInterrupt:
1284- print(_("KeyboardInterrupt caught."), file=sys.stderr)
1285- sys.exit(1)
1286+ file=stderr)
1287+ sys.exit(1)
1288+
1289+ if len(content) > int(settings.get('sizelimit', sys.maxsize)):
1290+ print(_("The content you are trying to send exceeds "
1291+ "the pastebin's size limit."), file=stderr)
1292+ sys.exit(1)
1293+
1294+
1295+def confirm():
1296+ fd_tty = open("/dev/tty", "r")
1297+ answer = fd_tty.readline().rstrip('\n')
1298+ fd_tty.close()
1299+ return not answer or answer.upper().startswith("Y")
1300+
1301+
1302+def findKey(gpg, settings):
1303+ try:
1304+ if not settings['gpg_key']:
1305+ return gpg.list_keys()[0]
1306+ else:
1307+ for k in gpg.list_keys():
1308+ for uid in k['uids']:
1309+ if settings['gpg_key'].lower() in uid.lower():
1310+ return k
1311+ print(_("Could not find key with value: %s" % settings['gpg_key']),
1312+ file=stderr)
1313+ sys.exit(1)
1314+ except IndexError:
1315+ print(_('Tried to sign but no keys available.'), file=stderr)
1316+ sys.exit(1)
1317+
1318+
1319+def reviewKey(key, settings):
1320+ if settings['review']:
1321+ print(_("Review: Use this key '%s' [Y|n] ?" % key['uids']), file=stderr)
1322+ if not confirm():
1323+ print(_("Paste Aborted: key rejected by user"), file=stderr)
1324+ sys.exit(2)
1325+
1326+
1327+def getGPG(settings):
1328+ if not gnupg:
1329+ print(_("Please install python-gnupg."), file=stderr)
1330+ sys.exit(1)
1331+ return gnupg.GPG(gnupghome=settings['gpg_dir'])
1332+
1333+
1334+def reviewContent(content, settings):
1335+ if not settings['review']:
1336+ return True
1337+
1338+ print("-" * 15 + " REVIEW " + "-" * 15, file=stdout)
1339+ print(content, file=stdout)
1340+ print("-" * 15 + " REVIEW " + "-" * 15, file=stdout)
1341+ print(_("Review: do you wish to paste to %s [Y|n] ?" %
1342+ settings['website']), file=stdout)
1343+ # need to directly open the terminal to get input
1344+ # since stdin is likely to have been redirected from a pipe
1345+ return confirm()
1346+
1347+
1348+def handleEncryption(settings):
1349+ #Sign contents
1350+ if settings['gpg_sign'] and not settings['gpg_encrypt']:
1351+ gpg = getGPG(settings)
1352+ key = findKey(gpg, settings)
1353+ reviewKey(key, settings)
1354+ return [
1355+ gpg.sign(c, keyid=key['keyid'])
1356+ for c in settings['contents']
1357+ ]
1358+
1359+ #Encrypt contents
1360+ if settings['gpg_encrypt'] and not settings['gpg_sign']:
1361+ gpg = getGPG(settings)
1362+ return [
1363+ gpg.encrypt(c, settings['gpg_recipients'])
1364+ for c in settings['contents']
1365+ ]
1366+
1367+ #Encrypt and sign contents
1368+ if settings['gpg_encrypt'] and settings['gpg_sign']:
1369+ gpg = getGPG(settings)
1370+ key = findKey(gpg, settings)
1371+ reviewKey(key, settings)
1372+ return [
1373+ gpg.encrypt(c, settings['gpg_recipients'], sign=key['keyid'])
1374+ for c in settings['contents']
1375+ ]
1376+
1377+ return settings['contents']
1378+
1379+
1380+def main():
1381+ try:
1382+ settings = defaults.copy()
1383+ settings.update(readConfigFile(readFile(config_path)))
1384+ settings.update(readOptions(getOpts()))
1385+
1386+ paste_config, settings['website'] = getPasteConfig(settings['website'])
1387+ params = readPasteConfig(paste_config, settings)
1388+
1389+ validateContents(settings)
1390+ opener = PasteURLopener(settings)
1391+
1392+ url = settings['website'] + settings.get('page', '').lstrip("/")
1393+ settings['contents'] = handleEncryption(settings)
1394+ for content in settings['contents']:
1395+ params[paste_config.get('format', 'content')] = content
1396+ if not reviewContent(content, settings):
1397+ print(_("Paste aborted"), file=stderr)
1398+ continue
1399+ page = opener.make_request(url, params)
1400+ print(readPageURL(page, settings), file=stdout)
1401+ except KeyboardInterrupt:
1402+ print(_("KeyboardInterrupt caught."), file=stderr)
1403+ sys.exit(1)
1404+
1405+if __name__ == '__main__':
1406+ main()
1407\ No newline at end of file
1408
1409=== added symlink 'pastebinit.py'
1410=== target is u'pastebinit'
1411=== added file 'test.py'
1412--- test.py 1970-01-01 00:00:00 +0000
1413+++ test.py 2014-12-07 21:54:13 +0000
1414@@ -0,0 +1,135 @@
1415+import unittest
1416+import pastebinit
1417+try:
1418+ from StringIO import StringIO
1419+except ImportError:
1420+ from io import StringIO
1421+
1422+pastebinit.stdin = StringIO('')
1423+pastebinit.stdout = StringIO('')
1424+pastebinit.stderr = StringIO('')
1425+
1426+
1427+class ConfigXMLTests(unittest.TestCase):
1428+
1429+ def test_config_xml_parse(self):
1430+ xml = """\
1431+ <pastebinit>
1432+ <pastebin>http://paste.debian.net</pastebin>
1433+ <author>A pastebinit user</author>
1434+ <jabberid>nobody@nowhere.org</jabberid>
1435+ <format>text</format>
1436+ </pastebinit>
1437+ """
1438+ out = {
1439+ 'website': u'http://paste.debian.net',
1440+ 'user': u'A pastebinit user',
1441+ 'jabberid': u'nobody@nowhere.org',
1442+ 'format': u'text',
1443+ }
1444+ assert pastebinit.readConfigFile(xml) == out, 'readConfigFile failed'
1445+
1446+ def test_config_xml_parse_missing(self):
1447+ xml = """\
1448+ <pastebinit>
1449+ <author>A pastebinit user</author>
1450+ <jabberid>nobody@nowhere.org</jabberid>
1451+ <format>text</format>
1452+ </pastebinit>
1453+ """
1454+ out = {
1455+ 'user': u'A pastebinit user',
1456+ 'jabberid': u'nobody@nowhere.org',
1457+ 'format': u'text',
1458+ }
1459+ assert pastebinit.readConfigFile(xml) == out, 'readConfigFile failed'
1460+
1461+ def test_config_xml_parse_additional(self):
1462+ xml = """\
1463+ <pastebinit>
1464+ <author>A pastebinit user</author>
1465+ <jabberid>nobody@nowhere.org</jabberid>
1466+ <format>text</format>
1467+ <test>nonsense</test>
1468+ </pastebinit>
1469+ """
1470+ out = {
1471+ 'user': u'A pastebinit user',
1472+ 'jabberid': u'nobody@nowhere.org',
1473+ 'format': u'text',
1474+ }
1475+ assert pastebinit.readConfigFile(xml) == out, 'readConfigFile failed'
1476+
1477+ def test_config_xml_parse_invalid(self):
1478+ try:
1479+ pastebinit.readConfigFile("invalid")
1480+ assert False, "Invalid file was parsed without error."
1481+ except SystemExit:
1482+ pass
1483+
1484+
1485+class GetOptsTests(unittest.TestCase):
1486+
1487+ def _check_arg(self, argname, settingname):
1488+ settings = pastebinit.readOptions(pastebinit.getOpts([argname, 'test']))
1489+ assert settings[settingname] == 'test', "Failed to read " + argname
1490+
1491+ def test_help(self):
1492+ try:
1493+ pastebinit.readOptions(['-h'])
1494+ assert False, "Failed to quit after help option"
1495+ except SystemExit:
1496+ pass
1497+
1498+ def test_list(self):
1499+ try:
1500+ pastebinit.readOptions(['-l'])
1501+ assert False, "Failed to quit after list option"
1502+ except SystemExit:
1503+ pass
1504+
1505+ def test_version(self):
1506+ try:
1507+ pastebinit.readOptions(['-v'])
1508+ assert False, "Failed to quit after version option"
1509+ except SystemExit:
1510+ pass
1511+
1512+ def test_website(self):
1513+ self._check_arg('-b', 'website')
1514+
1515+ def test_user(self):
1516+ self._check_arg('-a', 'user')
1517+
1518+ def test_format(self):
1519+ self._check_arg('-f', 'format')
1520+
1521+ def test_jabberid(self):
1522+ self._check_arg('-j', 'jabberid')
1523+
1524+ def test_title(self):
1525+ self._check_arg('-t', 'title')
1526+
1527+ def test_permatag(self):
1528+ self._check_arg('-m', 'permatag')
1529+
1530+ def test_username(self):
1531+ self._check_arg('-u', 'username')
1532+
1533+ def test_password(self):
1534+ self._check_arg('-p', 'password')
1535+
1536+ def test_parentpid(self):
1537+ self._check_arg('-r', 'parentpid')
1538+
1539+ def test_filenames(self):
1540+ opts = pastebinit.getOpts(['-i', 'testa',
1541+ '-i', 'testb',
1542+ '-i', 'testc',
1543+ ])
1544+ assert opts['-i'] == [
1545+ 'testa','testb','testc'], "Failed to read filenames"
1546+
1547+
1548+if __name__ == "__main__":
1549+ unittest.main()
1550
1551=== modified file 'test.sh'
1552--- test.sh 2014-01-06 23:23:28 +0000
1553+++ test.sh 2014-12-07 21:54:13 +0000
1554@@ -1,11 +1,16 @@
1555 #!/bin/sh
1556-teststring="blah blah blah"
1557+#paste.debian.net needs 3 lines
1558+teststring="""\
1559+test from pastebinit
1560+test from pastebinit
1561+test from pastebinit
1562+"""
1563
1564 for interpreter in python python3; do
1565- for pastebin in $($interpreter pastebinit -l | egrep "^-" | sed "s/^- //g")
1566+ for pastebin in $(ls pastebin.d/*.conf | sed 's~^pastebin.d/\(.*\)\.conf$~\1~')
1567 do
1568 echo "Trying http://$pastebin ($interpreter)"
1569- URL=$(echo "$teststring\n$teststring\n$teststring" | $interpreter pastebinit -b http://$pastebin)
1570+ URL=$(echo teststring | $interpreter ./pastebinit.py -b http://$pastebin)
1571
1572 if [ "$pastebin" = "paste.ubuntu.org.cn" ]; then
1573 out=$(wget -O - -q "$URL" | gzip -d | grep "$teststring")
1574@@ -14,9 +19,13 @@
1575 fi
1576
1577 if [ -n "$out" ]; then
1578- echo "PASS: http://$pastebin ($URL) ($interpreter)"
1579+ echo "PASS: http://$pastebin"
1580+ echo " ($URL)"
1581+ echo " ($interpreter)"
1582 else
1583- echo "FAIL: http://$pastebin ($URL) ($interpreter)"
1584+ echo "FAIL: http://$pastebin"
1585+ echo " ($URL)"
1586+ echo " ($interpreter)"
1587 fi
1588 echo ""
1589 done

Subscribers

People subscribed via source and target branches

to all changes: