1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.tools.bugreport;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.openstreetmap.josm.spi.preferences.Config;
import org.openstreetmap.josm.tools.HttpClient;
import org.openstreetmap.josm.tools.HttpClient.Response;
import org.openstreetmap.josm.tools.Logging;
import org.openstreetmap.josm.tools.OpenBrowser;
import org.openstreetmap.josm.tools.Utils;
import org.openstreetmap.josm.tools.XmlUtils;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
/**
* This class handles sending the bug report to JOSM website.
* <p>
* Currently, we try to open a browser window for the user that displays the bug report.
*
* @author Michael Zangl
* @since 10055
*/
public class BugReportSender extends Thread {
/**
* Called during bug submission to JOSM bugtracker. Completes the bug report submission and handles errors.
* @since 12790
*/
public interface BugReportSendingHandler {
/**
* Called when a bug is sent to JOSM bugtracker.
* @param bugUrl URL to visit to effectively submit the bug report to JOSM website
* @param statusText the status text being sent
* @return <code>null</code> for success or a string in case of an error
*/
String sendingBugReport(String bugUrl, String statusText);
/**
* Called when a bug failed to be sent to JOSM bugtracker.
* @param errorMessage the error message
* @param statusText the status text being sent
*/
void failed(String errorMessage, String statusText);
}
/**
* The fallback bug report sending handler if none is set.
* @since 12790
*/
public static final BugReportSendingHandler FALLBACK_BUGREPORT_SENDING_HANDLER = new BugReportSendingHandler() {
@Override
public String sendingBugReport(String bugUrl, String statusText) {
return OpenBrowser.displayUrl(bugUrl);
}
@Override
public void failed(String errorMessage, String statusText) {
Logging.error("Unable to send bug report: {0}\n{1}", errorMessage, statusText);
}
};
private static volatile BugReportSendingHandler handler = FALLBACK_BUGREPORT_SENDING_HANDLER;
private final String statusText;
private String errorMessage;
/**
* Creates a new sender.
* @param statusText The status text to send.
*/
protected BugReportSender(String statusText) {
super("Bug report sender");
this.statusText = statusText;
}
@Override
public void run() {
try {
String bugUrl = "https://bugs.debian.org/";
// then notify handler
errorMessage = handler.sendingBugReport(bugUrl, statusText);
if (errorMessage != null) {
Logging.warn(errorMessage);
handler.failed(errorMessage, statusText);
}
} catch (Exception e) {
Logging.warn(e);
errorMessage = e.getMessage();
handler.failed(errorMessage, statusText);
}
}
/**
* Sends the debug text to the server.
* @return The token which was returned by the server. We need to pass this on to the ticket system.
* @throws BugReportSenderException if sending the report failed.
*/
private String pasteDebugText() throws BugReportSenderException {
try {
String text = Utils.strip(statusText);
String pdata = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
String postQuery = "pdata=" + Utils.encodeUrl(pdata);
HttpClient client = HttpClient.create(new URL(getJOSMTicketURL()), "POST")
.setHeader("Content-Type", "application/x-www-form-urlencoded")
.setRequestBody(postQuery.getBytes(StandardCharsets.UTF_8));
Response connection = client.connect();
if (connection.getResponseCode() >= 500) {
throw new BugReportSenderException("Internal server error.");
}
try (InputStream in = connection.getContent()) {
return retrieveDebugToken(XmlUtils.parseSafeDOM(in));
}
} catch (IOException | SAXException | ParserConfigurationException | XPathExpressionException t) {
throw new BugReportSenderException(t);
}
}
private static String getJOSMTicketURL() {
return "https://bugs.debian.org/";
}
private static String retrieveDebugToken(Document document) throws XPathExpressionException, BugReportSenderException {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
String status = (String) xpath.compile("/josmticket/@status").evaluate(document, XPathConstants.STRING);
if (!"ok".equals(status)) {
String message = (String) xpath.compile("/josmticket/error/text()").evaluate(document,
XPathConstants.STRING);
if (message.isEmpty()) {
message = "Error in server response but server did not tell us what happened.";
}
throw new BugReportSenderException(message);
}
String token = (String) xpath.compile("/josmticket/preparedid/text()")
.evaluate(document, XPathConstants.STRING);
if (token.isEmpty()) {
throw new BugReportSenderException("Server did not respond with a prepared id.");
}
return token;
}
/**
* Returns the error message that could have occurred during bug sending.
* @return the error message, or {@code null} if successful
*/
public final String getErrorMessage() {
return errorMessage;
}
private static class BugReportSenderException extends Exception {
BugReportSenderException(String message) {
super(message);
}
BugReportSenderException(Throwable cause) {
super(cause);
}
}
/**
* Opens the bug report window on the JOSM server.
* @param statusText The status text to send along to the server.
* @return bug report sender started thread
*/
public static BugReportSender reportBug(String statusText) {
BugReportSender sender = new BugReportSender(statusText);
sender.start();
return sender;
}
/**
* Sets the {@link BugReportSendingHandler} for bug report sender.
* @param bugReportSendingHandler the handler in charge of completing the bug report submission and handle errors. Must not be null
* @since 12790
*/
public static void setBugReportSendingHandler(BugReportSendingHandler bugReportSendingHandler) {
handler = Objects.requireNonNull(bugReportSendingHandler, "bugReportSendingHandler");
}
}
|