Last active
August 29, 2015 13:56
-
-
Save nkmathew/9158212 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 = "<stdin>" | |
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