Newer
Older
# -*- coding: utf-8 -*-
# Copyright(C) 2016 Vincent A
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from base64 import b64decode, b64encode
from datetime import datetime
from zlib import decompress, MAX_WBITS, compressobj, DEFLATED
from weboob.browser.pages import HTMLPage
from weboob.browser.filters.standard import CleanText
from weboob.tools.json import json
from weboob.tools.compat import urljoin
from .crypto import decrypt, encrypt
class ReadPageZero(HTMLPage):
# for zerobin/privatebin
def _get_dict(self):
d = json.loads(CleanText('//div[@id="cipherdata"]')(self.doc))
if isinstance(d, list):
# zerobin
return d[0]
else:
# privatebin
return d
def decode_paste(self, key):
subd = json.loads(self._get_dict()['data'])
decr = decrypt(key, subd)
return decompress(b64decode(decr), -MAX_WBITS)
def get_expire(self):
d = self._get_dict()['meta']
if 'expire_date' in d:
return datetime.fromtimestamp(d['expire_date'])
def has_paste(self):
return bool(CleanText('//div[@id="cipherdata"]')(self.doc))
def fix_base64(s):
pad = {
2: '==',
3: '=',
}
return s + pad.get(len(s) % 4, '')
class ReadPage0(HTMLPage):
# for 0bin
def decode_paste(self, key):
d = json.loads(CleanText('//pre[@id="paste-content"]')(self.doc))
for k in ('iv', 'ct', 'salt'):
d[k] = fix_base64(d[k])
decr = decrypt(key, d)
# 0bin is supposed to use LZW but their js impl is such a piece of crap it doesn't compress anything
# this is easier for us though hehe
return b64decode(decr).decode('utf-8')
def has_paste(self):
return len(self.doc.xpath('//pre[@id="paste-content"]'))
class WritePageZero(HTMLPage):
AGES = {
300: '5min',
600: '10min',
3600: '1hour',
86400: '1day',
7 * 86400: '1week',
30 * 86400: '1month',
30.5 * 86400: '1month', # pastoob's definition of "1 month" is approximately okay
365 * 86400: '1year',
None: 'never',
0: 0,
}
def is_here(self):
if not self.doc.xpath('//select[@id="pasteExpiration"]'):
return False
return 'zerobin' in self.text or 'privatebin' in self.text
def post(self, contents, max_age):
compressor = compressobj(-1, DEFLATED, -MAX_WBITS)
contents = compressor.compress(contents.encode('utf-8'))
contents += compressor.flush()
password, d = encrypt(b64encode(contents))
data = {
'data': json.dumps(d),
'expire': self.AGES[max_age],
'burnafterreading': str(int(max_age is 0)),
'opendiscussion': str(int(self.browser.opendiscussion)),
'syntaxcoloring': '1',
}
headers = {
'Accept': 'application/json',
}
response = self.browser.location(self.url, data=data, headers=headers)
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
j = response.json()
assert j['status'] == 0
return '%s?%s#%s' % (self.url, j['id'], password)
class WritePage0(HTMLPage):
AGES = {
86400: '1_day',
30 * 86400: '1_month',
30.5 * 86400: '1_month', # pastoob's definition of "1 month" is approximately okay
None: 'never',
0: 'burn_after_reading',
}
is_here = '//form[contains(@action,"paste/create")]'
def post(self, contents, max_age):
form = self.get_form(xpath='//form[@class="well"]')
password, d = encrypt(b64encode(contents.encode('utf-8')))
form['content'] = json.dumps(d)
form['expiration'] = self.AGES[max_age]
j = form.submit().json()
assert j['status'] == 'ok'
return urljoin(urljoin(self.url, form.url), '%s#%s' % (j['paste'], password))