Skip to content

Instantly share code, notes, and snippets.

@nkmathew
Last active August 29, 2015 13:56
Show Gist options
  • Save nkmathew/9158212 to your computer and use it in GitHub Desktop.
Save nkmathew/9158212 to your computer and use it in GitHub Desktop.
#!/usr/bin/python2.7
## @Date: 21st February 2014, 16:00
"""
This code is supposed to help you paste to www.pastebin.com from the command
line as a Guest does in the web interface. The old pastebin
api(www.pastebin.com/api_public.php) has been deprecated in favor of the new
one(www.pastebin.com/api) which requires an api_key that can only be obtained by a
registered user.
pastebin uses a random unique key hidden in the html source as an input element like this:
<input name="post_key" value="rcbGxD7W" type="hidden">
Any attempt to modify the key or use a random one will land you in the spam
detection page.
This means that in order to paste to pastebin(as a Guest), you need to
download the page(http://pastebin.com) extract the `post_key` and then make
a POST request.
Usage:
=======
pastebin_paster.py [[<filename>] [language] [title] [paste-expiry]]
e.g
## A paste that expires in 1 day with a title
pastebin_paster.py hello-world.lisp newlisp "Lisp Hello World" 1D
## Pasting from <stdin>. The paste expires in 1 Month
type hello-world.lsp | pastebin_paster.py newLisp "newLISP hello world" 1m
All your pastes will be logged in a markdown file to be found in your home
directory.
## A example of pastebin post data
multipart/post-data - Values set in the forms
-------------------
post_key q75thHGY
submit_hidden submit_hidden
paste_format 6
paste_expire_date 1D
paste_private 0
paste_name Cracking Programs
Content-Disposition: form-data; name="post_key" FUHegAaw
Content-Disposition: form-data; name="submit_hidden" submit_hidden
Content-Disposition: form-data; name="paste_code" (display 'Hello-world)
Content-Disposition: form-data; name="paste_format" 29
Content-Disposition: form-data; name="paste_expire_date" 1D
Content-Disposition: form-data; name="paste_private" 0
Content-Disposition: form-data; name="paste_name" Lisp Hello world
Shortcomings:
+ You may experience problems uploading files with code page property encoding
because urrlib.urlencode only works with 'ascii' encoding.
+ The number of bytes used is not always(almost never) accurate. No matter
how small the file is, my net meter always indicates an increase by 100KB.
Not sure why it takes up that much.
+ It's not very fast AFAICT. It's sometimes faster to click the `new paste`
button and quickly paste what you want. There are times when it takes
30 seconds to paste(maybe it's because I use a dial up connection). The
fastest paste time I've recorded is 6 seconds.
"""
## Becomes `api_test_format` in POST
LANGUAGE_IDS = {
"1" : "None",
"8" : "Bash",
"9" : "C",
"14" : "C#",
"13" : "C++",
"16" : "CSS",
"25" : "HTML",
"196" : "HTML 5",
"27" : "Java",
"28" : "JavaScript",
"30" : "Lua",
"1" : "None",
"35" : "Objective C",
"40" : "Perl",
"41" : "PHP",
"42" : "Python",
"67" : "Rails",
"1" : "-------------",
"142" : "4CS",
"143" : "6502 ACME Cross Assembler",
"144" : "6502 Kick Assembler",
"145" : "6502 TASM64TASS",
"73" : "ABAP",
"2" : "ActionScript",
"74" : "ActionScript 3",
"3" : "Ada",
"147" : "ALGOL 68",
"4" : "Apache Log",
"5" : "AppleScript",
"75" : "APT Sources",
"217" : "ARM",
"6" : "ASM (NASM)",
"7" : "ASP",
"218" : "Asymptote",
"148" : "autoconf",
"149" : "Autohotkey",
"54" : "AutoIt",
"76" : "Avisynth",
"150" : "Awk",
"198" : "BASCOM AVR",
"8" : "Bash",
"77" : "Basic4GL",
"78" : "BibTeX",
"55" : "Blitz Basic",
"56" : "BNF",
"80" : "BOO",
"79" : "BrainFuck",
"9" : "C",
"10" : "C for Macs",
"82" : "C Intermediate Language",
"14" : "C#",
"13" : "C++",
"154" : "C++ (with QT extensions)",
"199" : "C:Loadrunner",
"11" : "CAD DCL",
"12" : "CAD Lisp",
"81" : "CFDG",
"152" : "ChaiScript",
"153" : "Clojure",
"99" : "Clone C",
"100" : "Clone C++",
"83" : "CMake",
"84" : "COBOL",
"200" : "CoffeeScript",
"15" : "ColdFusion",
"16" : "CSS",
"151" : "Cuesheet",
"17" : "D",
"219" : "DCL",
"220" : "DCPU-16",
"85" : "DCS",
"18" : "Delphi",
"177" : "Delphi Prism (Oxygene)",
"19" : "Diff",
"86" : "DIV",
"20" : "DOS",
"87" : "DOT",
"155" : "E",
"156" : "ECMAScript",
"21" : "Eiffel",
"88" : "Email",
"201" : "EPC",
"57" : "Erlang",
"158" : "F#",
"202" : "Falcon",
"89" : "FO Language",
"157" : "Formula One",
"22" : "Fortran",
"23" : "FreeBasic",
"206" : "FreeSWITCH",
"159" : "GAMBAS",
"24" : "Game Maker",
"160" : "GDB",
"58" : "Genero",
"161" : "Genie",
"90" : "GetText",
"162" : "Go",
"59" : "Groovy",
"163" : "GwBasic",
"60" : "Haskell",
"221" : "Haxe",
"164" : "HicEst",
"93" : "HQ9 Plus",
"25" : "HTML",
"196" : "HTML 5",
"165" : "Icon",
"94" : "IDL",
"26" : "INI file",
"61" : "Inno Script",
"95" : "INTERCAL",
"96" : "IO",
"166" : "J",
"27" : "Java",
"97" : "Java 5",
"28" : "JavaScript",
"167" : "jQuery",
"98" : "KiXtart",
"62" : "Latex",
"222" : "LDIF",
"168" : "Liberty BASIC",
"63" : "Linden Scripting",
"29" : "Lisp",
"203" : "LLVM",
"101" : "Loco Basic",
"169" : "Logtalk",
"102" : "LOL Code",
"103" : "Lotus Formulas",
"104" : "Lotus Script",
"105" : "LScript",
"30" : "Lua",
"65" : "M68000 Assembler",
"170" : "MagikSF",
"106" : "Make",
"171" : "MapBasic",
"64" : "MatLab",
"66" : "mIRC",
"172" : "MIX Assembler",
"173" : "Modula 2",
"107" : "Modula 3",
"146" : "Motorola 68000 HiSoft Dev",
"32" : "MPASM",
"108" : "MXML",
"33" : "MySQL",
"223" : "Nagios",
"174" : "newLISP",
"1" : "None",
"34" : "NullSoft Installer",
"109" : "Oberon 2",
"175" : "Objeck Programming Langua",
"35" : "Objective C",
"110" : "OCalm Brief",
"36" : "OCaml",
"224" : "Octave",
"181" : "OpenBSD PACKET FILTER",
"91" : "OpenGL Shading",
"37" : "Openoffice BASIC",
"111" : "Oracle 11",
"38" : "Oracle 8",
"178" : "Oz",
"225" : "ParaSail",
"226" : "PARIGP",
"39" : "Pascal",
"141" : "PAWN",
"179" : "PCRE",
"112" : "Per",
"40" : "Perl",
"180" : "Perl 6",
"41" : "PHP",
"113" : "PHP Brief",
"114" : "Pic 16",
"182" : "Pike",
"115" : "Pixel Bender",
"68" : "PLSQL",
"183" : "PostgreSQL",
"116" : "POV-Ray",
"117" : "Power Shell",
"184" : "PowerBuilder",
"197" : "ProFTPd",
"118" : "Progress",
"119" : "Prolog",
"120" : "Properties",
"121" : "ProvideX",
"185" : "PureBasic",
"204" : "PyCon",
"42" : "Python",
"227" : "Python for S60",
"186" : "qkdb+",
"43" : "QBasic",
"188" : "R",
"67" : "Rails",
"122" : "REBOL",
"123" : "REG",
"228" : "Rexx",
"44" : "Robots",
"187" : "RPM Spec",
"45" : "Ruby",
"92" : "Ruby Gnuplot",
"124" : "SAS",
"125" : "Scala",
"46" : "Scheme",
"126" : "Scilab",
"127" : "SdlBasic",
"69" : "Smalltalk",
"47" : "Smarty",
"229" : "SPARK",
"230" : "SPARQL",
"48" : "SQL",
"231" : "StoneScript",
"189" : "SystemVerilog",
"130" : "T-SQL",
"70" : "TCL",
"128" : "Tera Term",
"129" : "thinBasic",
"131" : "TypoScript",
"191" : "Unicon",
"195" : "UnrealScript",
"232" : "UPC",
"233" : "Urbi",
"192" : "Vala",
"51" : "VB.NET",
"234" : "Vedit",
"132" : "VeriLog",
"133" : "VHDL",
"134" : "VIM",
"135" : "Visual ProLog",
"50" : "VisualBasic",
"52" : "VisualFoxPro",
"136" : "WhiteSpace",
"137" : "WHOIS",
"138" : "Winbatch",
"193" : "XBasic",
"53" : "XML",
"139" : "Xorg Config",
"140" : "XPP",
"205" : "YAML",
"72" : "Z80 Assembler"
}
## Classes to please the gods of OOP
class Logger(object):
''' Keeps a list of actions performed and writes to a log file if the paste
was submitted successfully
Paste date: `Sat Feb 22 19:56:38 2014`
Paste language: `ASM (NASM)`
Paste expiry: `1 Hour` (**Sat Feb 22 20:56:50 2014**)
Paste source: [C:/mine/.asm/test.asm](file:///C:/mine/.asm/test.asm)
Paste title: `masm`
Paste post_key: `3GEyfgJW`
Paste size: **378 bytes**
Bytes used: **9378 bytes**
Paste URL: [[ASM (NASM)] masm - Pastebin.com](http://pastebin.com/t70bKgSZ)
Upload time: **11.6207 seconds**
------------------------------------------
'''
def __init__(self):
self.log_start = time.clock()
## The date is going to be used to calculate the date the paste expires
self.log_date = time.time()
self.events = ["Paste date:\t`%s` \n" % time.ctime()]
def write_log(self, expiry):
self.log_event("Upload time:\t**%.4f seconds**" % (time.clock() - self.log_start))
self.events[2] = self.events[2].strip()
self.events[2] += (" (**%s**) \n" % time.ctime(time.time() + PASTE_EXPIRE_DATE[expiry][1]))
home_folder = os.path.expanduser('~')
log_file_name = home_folder + os.sep + 'pastebin_pastes.md'
with open(log_file_name, "a+") as log_file:
self.log_event("\n------------------------------------------")
log_file.write(''.join(self.events))
def print_log(self):
sys.stderr.write(''.join(self.events) + "\n")
def log_event(self, event_string):
self.events.append("%s \n" % event_string)
class Options(object):
''' Process command line options
'''
def __init__(self, opt_list, logger):
## Setup default options
self.paste_lang_id = "1"
self.paste_title = "Untitled"
## Use <stdin> if no file is passed
self.paste_source = sys.stdin
self.paste_expiry = '1W'
self.cmd_options = opt_list
self.logger = logger
self.first_arg = self.cmd_options[1:2]
self.second_arg = self.cmd_options[2:3]
self.third_arg = self.cmd_options[3:4]
self.fourth_arg = self.cmd_options[4:5]
self.first_arg = self.first_arg[0] if self.first_arg else ""
self.second_arg = self.second_arg[0] if self.second_arg else ""
self.third_arg = self.third_arg[0] if self.third_arg else ""
self.fourth_arg = self.fourth_arg[0] if self.fourth_arg else ""
## Change the defaults according to the options passed
self.set_paste_source_and_title()
def set_paste_source_and_title(self):
'''
We simply check if the first argument is a valid filename. If it
isn't, the first argument becomes the title of the paste and the
paste source defaults to <stdin>.
Language -> Title -> Expiry-Date
'''
if os.path.exists(self.first_arg):
## Found a file
self.paste_source = codecs.open(self.first_arg, "rb", encoding = 'utf-8')
self.paste_title = self.third_arg if self.third_arg else self.paste_title
self.paste_expiry = self.fourth_arg if self.fourth_arg else self.paste_expiry
self.set_paste_lang_id(self.second_arg)
else:
## Found the title(paste source defaults to <stdin>
self.paste_title = self.first_arg if self.first_arg else self.paste_title
self.paste_expiry = self.second_arg if self.second_arg else self.paste_expiry
self.set_paste_lang_id(self.third_arg)
self.paste_expiry = self.paste_expiry.upper()
self.logger.log_event('Paste expiry:\t`%s`' %
PASTE_EXPIRE_DATE[self.paste_expiry][0])
source = ""
if self.paste_source == sys.stdin:
source = "&lt;stdin&gt;"
else:
fpath = os.path.abspath(self.paste_source.name)
source = "[%s](file:///%s)" % (fpath, fpath)
self.logger.log_event('Paste source:\t%s' % source.replace("\\","/"))
self.logger.log_event('Paste title:\t`%s`' % self.paste_title)
def set_paste_lang_id(self, lang):
temp = ""
if lang:
for lang_id in LANGUAGE_IDS:
## Turn the partial/full language name into a regex and match
## with the names in the dictionary.
lang_rx = re.compile(re.escape(str(lang)), re.I)
if lang == lang_id:
temp = lang_id
break
if lang_rx.search(LANGUAGE_IDS[lang_id]):
temp = lang_id
if temp:
self.paste_lang_id = temp
self.logger.log_event('Paste language:\t`%s`' % LANGUAGE_IDS[self.paste_lang_id])
class Paster(object):
def __init__(self, options, log):
self.options = options
## Used to extract the post_key
self.key_rx = re.compile(
'<input name="post_key" value="(.{8,})" type="hidden" />')
## Used to extract the title. The title keeps and indication of the
## success of the request.
self.title_rx = re.compile("<title>(.+)</title>")
self.url_rx = re.compile('<meta property="og:url" content="(.+)" />')
self.pastebin_home = "http://www.pastebin.com"
self.pastebin_post = "http://www.pastebin.com/post.php"
self.post_key = ""
self.logger = log
self.logger.print_log()
def obtain_paste_post_key(self):
## Download a portion of the `new paste` page and extract a post_key
## from that page.
request = urllib2.Request(self.pastebin_home)
response = urllib2.urlopen(request)
## Just read enough to get the post_key needed for posting the paste.
## Without it, you'll be redirected to the spam detection page.
content = response.read(BYTES_TO_READ_IN_NEWPASTE_PAGE)
post_key = self.key_rx.findall(content)
## A post_key string always exists
assert len(post_key) == 1, "Something went wrong when retrieving the post_key"
self.post_key = post_key[0]
self.logger.log_event("Paste post_key:\t`%s`" % self.post_key)
def send_paste(self):
## Build the paste string/code
paste_content = u''
for line in self.options.paste_source:
paste_content += line
content_length = len(paste_content)
self.logger.log_event("Paste size:\t**%ld bytes**" % content_length)
self.logger.log_event("Bytes used:\t**%ld bytes**" % (content_length +
BYTES_TO_READ_IN_PASTE_PAGE + BYTES_TO_READ_IN_NEWPASTE_PAGE))
post_data = urllib.urlencode(dict(
post_key = self.post_key,
## This key never changes
submit_hidden = 'submit_hidden',
paste_format = self.options.paste_lang_id,
paste_expire_date = self.options.paste_expiry,
## Always zero, unless you want it hidden which would beat the
## purpose of posting to a pastebin service.
paste_private = 0,
paste_name = self.options.paste_title,
paste_code = paste_content.encode('utf8')
))
request = urllib2.Request(self.pastebin_post, post_data)
response = urllib2.urlopen(request)
## Extract the title from the sent web page
paste_page_contents = response.read(BYTES_TO_READ_IN_PASTE_PAGE)
paste_title = self.title_rx.search(paste_page_contents)
paste_url = self.url_rx.search(paste_page_contents)
self.logger.log_event("Paste URL:\t[%s](%s)" %
(paste_title.groups()[0], paste_url.groups()[0]))
TEN_MINUTES = 60 * 10
ONE_HOUR = 60 * 60
ONE_DAY = ONE_HOUR * 24
ONE_WEEK = ONE_DAY * 7
ONE_MONTH = ONE_WEEK * 4
TWO_WEEKS = ONE_WEEK * 2
## An estimate for 'NEVER'
HUNDRED_YEARS = ONE_MONTH * 12 * 100
BYTES_TO_READ_IN_NEWPASTE_PAGE = 8000
BYTES_TO_READ_IN_PASTE_PAGE = 1000
## You can't know this beforehand
PASTE_EXPIRE_DATE = {'10M': ('10 Minutes', TEN_MINUTES),
'1H': ('1 Hour', ONE_HOUR),
'1W': ('1 Week', ONE_WEEK),
'2W': ('2 Weeks', TWO_WEEKS),
'1M': ('1 Month', ONE_MONTH),
'N': ('Never', HUNDRED_YEARS),
'1D': ('1 Day', ONE_DAY)}
import os
import re
import sys
import time
import urllib
import urllib2
import codecs
def main():
log = Logger()
options = Options(sys.argv, log)
paster = Paster(options, log)
if not DEBUG:
paster.obtain_paste_post_key()
paster.send_paste()
log.write_log(options.paste_expiry)
log.print_log()
return 0
DEBUG = 0
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment