parent
aefa637bc5
commit
decfd02033
101
qutebrowser/network/pastebin.py
Normal file
101
qutebrowser/network/pastebin.py
Normal file
@ -0,0 +1,101 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Client for the pastebin."""
|
||||
|
||||
import functools
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
|
||||
from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkRequest,
|
||||
QNetworkReply)
|
||||
|
||||
|
||||
class PastebinClient(QObject):
|
||||
|
||||
"""A client for http://p.cmpl.cc/ using QNetworkAccessManager.
|
||||
|
||||
Attributes:
|
||||
_nam: The QNetworkAccessManager used.
|
||||
|
||||
Class attributes:
|
||||
API_URL: The base API URL.
|
||||
|
||||
Signals:
|
||||
success: Emitted when the paste succeeded.
|
||||
arg: The URL of the paste, as string.
|
||||
error: Emitted when the paste failed.
|
||||
arg: The error message, as string.
|
||||
"""
|
||||
|
||||
API_URL = 'http://paste.the-compiler.org/api/'
|
||||
success = pyqtSignal(str)
|
||||
error = pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._nam = QNetworkAccessManager(self)
|
||||
|
||||
def paste(self, name, title, text, parent=None):
|
||||
"""Paste the text into a pastebin and return the URL.
|
||||
|
||||
Args:
|
||||
name: The username to post as.
|
||||
title: The post title.
|
||||
text: The text to post.
|
||||
parent: The parent paste to reply to.
|
||||
"""
|
||||
data = {
|
||||
'text': text,
|
||||
'title': title,
|
||||
'name': name,
|
||||
}
|
||||
if parent is not None:
|
||||
data['reply'] = parent
|
||||
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
|
||||
create_url = urllib.parse.urljoin(self.API_URL, 'create')
|
||||
request = QNetworkRequest(QUrl(create_url))
|
||||
request.setHeader(QNetworkRequest.ContentTypeHeader,
|
||||
'application/x-www-form-urlencoded;charset=utf-8')
|
||||
reply = self._nam.post(request, encoded_data)
|
||||
if reply.isFinished():
|
||||
self.on_reply_finished(reply)
|
||||
else:
|
||||
reply.finished.connect(functools.partial(
|
||||
self.on_reply_finished, reply))
|
||||
|
||||
def on_reply_finished(self, reply):
|
||||
"""Read the data and finish when the reply finished.
|
||||
|
||||
Args:
|
||||
reply: The QNetworkReply which finished.
|
||||
"""
|
||||
if reply.error() != QNetworkReply.NoError:
|
||||
self.error.emit(reply.errorString())
|
||||
return
|
||||
try:
|
||||
url = bytes(reply.readAll()).decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
self.error.emit("Invalid UTF-8 data received in reply!")
|
||||
return
|
||||
if url.startswith('http://'):
|
||||
self.success.emit(url)
|
||||
else:
|
||||
self.error.emit("Invalid data received in reply!")
|
@ -24,8 +24,6 @@ import sys
|
||||
import enum
|
||||
import inspect
|
||||
import os.path
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import collections
|
||||
import functools
|
||||
import contextlib
|
||||
@ -85,36 +83,6 @@ def read_file(filename):
|
||||
return data.decode('UTF-8')
|
||||
|
||||
|
||||
def pastebin(name, title, text, parent=None):
|
||||
"""Paste the text into a pastebin and return the URL.
|
||||
|
||||
Args:
|
||||
name: The username to post as.
|
||||
title: The post title.
|
||||
text: The text to post.
|
||||
parent: The parent paste to reply to.
|
||||
"""
|
||||
api_url = 'http://paste.the-compiler.org/api/'
|
||||
data = {
|
||||
'text': text,
|
||||
'title': title,
|
||||
'name': name,
|
||||
}
|
||||
if parent is not None:
|
||||
data['reply'] = parent
|
||||
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
|
||||
create_url = urllib.parse.urljoin(api_url, 'create')
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
||||
}
|
||||
request = urllib.request.Request(create_url, encoded_data, headers)
|
||||
response = urllib.request.urlopen(request)
|
||||
url = response.read().decode('utf-8').rstrip()
|
||||
if not url.startswith('http'):
|
||||
raise ValueError("Got unexpected response: {}".format(url))
|
||||
return url
|
||||
|
||||
|
||||
def actute_warning():
|
||||
"""Display a warning about the dead_actute issue if needed."""
|
||||
# WORKAROUND (remove this when we bump the requirements to 5.3.0)
|
||||
|
@ -33,6 +33,7 @@ from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QPushButton,
|
||||
|
||||
from qutebrowser.utils import version, log, utils, objreg
|
||||
from qutebrowser.widgets.misc import DetailFold
|
||||
from qutebrowser.network import pastebin
|
||||
|
||||
|
||||
class _CrashDialog(QDialog):
|
||||
@ -50,6 +51,9 @@ class _CrashDialog(QDialog):
|
||||
_hbox: The QHboxLayout containing the buttons
|
||||
_url: Pastebin URL QLabel.
|
||||
_crash_info: A list of tuples with title and crash information.
|
||||
_paste_client: A PastebinClient instance to use.
|
||||
_paste_text: The text to pastebin.
|
||||
_resolution: Whether the dialog should be accepted on close.
|
||||
"""
|
||||
|
||||
NAME = None
|
||||
@ -68,9 +72,12 @@ class _CrashDialog(QDialog):
|
||||
self._hbox = None
|
||||
self._lbl = None
|
||||
self._chk_report = None
|
||||
self._resolution = None
|
||||
self._paste_text = None
|
||||
self.setWindowTitle("Whoops!")
|
||||
self.resize(QSize(640, 600))
|
||||
self._vbox = QVBoxLayout(self)
|
||||
self._paste_client = pastebin.PastebinClient(self)
|
||||
self._init_text()
|
||||
|
||||
info = QLabel("What were you doing when this crash/bug happened?")
|
||||
@ -179,20 +186,20 @@ class _CrashDialog(QDialog):
|
||||
lines.append(self._contact.toPlainText())
|
||||
lines.append("========== Debug log ==========")
|
||||
lines.append(self._debug_log.toPlainText())
|
||||
text = '\n\n'.join(lines)
|
||||
self._paste_text = '\n\n'.join(lines)
|
||||
try:
|
||||
user = getpass.getuser()
|
||||
except Exception as e:
|
||||
log.misc.exception("Error while getting user")
|
||||
user = 'unknown'
|
||||
try:
|
||||
utils.pastebin(user, "qutebrowser {}".format(self.NAME), text,
|
||||
parent='90286958') # http://p.cmpl.cc/90286958
|
||||
# parent: http://p.cmpl.cc/90286958
|
||||
self._paste_client.paste(user, "qutebrowser {}".format(self.NAME),
|
||||
self._paste_text, parent='90286958')
|
||||
except Exception as e:
|
||||
log.misc.exception("Error while paste-binning")
|
||||
exc_text = '{}: {}'.format(e.__class__.__name__, e)
|
||||
error_dlg = ReportErrorDialog(exc_text, text, self)
|
||||
error_dlg.exec_()
|
||||
self.show_error(exc_text)
|
||||
|
||||
@pyqtSlot()
|
||||
def on_button_clicked(self, button, accept):
|
||||
@ -200,18 +207,44 @@ class _CrashDialog(QDialog):
|
||||
button.setText("Reporting...")
|
||||
for btn in self._buttons:
|
||||
btn.setEnabled(False)
|
||||
self.hide()
|
||||
self.maybe_report()
|
||||
if accept:
|
||||
self._resolution = accept
|
||||
self._paste_client.success.connect(self.finish)
|
||||
self._paste_client.error.connect(self.show_error)
|
||||
reported = self.maybe_report()
|
||||
if not reported:
|
||||
self.finish()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def show_error(self, text):
|
||||
"""Show a paste error dialog.
|
||||
|
||||
Args:
|
||||
text: The paste text to show.
|
||||
"""
|
||||
error_dlg = ReportErrorDialog(text, self._paste_text, self)
|
||||
error_dlg.finished.connect(self.finish)
|
||||
error_dlg.show()
|
||||
|
||||
@pyqtSlot()
|
||||
def finish(self):
|
||||
"""Accept/reject the dialog when reporting is done."""
|
||||
if self._resolution:
|
||||
self.accept()
|
||||
else:
|
||||
self.reject()
|
||||
|
||||
@pyqtSlot()
|
||||
def maybe_report(self):
|
||||
"""Report the bug if the user allowed us to."""
|
||||
"""Report the bug if the user allowed us to.
|
||||
|
||||
Return:
|
||||
True if a report was done, False otherwise.
|
||||
"""
|
||||
if self._chk_report.isChecked():
|
||||
self.report()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class ExceptionCrashDialog(_CrashDialog):
|
||||
@ -371,8 +404,8 @@ class ReportDialog(_CrashDialog):
|
||||
def _init_buttons(self):
|
||||
super()._init_buttons()
|
||||
self._btn_report = QPushButton("Report", default=True)
|
||||
self._btn_report.clicked.connect(self.report)
|
||||
self._btn_report.clicked.connect(self.close)
|
||||
self._btn_report.clicked.connect(
|
||||
functools.partial(self.on_button_clicked, self._btn_report, True))
|
||||
self._hbox.addWidget(self._btn_report)
|
||||
|
||||
def _init_checkboxes(self, _debug):
|
||||
@ -400,6 +433,7 @@ class ReportDialog(_CrashDialog):
|
||||
report, which would be pretty useless without this info.
|
||||
"""
|
||||
self.report()
|
||||
return True
|
||||
|
||||
|
||||
class ReportErrorDialog(QDialog):
|
||||
|
Loading…
Reference in New Issue
Block a user