/******************************************************************************* * Copyright (c) 2004, 2015 Tasktop Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tasktop Technologies - initial API and implementation * Frank Becker - fixes for bug 165072 * Red Hat Inc. - fixes for bug 259291 *******************************************************************************/ package org.eclipse.mylyn.internal.bugzilla.core; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.Charset; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.swing.text.html.HTML.Tag; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.RedirectException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.PartBase; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.httpclient.util.DateParseException; import org.apache.commons.httpclient.util.DateUtil; import org.apache.xmlrpc.XmlRpcException; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.mylyn.commons.core.HtmlStreamTokenizer; import org.eclipse.mylyn.commons.core.HtmlStreamTokenizer.Token; import org.eclipse.mylyn.commons.core.HtmlTag; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.commons.net.AbstractWebLocation; import org.eclipse.mylyn.commons.net.AuthenticationCredentials; import org.eclipse.mylyn.commons.net.AuthenticationType; import org.eclipse.mylyn.commons.net.Policy; import org.eclipse.mylyn.commons.net.WebLocation; import org.eclipse.mylyn.commons.net.WebUtil; import org.eclipse.mylyn.internal.bugzilla.core.IBugzillaConstants.BUGZILLA_REPORT_STATUS_4_0; import org.eclipse.mylyn.internal.bugzilla.core.service.BugzillaXmlRpcClient; import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryLocation; import org.eclipse.mylyn.tasks.core.IRepositoryQuery; import org.eclipse.mylyn.tasks.core.RepositoryResponse; import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind; import org.eclipse.mylyn.tasks.core.RepositoryStatus; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentSource; import org.eclipse.mylyn.tasks.core.data.TaskAttachmentMapper; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper; import org.eclipse.mylyn.tasks.core.data.TaskData; import org.eclipse.mylyn.tasks.core.data.TaskDataCollector; /** * @author Mik Kersten * @author Rob Elves * @author Steffen Pingel * @author Frank Becker */ public class BugzillaClient { private static final String INPUT_TYPE_HIDDEN_NAME_BUGZILLA_LOGIN_TOKEN = "<input type=\"hidden\" name=\"Bugzilla_login_token"; //$NON-NLS-1$ private static final String UNKNOWN_REPOSITORY_ERROR = "An unknown repository error has occurred: "; //$NON-NLS-1$ private static final String COOKIE_BUGZILLA_LOGIN = "Bugzilla_login"; //$NON-NLS-1$ protected static final String USER_AGENT = "BugzillaConnector"; //$NON-NLS-1$ public static final int MAX_RETRIEVED_PER_QUERY = 50; private static final String QUERY_DELIMITER = "?"; //$NON-NLS-1$ private static final String KEY_ID = "id"; //$NON-NLS-1$ private static final String VAL_TRUE = "true"; //$NON-NLS-1$ private static final String KEY_CC = "cc"; //$NON-NLS-1$ private static final String POST_BUG_CGI = "/post_bug.cgi"; //$NON-NLS-1$ private static final String ENTER_BUG_PRODUCT_CGI = "/enter_bug.cgi?product="; //$NON-NLS-1$ private static final String ENTER_ATTACHMENT_CGI = "/attachment.cgi?action=enter&bugid="; //$NON-NLS-1$ private static final String PROCESS_BUG_CGI = "/process_bug.cgi"; //$NON-NLS-1$ private static final String PROCESS_ATTACHMENT_CGI = "/attachment.cgi"; //$NON-NLS-1$ public static final int WRAP_LENGTH = 80; private static final String VAL_PROCESS_BUG = "process_bug"; //$NON-NLS-1$ private static final String KEY_FORM_NAME = "form_name"; //$NON-NLS-1$ private static final String VAL_NONE = "none"; //$NON-NLS-1$ private static final String KEY_KNOB = "knob"; //$NON-NLS-1$ // TODO change to BugzillaReportElement.ADD_COMMENT private static final String KEY_COMMENT = "comment"; //$NON-NLS-1$ private static final String KEY_SHORT_DESC = "short_desc"; //$NON-NLS-1$ private static final String VALUE_CONTENTTYPEMETHOD_MANUAL = "manual"; //$NON-NLS-1$ private static final String VALUE_ISPATCH = "1"; //$NON-NLS-1$ private static final String VALUE_ACTION_INSERT = "insert"; //$NON-NLS-1$ private static final String ATTRIBUTE_CONTENTTYPEENTRY = "contenttypeentry"; //$NON-NLS-1$ private static final String ATTRIBUTE_CONTENTTYPEMETHOD = "contenttypemethod"; //$NON-NLS-1$ private static final String ATTRIBUTE_ISPATCH = "ispatch"; //$NON-NLS-1$ private static final String CONTENT_TYPE_APP_RDF_XML = "application/rdf+xml"; //$NON-NLS-1$ private static final String CONTENT_TYPE_APP_XML = "application/xml"; //$NON-NLS-1$ private static final String CONTENT_TYPE_TEXT_XML = "text/xml"; //$NON-NLS-1$ private static final String[] VALID_CONFIG_CONTENT_TYPES = { CONTENT_TYPE_APP_RDF_XML, CONTENT_TYPE_APP_XML, CONTENT_TYPE_TEXT_XML }; private static final String ATTR_CHARSET = "charset"; //$NON-NLS-1$ protected Proxy proxy = Proxy.NO_PROXY; protected URL repositoryUrl; protected String characterEncoding; private boolean loggedIn; private final Map<String, String> configParameters; private final HttpClient httpClient = new HttpClient(WebUtil.getConnectionManager()); private boolean lastModifiedSupported = true; private final BugzillaLanguageSettings bugzillaLanguageSettings; private RepositoryConfiguration repositoryConfiguration; private HostConfiguration hostConfiguration; private final AbstractWebLocation location; private final BugzillaRepositoryConnector connector; private BugzillaXmlRpcClient xmlRpcClient = null; public BugzillaClient(AbstractWebLocation location, String characterEncoding, Map<String, String> configParameters, BugzillaLanguageSettings languageSettings, BugzillaRepositoryConnector connector) throws MalformedURLException { this.repositoryUrl = new URL(location.getUrl()); this.location = location; this.characterEncoding = characterEncoding; this.configParameters = configParameters; this.bugzillaLanguageSettings = languageSettings; this.connector = connector; this.proxy = location.getProxyForHost(location.getUrl(), IProxyData.HTTP_PROXY_TYPE); WebUtil.configureHttpClient(httpClient, USER_AGENT); } public BugzillaClient(AbstractWebLocation location, TaskRepository taskRepository, BugzillaRepositoryConnector connector) throws MalformedURLException { this(location, taskRepository.getCharacterEncoding(), taskRepository.getProperties(), getLanguageSettings(taskRepository), connector); } private static BugzillaLanguageSettings getLanguageSettings(TaskRepository taskRepository) { String language = taskRepository.getProperty(IBugzillaConstants.BUGZILLA_LANGUAGE_SETTING); if (language == null || language.equals("")) { //$NON-NLS-1$ language = IBugzillaConstants.DEFAULT_LANG; } return BugzillaRepositoryConnector.getLanguageSetting(language); } public void validate(IProgressMonitor monitor) throws IOException, CoreException { monitor = Policy.monitorFor(monitor); GzipGetMethod method = null; try { logout(monitor); method = getConnect(repositoryUrl + "/", monitor); //$NON-NLS-1$ } finally { if (method != null) { WebUtil.releaseConnection(method, monitor); } } CustomTransitionManager validTransitions = new CustomTransitionManager(); String transitionsFileName = configParameters.get(IBugzillaConstants.BUGZILLA_DESCRIPTOR_FILE); if (transitionsFileName != null && !transitionsFileName.equals("")) { //$NON-NLS-1$ if (!validTransitions.parse(transitionsFileName)) { throw new CoreException( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "Invalide Transition File Content")); //$NON-NLS-1$ } } if (Boolean.parseBoolean(configParameters.get(IBugzillaConstants.BUGZILLA_USE_XMLRPC))) { getXmlRpcClient(); int uID = -1; try { uID = xmlRpcClient.login(monitor); if (uID == -1) { throw new CoreException( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "XMLRPC user could not login")); //$NON-NLS-1$ } } catch (XmlRpcException e) { throw new CoreException( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "XMLRPC is not installed")); //$NON-NLS-1$ } } } protected boolean hasAuthenticationCredentials() { AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY); return (credentials != null && credentials.getUserName() != null && credentials.getUserName().length() > 0); } protected boolean hasHTTPAuthenticationCredentials() { AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.HTTP); return (credentials != null && credentials.getUserName() != null && credentials.getUserName().length() > 0); } private GzipGetMethod getConnect(String serverURL, IProgressMonitor monitor) throws IOException, CoreException { return connectInternal(serverURL, false, monitor, null); } protected GzipGetMethod getConnectGzip(String serverURL, IProgressMonitor monitor) throws IOException, CoreException { return getConnectGzip(serverURL, monitor, null); } /** * in order to provide an even better solution for bug 196056 the size of the bugzilla configuration downloaded must * be reduced. By using a cached version of the config.cgi this can reduce traffic considerably: * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.phoenix/infra-scripts/bugzilla/?root=Technology_Project * * @param serverURL * @return a GetMethod with possibly gzip encoded response body, so caller MUST check with * "gzip".equals(method.getResponseHeader("Content-encoding") or use the utility method * getResponseBodyAsUnzippedStream(). * @throws IOException * @throws CoreException */ protected GzipGetMethod getConnectGzip(String serverURL, IProgressMonitor monitor, String eTagValue) throws IOException, CoreException { return connectInternal(serverURL, true, monitor, eTagValue); } private GzipGetMethod connectInternal(String requestURL, boolean gzip, IProgressMonitor monitor, String eTagValue) throws IOException, CoreException { monitor = Policy.monitorFor(monitor); hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); for (int attempt = 0; attempt < 2; attempt++) { // force authentication authenticate(monitor); GzipGetMethod getMethod = new GzipGetMethod(WebUtil.getRequestPath(requestURL), gzip); if (requestURL.contains(QUERY_DELIMITER)) { getMethod.setQueryString(requestURL.substring(requestURL.indexOf(QUERY_DELIMITER))); } getMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=" //$NON-NLS-1$ //$NON-NLS-2$ + getCharacterEncoding()); if (eTagValue != null && eTagValue.compareTo("") != 0) { //$NON-NLS-1$ getMethod.setRequestHeader("If-None-Match", eTagValue); //$NON-NLS-1$ } // Resolves bug#195113 httpClient.getParams().setParameter("http.protocol.single-cookie-header", true); //$NON-NLS-1$ // WARNING!! Setting browser compatibility breaks Bugzilla // authentication // getMethod.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); // getMethod.getParams().setCookiePolicy(CookiePolicy.RFC_2109); getMethod.setDoAuthentication(true); int code; try { code = WebUtil.execute(httpClient, hostConfiguration, getMethod, monitor); } catch (IOException e) { WebUtil.releaseConnection(getMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, repositoryUrl.toString(), e)); } switch (code) { case HttpURLConnection.HTTP_OK: return getMethod; case HttpURLConnection.HTTP_NOT_MODIFIED: WebUtil.releaseConnection(getMethod, monitor); throw new CoreException(new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "Not changed")); //$NON-NLS-1$ case HttpURLConnection.HTTP_UNAUTHORIZED: case HttpURLConnection.HTTP_FORBIDDEN: // login or reauthenticate due to an expired session loggedIn = false; WebUtil.releaseConnection(getMethod, monitor); authenticate(monitor); break; case HttpURLConnection.HTTP_PROXY_AUTH: loggedIn = false; WebUtil.releaseConnection(getMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "Proxy authentication required")); //$NON-NLS-1$ case HttpURLConnection.HTTP_INTERNAL_ERROR: loggedIn = false; InputStream stream = getResponseStream(getMethod, monitor); ByteArrayOutputStream ou = new ByteArrayOutputStream(1024); transferData(stream, ou); WebUtil.releaseConnection(getMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, repositoryUrl.toString(), "Error = 500")); //$NON-NLS-1$ default: WebUtil.releaseConnection(getMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, "Http error: " + HttpStatus.getStatusText(code))); //$NON-NLS-1$ } } throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "All connection attempts to " + repositoryUrl.toString() //$NON-NLS-1$ + " failed. Please verify connection and authentication information.")); //$NON-NLS-1$ } public void logout(IProgressMonitor monitor) throws IOException, CoreException { monitor = Policy.monitorFor(monitor); String loginUrl = repositoryUrl + "/relogin.cgi"; //$NON-NLS-1$ GzipGetMethod method = null; try { method = getConnect(loginUrl, monitor); loggedIn = false; httpClient.getState().clearCookies(); } finally { if (method != null) { WebUtil.releaseConnection(method, monitor); } } } protected InputStream getResponseStream(HttpMethodBase method, IProgressMonitor monitor) throws IOException { InputStream in = WebUtil.getResponseBodyAsStream(method, monitor); if (isZippedReply(method)) { in = new java.util.zip.GZIPInputStream(in); } return in; } private boolean isZippedReply(HttpMethodBase method) { // content-encoding:gzip can be set by a dedicated perl script or // mod_gzip boolean zipped = (null != method.getResponseHeader("Content-encoding") && method.getResponseHeader( //$NON-NLS-1$ "Content-encoding").getValue().equals(IBugzillaConstants.CONTENT_ENCODING_GZIP)) //$NON-NLS-1$ || // content-type: application/x-gzip can be set by any apache // after 302 redirect, based on .gz suffix (null != method.getResponseHeader("Content-Type") && method.getResponseHeader("Content-Type") //$NON-NLS-1$ //$NON-NLS-2$ .getValue() .equals("application/x-gzip")); //$NON-NLS-1$ return zipped; } private static String getStringFromInputStream(InputStream is) throws IOException { BufferedReader br = null; StringBuilder sb = new StringBuilder(); String line; try { br = new BufferedReader(new InputStreamReader(is)); while ((line = br.readLine()) != null) { sb.append(line); sb.append("\n"); //$NON-NLS-1$ } } finally { if (br != null) { br.close(); } } return sb.toString(); } private String getBugzillaLoginTokenIfExists(IProgressMonitor monitor) throws CoreException { String loginToken = null; GzipPostMethod getMethod = new GzipPostMethod(WebUtil.getRequestPath(repositoryUrl.toString()) + "/index.cgi", //$NON-NLS-1$ true); try { getMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=" //$NON-NLS-1$ //$NON-NLS-2$ + getCharacterEncoding()); getMethod.setDoAuthentication(true); getMethod.setFollowRedirects(false); httpClient.getState().clearCookies(); // for Bugzilla > 4.4.2 but not 4.5, 4.5.1 or 4.5.2 we need first // the Bugzilla_login_request_cookie int code = WebUtil.execute(httpClient, hostConfiguration, getMethod, monitor); WebUtil.releaseConnection(getMethod, monitor); if (code != HttpURLConnection.HTTP_OK) { loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, "Http error: " + HttpStatus.getStatusText(code))); //$NON-NLS-1$ } // for Bugzilla > 4.4.2 but not 4.5, 4.5.1 or 4.5.2 we now do the // real authentication code = WebUtil.execute(httpClient, hostConfiguration, getMethod, monitor); if (code != HttpURLConnection.HTTP_OK) { loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, "Http error: " + HttpStatus.getStatusText(code))); //$NON-NLS-1$ } InputStream is = getResponseStream(getMethod, monitor); String result = getStringFromInputStream(is); if (result.lastIndexOf(INPUT_TYPE_HIDDEN_NAME_BUGZILLA_LOGIN_TOKEN) != -1) { int index = result.lastIndexOf(INPUT_TYPE_HIDDEN_NAME_BUGZILLA_LOGIN_TOKEN); String loginTokenAndRest = result.substring(index); int valueStart = loginTokenAndRest.indexOf("value=\"") + 7; //$NON-NLS-1$ int valueEnd = loginTokenAndRest.indexOf("\">"); //$NON-NLS-1$ loginToken = loginTokenAndRest.substring(valueStart, valueEnd); } } catch (IOException e) { throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, repositoryUrl.toString(), e)); } finally { WebUtil.releaseConnection(getMethod, monitor); } return loginToken; } public void authenticate(IProgressMonitor monitor) throws CoreException { if (loggedIn || (!hasAuthenticationCredentials() && !hasHTTPAuthenticationCredentials())) { return; } monitor = Policy.monitorFor(monitor); GzipPostMethod postMethod = null; try { hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); NameValuePair[] formData; String loginToken = getBugzillaLoginTokenIfExists(monitor); if (loginToken != null) { formData = new NameValuePair[3]; formData[2] = new NameValuePair("Bugzilla_login_token", loginToken); //$NON-NLS-1$ } else { formData = new NameValuePair[2]; } AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY); AuthenticationCredentials httpAuthCredentials = location.getCredentials(AuthenticationType.HTTP); if (credentials == null && httpAuthCredentials == null) { loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "Authentication credentials from location missing.")); //$NON-NLS-1$ } if (credentials != null) { String password = credentials.getPassword(); if ("".equals(password) && !hasHTTPAuthenticationCredentials()) { //$NON-NLS-1$ loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_EMPTY_PASSWORD, repositoryUrl.toString(), "Empty password not allowed for Authentication credentials.")); //$NON-NLS-1$ } formData[0] = new NameValuePair(IBugzillaConstants.POST_INPUT_BUGZILLA_LOGIN, credentials.getUserName()); formData[1] = new NameValuePair(IBugzillaConstants.POST_INPUT_BUGZILLA_PASSWORD, credentials.getPassword()); } postMethod = new GzipPostMethod( WebUtil.getRequestPath(repositoryUrl.toString() + IBugzillaConstants.URL_POST_LOGIN), true); postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=" //$NON-NLS-1$ //$NON-NLS-2$ + getCharacterEncoding()); if (credentials != null) { postMethod.setRequestBody(formData); } postMethod.setDoAuthentication(true); postMethod.setFollowRedirects(false); if (httpAuthCredentials != null && httpAuthCredentials.getUserName() != null && httpAuthCredentials.getUserName().length() > 0) { httpClient.getParams().setAuthenticationPreemptive(true); } int code = WebUtil.execute(httpClient, hostConfiguration, postMethod, monitor); if (code == HttpURLConnection.HTTP_UNAUTHORIZED || code == HttpURLConnection.HTTP_FORBIDDEN) { loggedIn = false; WebUtil.releaseConnection(postMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "HTTP authentication failed.")); //$NON-NLS-1$ } else if (code == HttpURLConnection.HTTP_PROXY_AUTH) { loggedIn = false; WebUtil.releaseConnection(postMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "Proxy authentication required")); //$NON-NLS-1$ } else if (code != HttpURLConnection.HTTP_OK) { loggedIn = false; WebUtil.releaseConnection(postMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, "Http error: " + HttpStatus.getStatusText(code))); //$NON-NLS-1$ } if (httpAuthCredentials != null && httpAuthCredentials.getUserName() != null && httpAuthCredentials.getUserName().length() > 0) { // If httpAuthCredentials are used // HttpURLConnection.HTTP_UNAUTHORIZED when the credentials are // invalide so we // not need to test the cookies. // see bug 305267 or // https://bugzilla.mozilla.org/show_bug.cgi?id=385606 loggedIn = true; InputStream inputStream = getResponseStream(postMethod, monitor); try { BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding())); try { String errorMessage = extractErrorMessage(in); if (errorMessage != null) { loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), errorMessage)); } } finally { inputStream.close(); } } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else if (hasAuthenticationCredentials()) { for (Cookie cookie : httpClient.getState().getCookies()) { if (cookie.getName().equals(COOKIE_BUGZILLA_LOGIN)) { loggedIn = true; break; } } if (!loggedIn) { InputStream input = getResponseStream(postMethod, monitor); try { throw new CoreException(parseHtmlError(input)); } finally { input.close(); } } } else { // anonymous login loggedIn = true; } } catch (IOException e) { throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, repositoryUrl.toString(), e)); } finally { if (postMethod != null) { WebUtil.releaseConnection(postMethod, monitor); } httpClient.getParams().setAuthenticationPreemptive(false); } } private String extractErrorMessage(Reader responseContent) throws IOException, ParseException { HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer(responseContent, null); for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { if (isErrorMessageToken(token)) { return computeErrorMessage(tokenizer, token); } } return null; } private static String computeErrorMessage(HtmlStreamTokenizer tokenizer, Token token) throws IOException, ParseException { int tagDepth = 0; String errorMessage = ""; //$NON-NLS-1$ for (token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { if (token.getType() == Token.TAG) { HtmlTag htmlTag = (HtmlTag) token.getValue(); if (htmlTag.isEndTag()) { --tagDepth; if (tagDepth < 0) { break; } } else { ++tagDepth; } } else { errorMessage += token.toString(); } } errorMessage = errorMessage.replaceAll("\\s+", " "); //$NON-NLS-1$//$NON-NLS-2$ return errorMessage; } private boolean isErrorMessageToken(Token token) { if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.TD && !((HtmlTag) (token.getValue())).isEndTag()) { HtmlTag ta = ((HtmlTag) token.getValue()); String st = ta.getAttribute("id"); //$NON-NLS-1$ if (st != null && st.equals("error_msg")) { //$NON-NLS-1$ return true; } } return false; } @SuppressWarnings("deprecation") public boolean getSearchHits(IRepositoryQuery query, TaskDataCollector collector, TaskAttributeMapper mapper, IProgressMonitor monitor) throws IOException, CoreException { HttpMethodBase postMethod = null; try { authenticate(new SubProgressMonitor(monitor, 1)); String queryUrl = query.getUrl(); int start = queryUrl.indexOf('?'); List<NameValuePair> pairs = new ArrayList<NameValuePair>(); if (start != -1) { queryUrl = queryUrl.substring(start + 1); String[] result = queryUrl.split("&"); //$NON-NLS-1$ if (result.length > 0) { for (String string : result) { String[] nameValue = string.split("="); //$NON-NLS-1$ if (nameValue.length == 1) { pairs.add(new NameValuePair(nameValue[0].trim(), "")); //$NON-NLS-1$ } else if (nameValue.length == 2 && nameValue[0] != null && nameValue[1] != null) { // Hack around bugzilla's change of attribute name // for comment search field bug#289155 if (nameValue[0].startsWith("long_desc")) { //$NON-NLS-1$ pairs.add(new NameValuePair(nameValue[0].replace("long_desc", "longdesc"), //$NON-NLS-1$ //$NON-NLS-2$ URLDecoder.decode(nameValue[1].trim(), getCharacterEncoding()))); } pairs.add(new NameValuePair(nameValue[0].trim(), URLDecoder.decode(nameValue[1].trim(), getCharacterEncoding()))); } } } } NameValuePair ctypePair = new NameValuePair("ctype", "rdf"); //$NON-NLS-1$ //$NON-NLS-2$ // Test that we don't specify content type twice. if (!pairs.contains(ctypePair)) { pairs.add(ctypePair); } try { postMethod = postFormData(IBugzillaConstants.URL_BUGLIST, pairs.toArray(new NameValuePair[pairs.size()]), monitor); } catch (RedirectException r) { // Handle one redirect (Bugzilla 3.4 provides a redirect upon // query submission via post) postMethod = getConnectGzip(r.getMessage(), monitor, null); } if (postMethod != null && postMethod.getResponseHeader("Content-Type") != null) { //$NON-NLS-1$ Header responseTypeHeader = postMethod.getResponseHeader("Content-Type"); //$NON-NLS-1$ for (String type : VALID_CONFIG_CONTENT_TYPES) { if (responseTypeHeader.getValue().toLowerCase(Locale.ENGLISH).contains(type)) { InputStream stream = getResponseStream(postMethod, monitor); try { RepositoryQueryResultsFactory queryFactory = getQueryResultsFactory(stream); int count = queryFactory.performQuery(repositoryUrl.toString(), collector, mapper); return count > 0; } finally { stream.close(); } } } } // because html is not a valid config content type it is save to get // the response here throw new CoreException(parseHtmlError(getResponseStream(postMethod, monitor))); } finally { if (postMethod != null) { WebUtil.releaseConnection(postMethod, monitor); } } } protected RepositoryQueryResultsFactory getQueryResultsFactory(InputStream stream) { return new RepositoryQueryResultsFactory(stream, getCharacterEncoding()); } public void setupExistingBugAttributes(String serverUrl, TaskData existingReport) { // ordered list of elements as they appear in UI // and additional elements that may not appear in the incoming xml // stream but need to be present for bug submission / not always dirty // state handling BugzillaAttribute[] reportElements1 = { BugzillaAttribute.SHORT_DESC, BugzillaAttribute.BUG_STATUS, BugzillaAttribute.RESOLUTION, BugzillaAttribute.BUG_ID, BugzillaAttribute.REP_PLATFORM, BugzillaAttribute.PRODUCT, BugzillaAttribute.OP_SYS, BugzillaAttribute.COMPONENT, BugzillaAttribute.VERSION, BugzillaAttribute.PRIORITY, BugzillaAttribute.BUG_SEVERITY, BugzillaAttribute.ASSIGNED_TO }; BugzillaAttribute[] reportElements2 = { BugzillaAttribute.REPORTER, BugzillaAttribute.DEPENDSON, BugzillaAttribute.BLOCKED, BugzillaAttribute.BUG_FILE_LOC, BugzillaAttribute.NEWCC, BugzillaAttribute.KEYWORDS, BugzillaAttribute.CC, BugzillaAttribute.NEW_COMMENT }; TaskRepository taskRepository = existingReport.getAttributeMapper().getTaskRepository(); for (BugzillaAttribute element : reportElements1) { BugzillaTaskDataHandler.createAttribute(existingReport, element); } BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.TARGET_MILESTONE, IBugzillaConstants.BUGZILLA_PARAM_USETARGETMILESTONE, taskRepository, existingReport, true); for (BugzillaAttribute element : reportElements2) { BugzillaTaskDataHandler.createAttribute(existingReport, element); } BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.QA_CONTACT, IBugzillaConstants.BUGZILLA_PARAM_USEQACONTACT, taskRepository, existingReport, true); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.STATUS_WHITEBOARD, IBugzillaConstants.BUGZILLA_PARAM_USESTATUSWHITEBOARD, taskRepository, existingReport, true); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.ALIAS, IBugzillaConstants.BUGZILLA_PARAM_USEBUGALIASES, taskRepository, existingReport, false); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.CLASSIFICATION, IBugzillaConstants.BUGZILLA_PARAM_USECLASSIFICATION, taskRepository, existingReport, false); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.SEE_ALSO, IBugzillaConstants.BUGZILLA_PARAM_USE_SEE_ALSO, taskRepository, existingReport, false); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.REMOVE_SEE_ALSO, IBugzillaConstants.BUGZILLA_PARAM_USE_SEE_ALSO, taskRepository, existingReport, false); BugzillaUtil.addAttributeIfUsed(BugzillaAttribute.SEE_ALSO_READ, IBugzillaConstants.BUGZILLA_PARAM_USE_SEE_ALSO, taskRepository, existingReport, false); if (repositoryConfiguration == null) { repositoryConfiguration = connector.getRepositoryConfiguration(serverUrl); } if (repositoryConfiguration != null) { for (BugzillaCustomField bugzillaCustomField : repositoryConfiguration.getCustomFields()) { existingReport.getRoot().createAttribute(bugzillaCustomField.getName()); } } } public static String getBugUrlWithoutLogin(String repositoryUrl, String id) { String url = repositoryUrl + IBugzillaConstants.URL_GET_SHOW_BUG + id; return url; } public static String getCharsetFromString(String string) { int charsetStartIndex = string.indexOf(ATTR_CHARSET); if (charsetStartIndex != -1) { int charsetEndIndex = string.indexOf("\"", charsetStartIndex); // TODO: //$NON-NLS-1$ // could // be // space // after? if (charsetEndIndex == -1) { charsetEndIndex = string.length(); } String charsetString = string.substring(charsetStartIndex + 8, charsetEndIndex); if (Charset.availableCharsets().containsKey(charsetString)) { return charsetString; } } return null; } @Deprecated public RepositoryConfiguration getRepositoryConfiguration(IProgressMonitor monitor) throws IOException, CoreException { return getRepositoryConfiguration(monitor, null); } public RepositoryConfiguration getRepositoryConfiguration(IProgressMonitor monitor, String eTagValue) throws IOException, CoreException { GzipGetMethod method = null; int attempt = 0; while (attempt < 2) { try { method = getConnectGzip(repositoryUrl + IBugzillaConstants.URL_GET_CONFIG_RDF, monitor, eTagValue); // provide a solution for bug 196056 by allowing a (cached) // gzipped configuration to be sent // modified to also accept "application/x-gzip" as results from // a 302 redirect to a previously gzipped file. if (method == null) { throw new IOException("Could not retrieve configuratoin. HttpClient return null method."); //$NON-NLS-1$ } InputStream stream = getResponseStream(method, monitor); try { if (method.getResponseHeader("Content-Type") != null) { //$NON-NLS-1$ Header responseTypeHeader = method.getResponseHeader("Content-Type"); //$NON-NLS-1$ for (String type : VALID_CONFIG_CONTENT_TYPES) { if (responseTypeHeader.getValue().toLowerCase(Locale.ENGLISH).contains(type)) { RepositoryConfigurationFactory configFactory = new RepositoryConfigurationFactory( stream, getCharacterEncoding()); repositoryConfiguration = configFactory.getConfiguration(); Header eTag = method.getResponseHeader("ETag"); //$NON-NLS-1$ if (eTag != null) { repositoryConfiguration.setETagValue(eTag.getValue()); } else { repositoryConfiguration.setETagValue(null); } Header lastModifiedHeader = method.getResponseHeader("Last-Modified"); //$NON-NLS-1$ if (lastModifiedHeader != null) { try { repositoryConfiguration.setLastModifiedHeader( DateUtil.parseDate(lastModifiedHeader.getValue())); } catch (DateParseException e) { repositoryConfiguration.setLastModifiedHeader((Date) null); } } else { repositoryConfiguration.setLastModifiedHeader((Date) null); } if (repositoryConfiguration != null) { getXmlRpcClient(); if (xmlRpcClient != null) { xmlRpcClient.updateConfiguration(monitor, repositoryConfiguration, configParameters.get(IBugzillaConstants.BUGZILLA_DESCRIPTOR_FILE)); } else { repositoryConfiguration.setValidTransitions(monitor, configParameters.get(IBugzillaConstants.BUGZILLA_DESCRIPTOR_FILE), null); } if (!repositoryConfiguration.getOptionValues(BugzillaAttribute.PRODUCT).isEmpty()) { repositoryConfiguration.setRepositoryUrl(repositoryUrl.toString()); } if (!repositoryConfiguration.getOptionValues(BugzillaAttribute.PRODUCT).isEmpty()) { return repositoryConfiguration; } else { if (attempt == 0) { // empty configuration, retry // authenticate loggedIn = false; break; } else { throw new CoreException(new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "No products found in repository configuration. Ensure credentials are valid.")); //$NON-NLS-1$ } } } } } } if (loggedIn) { throw new CoreException(parseHtmlError(stream)); } } finally { stream.close(); } } finally { attempt++; if (method != null) { WebUtil.releaseConnection(method, monitor); } } } return null; } public InputStream getAttachmentData(String attachmentId, IProgressMonitor monitor) throws IOException, CoreException { String url = repositoryUrl + IBugzillaConstants.URL_GET_ATTACHMENT_DOWNLOAD + attachmentId; GetMethod method = getConnect(url, monitor); Status status = null; try { if (method.getStatusCode() == HttpStatus.SC_OK) { Header contentDisposition = method.getResponseHeader("Content-disposition"); //$NON-NLS-1$ if (contentDisposition == null) { status = parseHtmlError(method.getResponseBodyAsStream()); } else { // copy the response return method.getResponseBodyAsStream(); } } else { status = parseHtmlError(method.getResponseBodyAsStream()); } } catch (Exception e) { status = new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "Unable to retrieve attachment", e); //$NON-NLS-1$ } finally { if (status != null) { WebUtil.releaseConnection(method, monitor); throw new CoreException(status); } } throw new CoreException(status); } private String getCharacterEncoding() { if (repositoryConfiguration != null && repositoryConfiguration.getEncoding() != null && repositoryConfiguration.getEncoding().length() > 0) { // Special case for eclipse.org. See bug#280361 and bug#275102 return repositoryConfiguration.getEncoding(); } else { return characterEncoding; } } public void postAttachment(String bugReportID, String comment, AbstractTaskAttachmentSource source, TaskAttribute attachmentAttribute, IProgressMonitor monitor) throws HttpException, IOException, CoreException { monitor = Policy.monitorFor(monitor); String description = source.getDescription(); String contentType = source.getContentType(); String filename = source.getName(); boolean isPatch = false; if (attachmentAttribute != null) { TaskAttachmentMapper mapper = TaskAttachmentMapper.createFrom(attachmentAttribute); if (mapper.getDescription() != null) { description = mapper.getDescription(); } if (mapper.getContentType() != null) { contentType = mapper.getContentType(); } if (mapper.getFileName() != null) { filename = mapper.getFileName(); } if (mapper.isPatch() != null) { isPatch = mapper.isPatch(); } } Assert.isNotNull(bugReportID); Assert.isNotNull(source); Assert.isNotNull(contentType); if (description == null) { throw new CoreException(new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, Messages.BugzillaClient_description_required_when_submitting_attachments)); } hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); authenticate(monitor); GzipPostMethod postMethod = null; try { postMethod = new GzipPostMethod( WebUtil.getRequestPath(repositoryUrl + IBugzillaConstants.URL_POST_ATTACHMENT_UPLOAD), true); // This option causes the client to first // check // with the server to see if it will in fact receive the post before // actually sending the contents. postMethod.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, true); List<PartBase> parts = new ArrayList<PartBase>(); parts.add( new StringPart(IBugzillaConstants.POST_INPUT_ACTION, VALUE_ACTION_INSERT, getCharacterEncoding())); parts.add(new StringPart(IBugzillaConstants.POST_INPUT_BUGID, bugReportID, getCharacterEncoding())); if (description != null) { parts.add( new StringPart(IBugzillaConstants.POST_INPUT_DESCRIPTION, description, getCharacterEncoding())); } if (comment != null) { parts.add(new StringPart(IBugzillaConstants.POST_INPUT_COMMENT, comment, getCharacterEncoding())); } parts.add(new BugzillaFilePart(source, filename, contentType, getCharacterEncoding())); if (isPatch) { parts.add(new StringPart(ATTRIBUTE_ISPATCH, VALUE_ISPATCH)); } else { parts.add(new StringPart(ATTRIBUTE_CONTENTTYPEMETHOD, VALUE_CONTENTTYPEMETHOD_MANUAL)); parts.add(new StringPart(ATTRIBUTE_CONTENTTYPEENTRY, contentType)); } if (attachmentAttribute != null) { Collection<TaskAttribute> attributes = attachmentAttribute.getAttributes().values(); Iterator<TaskAttribute> itr = attributes.iterator(); while (itr.hasNext()) { TaskAttribute a = itr.next(); if (a.getId().startsWith(BugzillaAttribute.KIND_FLAG_TYPE) && repositoryConfiguration != null) { List<BugzillaFlag> flags = repositoryConfiguration.getFlags(); TaskAttribute requestee = a.getAttribute("requestee"); //$NON-NLS-1$ a = a.getAttribute("state"); //$NON-NLS-1$ String value = a.getValue(); String id = ""; //$NON-NLS-1$ if (value.equals(" ")) { //$NON-NLS-1$ continue; } String flagname = a.getMetaData().getLabel(); BugzillaFlag theFlag = null; for (BugzillaFlag bugzillaFlag : flags) { if (flagname.equals(bugzillaFlag.getName())) { theFlag = bugzillaFlag; break; } } if (theFlag != null) { int flagTypeNumber = theFlag.getFlagId(); id = "flag_type-" + flagTypeNumber; //$NON-NLS-1$ value = a.getValue(); if (value.equals("?") && requestee != null) { //$NON-NLS-1$ parts.add(new StringPart("requestee_type-" + flagTypeNumber, //$NON-NLS-1$ requestee.getValue() != null ? requestee.getValue() : "")); //$NON-NLS-1$ } } parts.add(new StringPart(id, value != null ? value : "")); //$NON-NLS-1$ } else if (a.getId().startsWith(BugzillaAttribute.KIND_FLAG)) { TaskAttribute flagnumber = a.getAttribute("number"); //$NON-NLS-1$ TaskAttribute requestee = a.getAttribute("requestee"); //$NON-NLS-1$ a = a.getAttribute("state"); //$NON-NLS-1$ String id = "flag-" + flagnumber.getValue(); //$NON-NLS-1$ String value = a.getValue(); if (value.equals(" ") || value.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ value = "X"; //$NON-NLS-1$ } if (value.equals("?") && requestee != null) { //$NON-NLS-1$ parts.add(new StringPart("requestee-" + flagnumber.getValue(), //$NON-NLS-1$ requestee.getValue() != null ? requestee.getValue() : "")); //$NON-NLS-1$ } parts.add(new StringPart(id, value != null ? value : "")); //$NON-NLS-1$ } } } String token = null; BugzillaVersion bugzillaVersion = null; if (repositoryConfiguration != null) { bugzillaVersion = repositoryConfiguration.getInstallVersion(); } else { bugzillaVersion = BugzillaVersion.MIN_VERSION; } if (bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_4_0) > 0) { token = getTokenInternal(repositoryUrl + ENTER_ATTACHMENT_CGI + bugReportID, monitor); } if (token != null) { parts.add(new StringPart(BugzillaAttribute.TOKEN.getKey(), token)); } postMethod.setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[1]), postMethod.getParams())); postMethod.setDoAuthentication(true); int status = WebUtil.execute(httpClient, hostConfiguration, postMethod, monitor); if (status == HttpStatus.SC_OK) { InputStream input = getResponseStream(postMethod, monitor); try { parsePostResponse(bugReportID, input); } finally { input.close(); } } else { WebUtil.releaseConnection(postMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, repositoryUrl.toString(), "Http error: " //$NON-NLS-1$ + HttpStatus.getStatusText(status))); // throw new IOException("Communication error occurred during // upload. \n\n" // + HttpStatus.getStatusText(status)); } } finally { if (postMethod != null) { WebUtil.releaseConnection(postMethod, monitor); } } } /** * calling method must release the connection on the returned PostMethod once finished. * * @throws CoreException */ private GzipPostMethod postFormData(String formUrl, NameValuePair[] formData, IProgressMonitor monitor) throws IOException, CoreException { GzipPostMethod postMethod = null; monitor = Policy.monitorFor(monitor); hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); authenticate(monitor); postMethod = new GzipPostMethod(WebUtil.getRequestPath(repositoryUrl.toString() + formUrl), true); postMethod.setRequestHeader("Content-Type", //$NON-NLS-1$ "application/x-www-form-urlencoded; charset=" + getCharacterEncoding()); //$NON-NLS-1$ httpClient.getHttpConnectionManager().getParams().setSoTimeout(WebUtil.getConnectionTimeout()); postMethod.setRequestBody(formData); postMethod.setDoAuthentication(true); int status = WebUtil.execute(httpClient, hostConfiguration, postMethod, monitor); if (status == HttpStatus.SC_OK) { return postMethod; } else if (status == HttpStatus.SC_MOVED_TEMPORARILY) { String redirectLocation; Header locationHeader = postMethod.getResponseHeader("location"); //$NON-NLS-1$ if (locationHeader != null) { redirectLocation = locationHeader.getValue(); if (!redirectLocation.startsWith("http")) { //$NON-NLS-1$ redirectLocation = location.getUrl() + "/" + redirectLocation; //$NON-NLS-1$ } WebUtil.releaseConnection(postMethod, monitor); throw new RedirectException(redirectLocation); } } WebUtil.releaseConnection(postMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, repositoryUrl.toString(), new IOException( "Communication error occurred during upload. \n\n" + HttpStatus.getStatusText(status)))); //$NON-NLS-1$ } public void postUpdateAttachment(TaskAttribute taskAttribute, String action, IProgressMonitor monitor) throws IOException, CoreException { List<NameValuePair> formData = new ArrayList<NameValuePair>(5); formData.add(new NameValuePair("action", action)); //$NON-NLS-1$ formData.add(new NameValuePair("contenttypemethod", "manual")); //$NON-NLS-1$ //$NON-NLS-2$ formData.add(new NameValuePair("id", taskAttribute.getValue())); //$NON-NLS-1$ Collection<TaskAttribute> attributes = taskAttribute.getAttributes().values(); Iterator<TaskAttribute> itr = attributes.iterator(); while (itr.hasNext()) { TaskAttribute attrib = itr.next(); String id = attrib.getId(); if (id.equals(BugzillaAttribute.DELTA_TS.getKey())) { continue; } String value = attrib.getValue(); if (id.equals(TaskAttribute.ATTACHMENT_AUTHOR) || id.equals("date") || id.equals("size") //$NON-NLS-1$ //$NON-NLS-2$ || id.equals(TaskAttribute.ATTACHMENT_URL)) { continue; } if (id.equals("desc")) { //$NON-NLS-1$ id = "description"; //$NON-NLS-1$ } if (id.equals("ctype")) { //$NON-NLS-1$ id = "contenttypeentry"; //$NON-NLS-1$ } if (id.equals(TaskAttribute.ATTACHMENT_IS_DEPRECATED)) { id = "isobsolete"; //$NON-NLS-1$ } if (id.equals(TaskAttribute.ATTACHMENT_IS_PATCH)) { id = "ispatch"; //$NON-NLS-1$ } if (id.startsWith(BugzillaAttribute.KIND_FLAG_TYPE)) { TaskAttribute requestee = attrib.getAttribute("requestee"); //$NON-NLS-1$ TaskAttribute state = attrib.getAttribute("state"); //$NON-NLS-1$ String requesteeName = "requestee_type-" + id.substring(26); //$NON-NLS-1$ String requesteeValue = requestee.getValue(); value = state.getValue(); if (value.equals(" ") || value.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ value = "X"; //$NON-NLS-1$ } if (value.equals("?")) { //$NON-NLS-1$ formData.add(new NameValuePair(requesteeName, requesteeValue)); } id = "flag_type-" + id.substring(26); //$NON-NLS-1$ } else if (id.startsWith(BugzillaAttribute.KIND_FLAG)) { TaskAttribute requestee = attrib.getAttribute("requestee"); //$NON-NLS-1$ TaskAttribute state = attrib.getAttribute("state"); //$NON-NLS-1$ String requesteeName = "requestee-" + id.substring(21); //$NON-NLS-1$ String requesteeValue = requestee.getValue(); value = state.getValue(); if (value.equals(" ") || value.equals("")) { //$NON-NLS-1$//$NON-NLS-2$ value = "X"; //$NON-NLS-1$ } if (value.equals("?")) { //$NON-NLS-1$ formData.add(new NameValuePair(requesteeName, requesteeValue)); } id = "flag-" + id.substring(21); //$NON-NLS-1$ } if (!value.equals("")) { //$NON-NLS-1$ formData.add(new NameValuePair(id, value)); } } GzipPostMethod method = null; InputStream input = null; try { method = postFormData(PROCESS_ATTACHMENT_CGI, formData.toArray(new NameValuePair[formData.size()]), monitor); if (method == null) { throw new IOException(Messages.BugzillaClient_could_not_post_form_null_returned); } input = getResponseStream(method, monitor); parsePostResponse(taskAttribute.getTaskData().getTaskId(), input); // BufferedReader in = new BufferedReader(new // InputStreamReader(input, method.getRequestCharSet())); // if (in.markSupported()) { // in.mark(1); // } // HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer(in, // null); // // boolean isTitle = false; // String title = ""; //$NON-NLS-1$ // // for (Token token = tokenizer.nextToken(); token.getType() != // Token.EOF; token = tokenizer.nextToken()) { // // if (token.getType() == Token.TAG && ((HtmlTag) // (token.getValue())).getTagType() == Tag.TITLE // && !((HtmlTag) (token.getValue())).isEndTag()) { // isTitle = true; // continue; // } // // if (isTitle) { // // get all of the data in the title tag // if (token.getType() != Token.TAG) { // title += ((StringBuffer) // token.getValue()).toString().toLowerCase(Locale.ENGLISH) + " "; // //$NON-NLS-1$ // continue; // } else if (token.getType() == Token.TAG && ((HtmlTag) // token.getValue()).getTagType() == Tag.TITLE // && ((HtmlTag) token.getValue()).isEndTag()) { // // for (Iterator<String> iterator = // bugzillaLanguageSettings.getResponseForCommand( // BugzillaLanguageSettings.COMMAND_CHANGES_SUBMITTED).iterator(); // iterator.hasNext() // && !existingBugPosted;) { // String value = iterator.next().toLowerCase(Locale.ENGLISH); // existingBugPosted = existingBugPosted || title.indexOf(value) != // -1; // } // break; // } // } // } // // if (existingBugPosted != true) { // try { // if (in.markSupported()) { // in.reset(); // } // } catch (IOException e) { // // ignore // } // parseHtmlError(in); // } // // } catch (ParseException e) { // loggedIn = false; // throw new CoreException(new BugzillaStatus(IStatus.ERROR, // BugzillaCorePlugin.ID_PLUGIN, // RepositoryStatus.ERROR_INTERNAL, "Unable to parse response from " // + repositoryUrl.toString() + ".")); //$NON-NLS-1$ //$NON-NLS-2$ } finally { if (input != null) { input.close(); } if (method != null) { WebUtil.releaseConnection(method, monitor); } } } public RepositoryResponse postTaskData(TaskData taskData, IProgressMonitor monitor) throws IOException, CoreException { try { return postTaskDataInternal(taskData, monitor); } catch (CoreException e) { TaskAttribute qaContact = taskData.getRoot().getAttribute(BugzillaAttribute.QA_CONTACT.getKey()); if (qaContact != null) { String qaContactValue = qaContact.getValue(); String message = e.getMessage(); if ("An unknown repository error has occurred: Bugzilla/Bug.pm line".equals(message) //$NON-NLS-1$ && qaContactValue != null && !qaContactValue.equals("")) { //$NON-NLS-1$ if (e.getStatus() instanceof RepositoryStatus) { RepositoryStatus repositoryStatus = (RepositoryStatus) e.getStatus(); RepositoryStatus status = RepositoryStatus.createHtmlStatus(repositoryStatus.getRepositoryUrl(), IStatus.INFO, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY, "Error may result when QAContact field not enabled.", //$NON-NLS-1$ repositoryStatus.getHtmlMessage()); throw new CoreException(status); } } } try { if (e.getStatus().getCode() == RepositoryStatus.ERROR_REPOSITORY_LOGIN) { return postTaskDataInternal(taskData, monitor); } else if (e.getStatus().getCode() == IBugzillaConstants.REPOSITORY_STATUS_SUSPICIOUS_ACTION) { taskData.getRoot().removeAttribute(BugzillaAttribute.TOKEN.getKey()); return postTaskDataInternal(taskData, monitor); } else { throw e; } } catch (CoreException e1) { throw e1; } } } private String getTokenInternal(String bugUrl, IProgressMonitor monitor) throws IOException, CoreException { String tokenValue = null; if (!loggedIn) { authenticate(new SubProgressMonitor(monitor, 1)); } hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); GzipGetMethod getMethod = new GzipGetMethod(WebUtil.getRequestPath(bugUrl), false); getMethod.setRequestHeader("Content-Type", "text/xml; charset=" + getCharacterEncoding()); //$NON-NLS-1$ //$NON-NLS-2$ httpClient.getParams().setParameter("http.protocol.single-cookie-header", true); //$NON-NLS-1$ getMethod.setDoAuthentication(true); int code; InputStream inStream = null; try { code = WebUtil.execute(httpClient, hostConfiguration, getMethod, monitor); if (code == HttpURLConnection.HTTP_OK) { inStream = getResponseStream(getMethod, monitor); HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer( new BufferedReader(new InputStreamReader(inStream, getCharacterEncoding())), null); for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.INPUT && !((HtmlTag) (token.getValue())).isEndTag()) { HtmlTag tag = (HtmlTag) token.getValue(); String name = tag.getAttribute("name"); //$NON-NLS-1$ String value = tag.getAttribute("value"); //$NON-NLS-1$ if (name != null && name.equalsIgnoreCase(BugzillaAttribute.TOKEN.getKey()) && value != null && value.length() > 0) { if (tokenValue == null) { tokenValue = value; } } } } } } catch (Exception e) { throw new CoreException(new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "Unable to retrieve group security information", e)); //$NON-NLS-1$ } finally { if (inStream != null) { try { inStream.close(); } catch (IOException e) { // ignore } } WebUtil.releaseConnection(getMethod, monitor); } return tokenValue; } public RepositoryResponse postTaskDataInternal(TaskData taskData, IProgressMonitor monitor) throws IOException, CoreException { NameValuePair[] formData = null; monitor = Policy.monitorFor(monitor); BugzillaRepositoryResponse response; authenticate(new SubProgressMonitor(monitor, 1)); if (repositoryConfiguration == null) { getRepositoryConfiguration(new SubProgressMonitor(monitor, 1), null); connector.addRepositoryConfiguration(repositoryConfiguration); } if (taskData == null) { return null; } else if (taskData.isNew()) { String token = null; BugzillaVersion bugzillaVersion = null; if (repositoryConfiguration != null) { bugzillaVersion = repositoryConfiguration.getInstallVersion(); } else { bugzillaVersion = BugzillaVersion.MIN_VERSION; } if (bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_4_0) > 0) { TaskAttribute productAttribute = taskData.getRoot().getAttribute(BugzillaAttribute.PRODUCT.getKey()); token = getTokenInternal( taskData.getRepositoryUrl() + ENTER_BUG_PRODUCT_CGI + URLEncoder.encode(productAttribute.getValue(), IBugzillaConstants.ENCODING_UTF_8), monitor); } formData = getPairsForNew(taskData, token); } else { formData = getPairsForExisting(taskData, new SubProgressMonitor(monitor, 1)); } GzipPostMethod method = null; InputStream input = null; try { if (taskData.isNew()) { method = postFormData(POST_BUG_CGI, formData, monitor); } else { method = postFormData(PROCESS_BUG_CGI, formData, monitor); } if (method == null) { throw new IOException("Could not post form, client returned null method."); //$NON-NLS-1$ } input = getResponseStream(method, monitor); response = parsePostResponse(taskData.getTaskId(), input); return response; } catch (CoreException e) { throw e; } finally { if (input != null) { input.close(); } if (method != null) { WebUtil.releaseConnection(method, monitor); } } } private NameValuePair[] getPairsForNew(TaskData taskData, String token) { Map<String, NameValuePair> fields = new HashMap<String, NameValuePair>(); if (token != null) { fields.put(BugzillaAttribute.TOKEN.getKey(), new NameValuePair(BugzillaAttribute.TOKEN.getKey(), token)); } BugzillaVersion bugzillaVersion = null; if (repositoryConfiguration != null) { bugzillaVersion = repositoryConfiguration.getInstallVersion(); } else { bugzillaVersion = BugzillaVersion.MIN_VERSION; } // go through all of the attributes and add them to // the bug post Collection<TaskAttribute> attributes = new ArrayList<TaskAttribute>( taskData.getRoot().getAttributes().values()); Iterator<TaskAttribute> itr = attributes.iterator(); while (itr.hasNext()) { TaskAttribute a = itr.next(); if (a != null && a.getId() != null && a.getId().compareTo("") != 0) { //$NON-NLS-1$ String value = null; value = a.getValue(); if (value == null) { continue; } String id = a.getId(); if (id.equals(BugzillaAttribute.BUG_STATUS.getKey()) && bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_4_0) >= 0) { if (repositoryConfiguration.getOptionValues(BugzillaAttribute.BUG_STATUS) .contains(BUGZILLA_REPORT_STATUS_4_0.IN_PROGRESS.toString()) || repositoryConfiguration.getOptionValues(BugzillaAttribute.BUG_STATUS) .contains(BUGZILLA_REPORT_STATUS_4_0.CONFIRMED.toString())) { TaskAttribute attributeOperation = taskData.getRoot() .getMappedAttribute(TaskAttribute.OPERATION); value = attributeOperation.getValue().toUpperCase(); if (!BugzillaOperation.new_default.toString().toUpperCase().equals(value)) { fields.put(id, new NameValuePair(id, value != null ? value : "")); //$NON-NLS-1$ } else { continue; } } } if (id.equals(BugzillaAttribute.NEWCC.getKey())) { TaskAttribute b = taskData.getRoot().createAttribute(BugzillaAttribute.CC.getKey()); b.getMetaData() .defaults() .setReadOnly(BugzillaAttribute.CC.isReadOnly()) .setKind(BugzillaAttribute.CC.getKind()) .setLabel(BugzillaAttribute.CC.toString()) .setType(BugzillaAttribute.CC.getType()); for (String val : a.getValues()) { if (val != null) { b.addValue(val); } } a = b; id = a.getId(); cleanIfShortLogin(a); } else { cleanQAContact(a); } if (a.getMetaData().getType() != null && a.getMetaData().getType().equals(TaskAttribute.TYPE_MULTI_SELECT)) { List<String> values = a.getValues(); int i = 0; for (String string : values) { fields.put(id + i++, new NameValuePair(id, string != null ? string : "")); //$NON-NLS-1$ } } else if (id != null && id.compareTo("") != 0) { //$NON-NLS-1$ fields.put(id, new NameValuePair(id, value != null ? value : "")); //$NON-NLS-1$ } } } TaskAttribute descAttribute = taskData.getRoot().getMappedAttribute(TaskAttribute.DESCRIPTION); if (descAttribute != null && !descAttribute.getValue().equals("")) { //$NON-NLS-1$ if (bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_2_18) == 0) { fields.put(KEY_COMMENT, new NameValuePair(KEY_COMMENT, formatTextToLineWrap(descAttribute.getValue(), true))); } else { fields.put(KEY_COMMENT, new NameValuePair(KEY_COMMENT, descAttribute.getValue())); } } return fields.values().toArray(new NameValuePair[fields.size()]); } private void cleanQAContact(TaskAttribute a) { if (a.getId().equals(BugzillaAttribute.QA_CONTACT.getKey())) { cleanIfShortLogin(a); } } private void cleanIfShortLogin(TaskAttribute a) { if ("true".equals(configParameters.get(IBugzillaConstants.REPOSITORY_SETTING_SHORT_LOGIN))) { //$NON-NLS-1$ if (a.getValue() != null && a.getValue().length() > 0) { int atIndex = a.getValue().indexOf("@"); //$NON-NLS-1$ if (atIndex != -1) { String newValue = a.getValue().substring(0, atIndex); a.setValue(newValue); } } } } private NameValuePair[] getPairsForExisting(TaskData model, IProgressMonitor monitor) throws CoreException { boolean groupSecurityEnabled = false; Map<String, NameValuePair> fields = new HashMap<String, NameValuePair>(); fields.put(KEY_FORM_NAME, new NameValuePair(KEY_FORM_NAME, VAL_PROCESS_BUG)); // go through all of the attributes and add them to the bug post Collection<TaskAttribute> attributes = model.getRoot().getAttributes().values(); Iterator<TaskAttribute> itr = attributes.iterator(); boolean tokenFound = false; boolean tokenRequired = false; BugzillaVersion bugzillaVersion = null; if (repositoryConfiguration != null) { bugzillaVersion = repositoryConfiguration.getInstallVersion(); } else { bugzillaVersion = BugzillaVersion.MIN_VERSION; } while (itr.hasNext()) { TaskAttribute a = itr.next(); if (a == null) { continue; } else { String id = a.getId(); if (id.equalsIgnoreCase(BugzillaAttribute.TOKEN.getKey())) { tokenFound = true; } else if (id.equals(BugzillaAttribute.QA_CONTACT.getKey()) || id.equals(BugzillaAttribute.ASSIGNED_TO.getKey())) { cleanIfShortLogin(a); } else if (id.equals(BugzillaAttribute.REPORTER.getKey()) || id.equals(BugzillaAttribute.CC.getKey()) || id.equals(BugzillaAttribute.REMOVECC.getKey()) || id.equals(BugzillaAttribute.REMOVE_SEE_ALSO.getKey()) || id.equals(BugzillaAttribute.SEE_ALSO_READ.getKey()) || id.equals(BugzillaAttribute.CREATION_TS.getKey()) || id.equals(BugzillaAttribute.BUG_STATUS.getKey()) || id.equals(BugzillaAttribute.VOTES.getKey())) { continue; } else if (id.equals(BugzillaAttribute.NEW_COMMENT.getKey())) { if (bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_2_18) == 0) { a.setValue(formatTextToLineWrap(a.getValue(), true)); } } else if (id.equals(BugzillaAttribute.GROUP.getKey()) && a.getValue().length() > 0) { groupSecurityEnabled = true; } if (a.getMetaData().getType() != null && a.getMetaData().getType().equals(TaskAttribute.TYPE_MULTI_SELECT)) { List<String> values = a.getValues(); int i = 0; for (String string : values) { fields.put(id + i++, new NameValuePair(id, string != null ? string : "")); //$NON-NLS-1$ } } else if (id != null && id.compareTo("") != 0) { //$NON-NLS-1$ String value = a.getValue(); if (id.equals(BugzillaAttribute.DELTA_TS.getKey())) { if (bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_4_7) < 0 || (bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_5) >= 0) && bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_6) < 0) { value = stripTimeZone(value); } } if (id.startsWith(BugzillaAttribute.KIND_FLAG_TYPE) && repositoryConfiguration != null) { List<BugzillaFlag> flags = repositoryConfiguration.getFlags(); TaskAttribute requestee = a.getAttribute("requestee"); //$NON-NLS-1$ a = a.getAttribute("state"); //$NON-NLS-1$ value = a.getValue(); if (value.equals(" ") || value.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ continue; } String flagname = a.getMetaData().getLabel(); BugzillaFlag theFlag = null; for (BugzillaFlag bugzillaFlag : flags) { if (flagname.equals(bugzillaFlag.getName()) && bugzillaFlag.getType().equals("bug")) { //$NON-NLS-1$ theFlag = bugzillaFlag; break; } } if (theFlag != null) { int flagTypeNumber = theFlag.getFlagId(); id = "flag_type-" + flagTypeNumber; //$NON-NLS-1$ value = a.getValue(); if (value.equals("?") && requestee != null) { //$NON-NLS-1$ fields.put("requestee_type-" + flagTypeNumber, //$NON-NLS-1$ new NameValuePair( "requestee_type-" //$NON-NLS-1$ + flagTypeNumber, requestee.getValue() != null ? requestee.getValue() : "")); //$NON-NLS-1$ } } } else if (id.startsWith(BugzillaAttribute.KIND_FLAG)) { TaskAttribute flagnumber = a.getAttribute("number"); //$NON-NLS-1$ TaskAttribute requestee = a.getAttribute("requestee"); //$NON-NLS-1$ a = a.getAttribute("state"); //$NON-NLS-1$ id = "flag-" + flagnumber.getValue(); //$NON-NLS-1$ value = a.getValue(); if (value.equals(" ") || value.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ value = "X"; //$NON-NLS-1$ } if (value.equals("?") && requestee != null) { //$NON-NLS-1$ fields.put("requestee-" + flagnumber.getValue(), //$NON-NLS-1$ new NameValuePair( "requestee-" //$NON-NLS-1$ + flagnumber.getValue(), requestee.getValue() != null ? requestee.getValue() : "")); //$NON-NLS-1$ } } else if (id.startsWith(TaskAttribute.PREFIX_COMMENT)) { String valueID = a.getValue(); TaskAttribute definedIsPrivate = a .getAttribute(IBugzillaConstants.BUGZILLA_PREFIX_DEFINED_ISPRIVATE + valueID); TaskAttribute isPrivate = a .getAttribute(IBugzillaConstants.BUGZILLA_PREFIX_ISPRIVATE + valueID); if (definedIsPrivate != null && isPrivate != null) { fields.put(definedIsPrivate.getId(), new NameValuePair(definedIsPrivate.getId(), definedIsPrivate.getValue() != null ? definedIsPrivate.getValue() : "")); //$NON-NLS-1$ fields.put(isPrivate.getId(), new NameValuePair(isPrivate.getId(), isPrivate.getValue() != null ? isPrivate.getValue() : "")); //$NON-NLS-1$ } // Don't post comments ("task.common.comment-") continue; } else if (id.compareTo(BugzillaAttribute.LONG_DESC.getKey()) == 0) { TaskAttribute idAttribute = a.getAttribute("id"); //$NON-NLS-1$ if (idAttribute != null) { String valueID = idAttribute.getValue(); TaskAttribute definedIsPrivate = a .getAttribute(IBugzillaConstants.BUGZILLA_PREFIX_DEFINED_ISPRIVATE + valueID); TaskAttribute isPrivate = a .getAttribute(IBugzillaConstants.BUGZILLA_PREFIX_ISPRIVATE + valueID); if (definedIsPrivate != null && isPrivate != null) { fields.put(definedIsPrivate.getId(), new NameValuePair(definedIsPrivate.getId(), definedIsPrivate.getValue() != null ? definedIsPrivate.getValue() : "")); //$NON-NLS-1$ fields.put(isPrivate.getId(), new NameValuePair(isPrivate.getId(), isPrivate.getValue() != null ? isPrivate.getValue() : "")); //$NON-NLS-1$ } } } else if (id.startsWith("task.common.")) { //$NON-NLS-1$ // Don't post any remaining non-bugzilla specific // attributes continue; } if (id.equals(BugzillaAttribute.DELTA_TS.getKey())) { value = BugzillaUtil.removeTimezone(value); } fields.put(id, new NameValuePair(id, value != null ? value : "")); //$NON-NLS-1$ } } } // when posting the bug id is encoded in a hidden field named 'id' TaskAttribute attributeBugId = model.getRoot().getAttribute(BugzillaAttribute.BUG_ID.getKey()); if (attributeBugId != null) { fields.put(KEY_ID, new NameValuePair(KEY_ID, attributeBugId.getValue())); } // add the operation to the bug post if (bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_2) < 0) { TaskAttribute attributeOperation = model.getRoot().getMappedAttribute(TaskAttribute.OPERATION); if (attributeOperation == null) { fields.put(KEY_KNOB, new NameValuePair(KEY_KNOB, VAL_NONE)); } else { TaskAttribute originalOperation = model.getRoot() .getAttribute(TaskAttribute.PREFIX_OPERATION + attributeOperation.getValue()); if (originalOperation == null) { // Work around for bug#241012 fields.put(KEY_KNOB, new NameValuePair(KEY_KNOB, VAL_NONE)); } else { String inputAttributeId = originalOperation.getMetaData() .getValue(TaskAttribute.META_ASSOCIATED_ATTRIBUTE_ID); if (inputAttributeId == null || inputAttributeId.equals("")) { //$NON-NLS-1$ String sel = attributeOperation.getValue(); fields.put(KEY_KNOB, new NameValuePair(KEY_KNOB, sel)); } else { fields.put(KEY_KNOB, new NameValuePair(KEY_KNOB, attributeOperation.getValue())); TaskAttribute inputAttribute = attributeOperation.getTaskData() .getRoot() .getAttribute(inputAttributeId); if (inputAttribute != null) { if (inputAttribute.getOptions().size() > 0) { String sel = inputAttribute.getValue(); String knob = inputAttribute.getId(); if (knob.equals(BugzillaOperation.resolve.getInputId()) || knob.equals(BugzillaOperation.close_with_resolution.getInputId())) { knob = BugzillaAttribute.RESOLUTION.getKey(); } fields.put(knob, new NameValuePair(knob, inputAttribute.getOption(sel))); } else { String sel = inputAttribute.getValue(); String knob = attributeOperation.getValue(); if (knob.equals(BugzillaOperation.reassign.toString())) { knob = BugzillaAttribute.ASSIGNED_TO.getKey(); } fields.put(knob, new NameValuePair(knob, sel)); } } } } if (model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW) != null && model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).getValue().length() > 0) { fields.put(KEY_COMMENT, new NameValuePair(KEY_COMMENT, model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).getValue())); } else if (attributeOperation != null && attributeOperation.getValue().equals(BugzillaOperation.duplicate.toString())) { // fix for bug#198677 fields.put(KEY_COMMENT, new NameValuePair(KEY_COMMENT, "")); //$NON-NLS-1$ } } } else { // A token is required for bugzilla 3.2.1 and newer tokenRequired = bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_2) > 0; String fieldName = BugzillaAttribute.BUG_STATUS.getKey(); TaskAttribute attributeStatus = model.getRoot().getMappedAttribute(TaskAttribute.STATUS); TaskAttribute attributeOperation = model.getRoot().getMappedAttribute(TaskAttribute.OPERATION); if (attributeOperation == null) { fields.put(fieldName, new NameValuePair(fieldName, attributeStatus.getValue())); } else { TaskAttribute originalOperation = model.getRoot() .getAttribute(TaskAttribute.PREFIX_OPERATION + attributeOperation.getValue()); if (originalOperation == null) { // Work around for bug#241012 fields.put(fieldName, new NameValuePair(fieldName, attributeStatus.getValue())); } else { String inputAttributeId = originalOperation.getMetaData() .getValue(TaskAttribute.META_ASSOCIATED_ATTRIBUTE_ID); String selOp = attributeOperation.getValue().toUpperCase(); if (selOp.equals("NONE")) { //$NON-NLS-1$ selOp = attributeStatus.getValue(); } if (selOp.equals("ACCEPT")) { //$NON-NLS-1$ selOp = "ASSIGNED"; //$NON-NLS-1$ } if (selOp.equals("RESOLVE")) { //$NON-NLS-1$ selOp = "RESOLVED"; //$NON-NLS-1$ } if (selOp.equals("VERIFY")) { //$NON-NLS-1$ selOp = "VERIFIED"; //$NON-NLS-1$ } if (selOp.equals("CLOSE")) { //$NON-NLS-1$ selOp = "CLOSED"; //$NON-NLS-1$ } if (selOp.equals("REOPEN")) { //$NON-NLS-1$ selOp = "REOPENED"; //$NON-NLS-1$ } if (selOp.equals("MARKNEW")) { //$NON-NLS-1$ selOp = "NEW"; //$NON-NLS-1$ } if (selOp.equals("DUPLICATE")) { //$NON-NLS-1$ if (repositoryConfiguration != null) { selOp = repositoryConfiguration.getDuplicateStatus(); } else { selOp = "RESOLVED"; //$NON-NLS-1$ } String knob = BugzillaAttribute.RESOLUTION.getKey(); fields.put(knob, new NameValuePair(knob, "DUPLICATE")); //$NON-NLS-1$ } fields.put(fieldName, new NameValuePair(fieldName, selOp)); if (inputAttributeId != null && !inputAttributeId.equals("")) { //$NON-NLS-1$ TaskAttribute inputAttribute = attributeOperation.getTaskData() .getRoot() .getAttribute(inputAttributeId); if (inputAttribute != null) { if (inputAttribute.getOptions().size() > 0) { String sel = inputAttribute.getValue(); String knob = inputAttribute.getId(); if (knob.equals(BugzillaOperation.resolve.getInputId())) { knob = BugzillaAttribute.RESOLUTION.getKey(); } fields.put(knob, new NameValuePair(knob, inputAttribute.getOption(sel))); } else { String sel = inputAttribute.getValue(); String knob = attributeOperation.getValue(); if (knob.equals(BugzillaOperation.duplicate.toString())) { knob = inputAttributeId; } if (knob.equals(BugzillaOperation.reassign.toString())) { knob = BugzillaAttribute.ASSIGNED_TO.getKey(); } fields.put(knob, new NameValuePair(knob, sel)); } } } } } if (model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW) != null && model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).getValue().length() > 0) { fields.put(KEY_COMMENT, new NameValuePair(KEY_COMMENT, model.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).getValue())); } } if (model.getRoot().getMappedAttribute(BugzillaAttribute.SHORT_DESC.getKey()) != null) { fields.put(KEY_SHORT_DESC, new NameValuePair(KEY_SHORT_DESC, model.getRoot().getMappedAttribute(BugzillaAttribute.SHORT_DESC.getKey()).getValue())); } TaskAttribute attributeRemoveCC = model.getRoot().getMappedAttribute(BugzillaAttribute.REMOVECC.getKey()); if (attributeRemoveCC != null) { List<String> removeCC = attributeRemoveCC.getValues(); if (removeCC != null && removeCC.size() > 0) { String[] s = new String[removeCC.size()]; fields.put(KEY_CC, new NameValuePair(KEY_CC, toCommaSeparatedList(removeCC.toArray(s)))); fields.put(BugzillaAttribute.REMOVECC.getKey(), new NameValuePair(BugzillaAttribute.REMOVECC.getKey(), VAL_TRUE)); } } TaskAttribute attributeRemoveSeeAlso = model.getRoot() .getMappedAttribute(BugzillaAttribute.REMOVE_SEE_ALSO.getKey()); if (attributeRemoveSeeAlso != null) { List<String> removeSeeAlso = attributeRemoveSeeAlso.getValues(); int idx = 0; for (String string : removeSeeAlso) { fields.put(BugzillaAttribute.REMOVE_SEE_ALSO.getKey() + idx++, new NameValuePair(BugzillaAttribute.REMOVE_SEE_ALSO.getKey(), string)); } } // check for security token (required for successful submit on Bugzilla // 3.2.1 and greater but not in xml until Bugzilla 3.2.3 bug#263318) if (groupSecurityEnabled || (!tokenFound && tokenRequired)) { // get security and token if exists from html and include in post HtmlInformation htmlInfo = getHtmlOnlyInformation(model, monitor); if (groupSecurityEnabled) { for (String key : htmlInfo.getGroups().keySet()) { fields.put(key, new NameValuePair(key, htmlInfo.getGroups().get(key))); } } if (htmlInfo.getToken() != null && htmlInfo.getToken().length() > 0 && tokenRequired) { NameValuePair tokenPair = fields.get(BugzillaAttribute.TOKEN.getKey()); if (tokenPair != null) { tokenPair.setValue(htmlInfo.getToken()); } else { fields.put(BugzillaAttribute.TOKEN.getKey(), new NameValuePair(BugzillaAttribute.TOKEN.getKey(), htmlInfo.getToken())); } } } return fields.values().toArray(new NameValuePair[fields.size()]); } private HtmlInformation getHtmlOnlyInformation(TaskData taskData, IProgressMonitor monitor) throws CoreException { HtmlInformation htmlInfo = new HtmlInformation(); authenticate(new SubProgressMonitor(monitor, 1)); hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); String bugUrl = taskData.getRepositoryUrl() + IBugzillaConstants.URL_GET_SHOW_BUG + taskData.getTaskId(); GzipGetMethod getMethod = new GzipGetMethod(WebUtil.getRequestPath(bugUrl), false); getMethod.setRequestHeader("Content-Type", "text/xml; charset=" + getCharacterEncoding()); //$NON-NLS-1$ //$NON-NLS-2$ httpClient.getParams().setParameter("http.protocol.single-cookie-header", true); //$NON-NLS-1$ getMethod.setDoAuthentication(true); int code; InputStream inStream = null; try { code = WebUtil.execute(httpClient, hostConfiguration, getMethod, monitor); if (code == HttpURLConnection.HTTP_OK) { inStream = getResponseStream(getMethod, monitor); HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer( new BufferedReader(new InputStreamReader(inStream, getCharacterEncoding())), null); String formName = null; for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.FORM && !((HtmlTag) (token.getValue())).isEndTag()) { HtmlTag tag = (HtmlTag) token.getValue(); formName = tag.getAttribute("name"); //$NON-NLS-1$ } else if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.INPUT && !((HtmlTag) (token.getValue())).isEndTag()) { HtmlTag tag = (HtmlTag) token.getValue(); // String name = tag.getAttribute("name"); String id = tag.getAttribute("id"); //$NON-NLS-1$ String checkedValue = tag.getAttribute("checked"); //$NON-NLS-1$ String type = tag.getAttribute("type"); //$NON-NLS-1$ String name = tag.getAttribute("name"); //$NON-NLS-1$ String value = tag.getAttribute("value"); //$NON-NLS-1$ if (type != null && type.equalsIgnoreCase("checkbox") && id != null && id.startsWith("bit-")) { //$NON-NLS-1$ //$NON-NLS-2$ htmlInfo.getGroups().put(id, checkedValue); } else if (name != null && name.equalsIgnoreCase(BugzillaAttribute.TOKEN.getKey()) && value != null && value.length() > 0 && formName != null && formName.equals("changeform")) { //$NON-NLS-1$ htmlInfo.setToken(value); } } } } } catch (Exception e) { throw new CoreException(new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "Unable to retrieve group security information", e)); //$NON-NLS-1$ } finally { if (inStream != null) { try { inStream.close(); } catch (IOException e) { // ignore } } WebUtil.releaseConnection(getMethod, monitor); } return htmlInfo; } public static String stripTimeZone(String longTime) { String result = longTime; if (longTime != null) { String[] values = longTime.split(" "); //$NON-NLS-1$ if (values != null && values.length > 2) { result = values[0] + " " + values[1]; //$NON-NLS-1$ } } return result; } private static String toCommaSeparatedList(String[] strings) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < strings.length; i++) { buffer.append(strings[i]); if (i != strings.length - 1) { buffer.append(","); //$NON-NLS-1$ } } return buffer.toString(); } /** * Utility method for determining what potential error has occurred from a bugzilla html reponse page */ private Status parseHtmlError(InputStream inputStream) { try { BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding())); parseRepositoryResponse(null, in); return new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "No Exception from parseHtmlError"); //$NON-NLS-1$ } catch (CoreException e) { if (e.getStatus() instanceof BugzillaStatus || e.getStatus() instanceof RepositoryStatus) { return (Status) e.getStatus(); } else { return new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "No Exception from parseHtmlError, Status is not from expected Type"); //$NON-NLS-1$ } } catch (UnsupportedEncodingException e1) { return new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "UnsupportedEncodingException:", e1); //$NON-NLS-1$ } catch (IOException e) { return new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "IOException:", e); //$NON-NLS-1$ } } private BugzillaRepositoryResponse parsePostResponse(String taskId, InputStream inputStream) throws IOException, CoreException { BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding())); return parseRepositoryResponse(taskId, in); } private BugzillaRepositoryResponse parseRepositoryResponse(String taskId, BufferedReader in) throws IOException, CoreException { HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer(in, null); BugzillaRepositoryResponse response; boolean isTitle = false; String title = ""; //$NON-NLS-1$ String body = ""; //$NON-NLS-1$ String errorMessage = null; try { for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { body += token.toString(); if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.TITLE && !((HtmlTag) (token.getValue())).isEndTag()) { isTitle = true; continue; } if (isTitle) { // get all of the data in the title tag if (token.getType() != Token.TAG) { title += ((StringBuffer) token.getValue()).toString().toLowerCase(Locale.ENGLISH) + " "; //$NON-NLS-1$ continue; } else if (token.getType() == Token.TAG && ((HtmlTag) token.getValue()).getTagType() == Tag.TITLE && ((HtmlTag) token.getValue()).isEndTag()) { boolean found = false; // Results for posting to Existing bugs for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_CHANGES_SUBMITTED)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { response = new BugzillaRepositoryResponse(ResponseKind.TASK_UPDATED, taskId); parseResultOK(tokenizer, response); return response; } } for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_PROCESSED)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { response = new BugzillaRepositoryResponse(ResponseKind.TASK_UPDATED, taskId); parseResultOK(tokenizer, response); return response; } } // Results for posting NEW bugs for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_SUBMITTED)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { int stopIndex = title.indexOf(value); if (stopIndex > -1) { for (String string2 : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_BUG)) { value = string2.toLowerCase(Locale.ENGLISH); int startIndex = title.indexOf(value); if (startIndex > -1) { startIndex = startIndex + value.length(); String result = (title.substring(startIndex, stopIndex)).trim(); response = new BugzillaRepositoryResponse(ResponseKind.TASK_CREATED, result); parseResultOK(tokenizer, response); return response; } } } StatusHandler.log(new BugzillaStatus(IStatus.INFO, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_INTERNAL, "Unable to retrieve new task id from: " + title)); //$NON-NLS-1$ throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_INTERNAL, Messages.BugzillaClient_Unable_to_retrieve_new_task)); } } // Error results for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_LOGIN)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { loggedIn = false; if (hasAuthenticationCredentials()) { throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), title)); } else { throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), Messages.BugzillaClient_anonymous_user_not_allowed)); } } } for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_COLLISION)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.REPOSITORY_COLLISION, repositoryUrl.toString())); } } for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_COMMENT_REQUIRED)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { throw new CoreException(new BugzillaStatus(IStatus.INFO, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.REPOSITORY_COMMENT_REQUIRED)); } } for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_SUSPICIOUS_ACTION)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { for (Token tokenError = tokenizer.nextToken(); tokenError .getType() != Token.EOF; tokenError = tokenizer.nextToken()) { body += tokenError.toString(); if (tokenError.getType() == Token.COMMENT) { if (tokenError.getValue().toString().startsWith("reason=")) { //$NON-NLS-1$ String reason = tokenError.getValue().toString().substring(7); throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, IBugzillaConstants.REPOSITORY_STATUS_SUSPICIOUS_ACTION, repositoryUrl.toString(), "Reason = " + reason)); //$NON-NLS-1$ } } } throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, IBugzillaConstants.REPOSITORY_STATUS_SUSPICIOUS_ACTION, repositoryUrl.toString(), "unknown reason because Bugzilla < 4.0 was used")); //$NON-NLS-1$ } } for (String string : bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_LOGGED_OUT)) { String value = string.toLowerCase(Locale.ENGLISH); found = title.indexOf(value) != -1; if (found) { loggedIn = false; throw new CoreException(new BugzillaStatus(IStatus.INFO, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.REPOSITORY_LOGGED_OUT, "You have been logged out. Please retry operation.")); //$NON-NLS-1$ } } for (Iterator<String> iterator = bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_CONFIRM_MATCH) .iterator(); iterator.hasNext() && !found;) { String value = iterator.next().toLowerCase(Locale.ENGLISH); found = found || title.indexOf(value) != -1; } if (found) { BugzillaUserMatchResponse matchResponse = new BugzillaUserMatchResponse(); matchResponse.parseResultConfirmMatch(tokenizer, repositoryUrl.toString(), body); } found = false; for (Iterator<String> iterator = bugzillaLanguageSettings .getResponseForCommand(BugzillaLanguageSettings.COMMAND_ERROR_MATCH_FAILED) .iterator(); iterator.hasNext() && !found;) { String value = iterator.next().toLowerCase(Locale.ENGLISH); found = found || title.indexOf(value) != -1; } if (found) { BugzillaUserMatchResponse matchResponse = new BugzillaUserMatchResponse(); matchResponse.parseResultMatchFailed(tokenizer, repositoryUrl.toString(), body); } isTitle = false; } } else { if (isErrorMessageToken(token)) { errorMessage = computeErrorMessage(tokenizer, token); break; } } } if (hasAuthenticationCredentials() && !loggedIn) { // None of the usual errors occurred. Log what cookies were // received to aid authentication debugging StringBuilder builder = new StringBuilder("Cookies: "); //$NON-NLS-1$ for (Cookie cookie : httpClient.getState().getCookies()) { builder.append(cookie.getName() + " = " + cookie.getValue() + " "); //$NON-NLS-1$ //$NON-NLS-2$ } StatusHandler.log( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, UNKNOWN_REPOSITORY_ERROR + body)); StatusHandler.log(new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, builder.toString())); } String result = title.trim(); if (errorMessage != null) { if (result.length() > 0) { result = result + ": " + errorMessage; //$NON-NLS-1$ } else { result = errorMessage; } throw new CoreException(RepositoryStatus.createHtmlStatus(repositoryUrl.toString(), IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY, result, body)); } if (result.length() == 0) { if (body.contains("Bugzilla/Bug.pm line")) { //$NON-NLS-1$ result = "Bugzilla/Bug.pm line"; //$NON-NLS-1$ } } RepositoryStatus status = RepositoryStatus.createHtmlStatus(repositoryUrl.toString(), IStatus.INFO, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY, UNKNOWN_REPOSITORY_ERROR + result, body); throw new CoreException(status); } catch (ParseException e) { loggedIn = false; throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_INTERNAL, "Unable to parse response from " + repositoryUrl.toString() + ".")); //$NON-NLS-1$ //$NON-NLS-2$ } finally { in.close(); } } public void getTaskData(Set<String> taskIds, final TaskDataCollector collector, final TaskAttributeMapper mapper, final IProgressMonitor monitor) throws IOException, CoreException { if (repositoryConfiguration == null) { getRepositoryConfiguration(new SubProgressMonitor(monitor, 1), null); connector.addRepositoryConfiguration(repositoryConfiguration); } GzipPostMethod method = null; HashMap<String, TaskData> taskDataMap = new HashMap<String, TaskData>(); // make a copy to modify set taskIds = new HashSet<String>(taskIds); int authenticationAttempt = 0; while (taskIds.size() > 0) { try { Set<String> idsToRetrieve = new HashSet<String>(); Iterator<String> itr = taskIds.iterator(); for (int x = 0; itr.hasNext() && x < MAX_RETRIEVED_PER_QUERY; x++) { String taskId = itr.next(); String taskIdOrg = taskId; // remove leading zeros boolean changed = false; while (taskId.startsWith("0")) { //$NON-NLS-1$ taskId = taskId.substring(1); changed = true; } idsToRetrieve.add(taskId); if (changed) { taskIds.remove(taskIdOrg); taskIds.add(taskId); } } NameValuePair[] formData = new NameValuePair[idsToRetrieve.size() + 2]; if (idsToRetrieve.size() == 0) { return; } itr = idsToRetrieve.iterator(); int x = 0; for (; itr.hasNext(); x++) { String taskId = itr.next(); formData[x] = new NameValuePair("id", taskId); //$NON-NLS-1$ TaskData taskData = new TaskData(mapper, getConnectorKind(), repositoryUrl.toString(), taskId); setupExistingBugAttributes(repositoryUrl.toString(), taskData); taskDataMap.put(taskId, taskData); } formData[x++] = new NameValuePair("ctype", "xml"); //$NON-NLS-1$ //$NON-NLS-2$ formData[x] = new NameValuePair("excludefield", "attachmentdata"); //$NON-NLS-1$ //$NON-NLS-2$ method = postFormData(IBugzillaConstants.URL_POST_SHOW_BUG, formData, monitor); if (method == null) { throw new IOException("Could not post form, client returned null method."); //$NON-NLS-1$ } boolean parseable = false; if (method.getResponseHeader("Content-Type") != null) { //$NON-NLS-1$ Header responseTypeHeader = method.getResponseHeader("Content-Type"); //$NON-NLS-1$ for (String type : VALID_CONFIG_CONTENT_TYPES) { if (responseTypeHeader.getValue().toLowerCase(Locale.ENGLISH).contains(type)) { InputStream input = getResponseStream(method, monitor); try { MultiBugReportFactory factory = new MultiBugReportFactory(input, getCharacterEncoding(), connector); List<BugzillaCustomField> customFields = new ArrayList<BugzillaCustomField>(); if (repositoryConfiguration != null) { customFields = repositoryConfiguration.getCustomFields(); } factory.populateReport(taskDataMap, collector, mapper, customFields); taskIds.removeAll(idsToRetrieve); taskDataMap.clear(); parseable = true; break; } finally { input.close(); } } } } if (!parseable) { // because html is not a valid config content type it is // save to get the response here throw new CoreException(parseHtmlError(getResponseStream(method, monitor))); } } catch (CoreException c) { if (c.getStatus().getCode() == RepositoryStatus.ERROR_REPOSITORY_LOGIN && authenticationAttempt < 1) { loggedIn = false; authenticationAttempt++; // StatusHandler.log(c.getStatus()); } else { throw c; } } finally { if (method != null) { WebUtil.releaseConnection(method, monitor); } } } } protected String getConnectorKind() { return BugzillaCorePlugin.CONNECTOR_KIND; } public String getConfigurationTimestamp(IProgressMonitor monitor) throws CoreException { if (!lastModifiedSupported) { return null; } String lastModified = null; HeadMethod method = null; try { method = connectHead(repositoryUrl + IBugzillaConstants.URL_GET_CONFIG_RDF, monitor); Header lastModifiedHeader = method.getResponseHeader("Last-Modified"); //$NON-NLS-1$ if (lastModifiedHeader != null && lastModifiedHeader.getValue() != null && lastModifiedHeader.getValue().length() > 0) { lastModified = lastModifiedHeader.getValue(); } else { lastModifiedSupported = false; } } catch (Exception e) { lastModifiedSupported = false; throw new CoreException(new Status(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, "Error retrieving configuration timestamp", e)); //$NON-NLS-1$ } finally { if (method != null) { WebUtil.releaseConnection(method, monitor); } } return lastModified; } private HeadMethod connectHead(String requestURL, IProgressMonitor monitor) throws IOException, CoreException { hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); for (int attempt = 0; attempt < 2; attempt++) { // force authentication authenticate(monitor); HeadMethod headMethod = new HeadMethod(WebUtil.getRequestPath(requestURL)); if (requestURL.contains(QUERY_DELIMITER)) { headMethod.setQueryString(requestURL.substring(requestURL.indexOf(QUERY_DELIMITER))); } headMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=" //$NON-NLS-1$ //$NON-NLS-2$ + getCharacterEncoding()); // WARNING!! Setting browser compatability breaks Bugzilla // authentication // getMethod.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); // headMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, // new BugzillaRetryHandler()); headMethod.setDoAuthentication(true); int code; try { code = WebUtil.execute(httpClient, hostConfiguration, headMethod, monitor); } catch (IOException e) { // ignore the response WebUtil.releaseConnection(headMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, repositoryUrl.toString(), e)); } if (code == HttpURLConnection.HTTP_OK) { return headMethod; } else if (code == HttpURLConnection.HTTP_UNAUTHORIZED || code == HttpURLConnection.HTTP_FORBIDDEN) { // ignore the response WebUtil.releaseConnection(headMethod, monitor); loggedIn = false; authenticate(monitor); } else if (code == HttpURLConnection.HTTP_PROXY_AUTH) { loggedIn = false; // ignore the response WebUtil.releaseConnection(headMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "Proxy authentication required")); //$NON-NLS-1$ } else { // ignore the response WebUtil.releaseConnection(headMethod, monitor); throw new CoreException(new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_NETWORK, "Http error: " + HttpStatus.getStatusText(code))); //$NON-NLS-1$ // throw new IOException("HttpClient connection error response // code: " + code); } } throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_REPOSITORY_LOGIN, repositoryUrl.toString(), "All connection attempts to " + repositoryUrl.toString() //$NON-NLS-1$ + " failed. Please verify connection and authentication information.")); //$NON-NLS-1$ } public void setRepositoryConfiguration(RepositoryConfiguration repositoryConfiguration) { this.repositoryConfiguration = repositoryConfiguration; } public RepositoryConfiguration getRepositoryConfiguration() { return repositoryConfiguration; } /** * Break text up into lines so that it is displayed properly in bugzilla */ public static String formatTextToLineWrap(String origText, boolean hardWrap) { if (!hardWrap) { return origText; } else { String newText = ""; //$NON-NLS-1$ while (!origText.equals("")) { //$NON-NLS-1$ int newLine = origText.indexOf('\n'); if (newLine == -1 || newLine > WRAP_LENGTH) { if (origText.length() > WRAP_LENGTH) { int spaceIndex = origText.lastIndexOf(" ", WRAP_LENGTH); //$NON-NLS-1$ if (spaceIndex == -1) { spaceIndex = origText.indexOf(" ", WRAP_LENGTH); //$NON-NLS-1$ if (spaceIndex == -1) { spaceIndex = newLine; } } newText = newText + origText.substring(0, spaceIndex) + "\n"; //$NON-NLS-1$ if (origText.charAt(spaceIndex) == ' ' || origText.charAt(spaceIndex) == '\n') { origText = origText.substring(spaceIndex + 1, origText.length()); } else { origText = origText.substring(spaceIndex, origText.length()); } } else { newText = newText + origText; origText = ""; //$NON-NLS-1$ } } else { newText = newText + origText.substring(0, newLine + 1); origText = origText.substring(newLine + 1, origText.length()); } } return newText; } } private class HtmlInformation { private final Map<String, String> groups; private String token; public HtmlInformation() { groups = new HashMap<String, String>(); } public Map<String, String> getGroups() { return groups; } public void setToken(String token) { this.token = token; } public String getToken() { return token; } } private void parseResultOK(HtmlStreamTokenizer tokenizer, BugzillaRepositoryResponse response) throws IOException, CoreException { String codeString = ""; //$NON-NLS-1$ boolean inBugzillaBody = false; int dlLevel = 0; boolean isDT = false; boolean isCODE = false; String dt1 = ""; //$NON-NLS-1$ String dt2 = ""; //$NON-NLS-1$ try { for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = tokenizer.nextToken()) { if (token.getType() == Token.TAG && ((HtmlTag) (token.getValue())).getTagType() == Tag.DIV) { String idValue = ((HtmlTag) (token.getValue())).getAttribute(KEY_ID); if (idValue != null) { inBugzillaBody = idValue.equals("bugzilla-body"); //$NON-NLS-1$ } else { inBugzillaBody = false; } } if (inBugzillaBody) { if (token.getType() == Token.TAG) { if (((HtmlTag) (token.getValue())).getTagType() == Tag.DL) { if (((HtmlTag) (token.getValue())).isEndTag()) { dlLevel--; } else { dlLevel++; } } else if (((HtmlTag) (token.getValue())).getTagType() == Tag.DT) { isDT = !((HtmlTag) (token.getValue())).isEndTag(); if (isDT) { if (dlLevel == 1) { dt1 = " "; //$NON-NLS-1$ } else if (dlLevel == 2) { dt2 = " "; //$NON-NLS-1$ } } } else if (((HtmlTag) (token.getValue())).getTagType() == Tag.CODE) { if (isCODE) { if (codeString.length() > 0) { codeString = codeString.replace("@", "@"); //$NON-NLS-1$ //$NON-NLS-2$ response.addResponseData(dt1, dt2, codeString); } codeString = ""; //$NON-NLS-1$ } isCODE = !((HtmlTag) (token.getValue())).isEndTag(); } } else { if (isDT) { if (dlLevel == 1) { dt1 += (" " + token.getValue()); //$NON-NLS-1$ } else if (dlLevel == 2) { dt2 += (" " + token.getValue()); //$NON-NLS-1$ } } else if (isCODE) { codeString += ("" + token.getValue()); //$NON-NLS-1$ } } } } } catch (ParseException e) { throw new CoreException( new BugzillaStatus(IStatus.ERROR, BugzillaCorePlugin.ID_PLUGIN, RepositoryStatus.ERROR_INTERNAL, "Unable to parse response from " + repositoryUrl.toString() + ".")); //$NON-NLS-1$ //$NON-NLS-2$ } } /** * Currently only necessary for testing. Allows setting of the descriptor file property. * * @param bugzillaDescriptorFile * @param canonicalPath */ public void setDescriptorFile(String canonicalPath) { configParameters.put(IBugzillaConstants.BUGZILLA_DESCRIPTOR_FILE, canonicalPath); } private BugzillaXmlRpcClient getXmlRpcClient() { boolean useXMLRPC = Boolean.parseBoolean(configParameters.get(IBugzillaConstants.BUGZILLA_USE_XMLRPC)); if (useXMLRPC && xmlRpcClient == null) { WebLocation webLocation = new WebLocation(this.repositoryUrl + "/xmlrpc.cgi"); //$NON-NLS-1$ String username = ""; //$NON-NLS-1$ String password = ""; //$NON-NLS-1$ if (location.getCredentials(AuthenticationType.REPOSITORY) != null) { username = location.getCredentials(AuthenticationType.REPOSITORY).getUserName(); } if (location.getCredentials(AuthenticationType.REPOSITORY) != null) { password = location.getCredentials(AuthenticationType.REPOSITORY).getPassword(); } webLocation.setCredentials(AuthenticationType.REPOSITORY, username, password); xmlRpcClient = new BugzillaXmlRpcClient(webLocation, this); xmlRpcClient.setContentTypeCheckingEnabled(true); } return xmlRpcClient; } public List<BugHistory> getBugHistory(String id, IProgressMonitor monitor) throws CoreException { int bugId = Integer.parseInt(id); try { BugzillaXmlRpcClient client = getXmlRpcClient(); if (client == null) { throw new CoreException( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "XMLRPC is not available")); //$NON-NLS-1$ } return client.getHistory(new Integer[] { bugId }, monitor); } catch (XmlRpcException e) { throw new CoreException( new Status(IStatus.WARNING, BugzillaCorePlugin.ID_PLUGIN, "XMLRPC is not installed")); //$NON-NLS-1$ } } /** * Copies all bytes in the given source stream to the given destination stream. Neither streams are closed. * * @param source * the given source stream * @param destination * the given destination stream * @throws IOException * in case of error */ private static void transferData(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } /** * Returns the given file path with its separator character changed from the given old separator to the given new * separator. * * @param path * a file path * @param oldSeparator * a path separator character * @param newSeparator * a path separator character * @return the file path with its separator character changed from the given old separator to the given new * separator */ public static String changeSeparator(String path, char oldSeparator, char newSeparator) { return path.replace(oldSeparator, newSeparator); } public void downloadXMLTransFile(String transFile, IProgressMonitor monitor) throws IOException, CoreException { monitor = Policy.monitorFor(monitor); String loginUrl = repositoryUrl + "/xml_transition_file.mylyn"; //$NON-NLS-1$ GzipGetMethod method = null; try { method = getConnect(loginUrl, monitor); InputStream input = null; File file = new File(changeSeparator(transFile, '/', File.separatorChar)); file.getParentFile().mkdirs(); FileOutputStream output = new FileOutputStream(transFile); input = getResponseStream(method, monitor); transferData(input, output); } finally { if (method != null) { WebUtil.releaseConnection(method, monitor); } } } public BugzillaRepositoryConnector getConnector() { return connector; } public TaskRepository getTaskRepository() { if (location instanceof TaskRepositoryLocation) { return ((TaskRepositoryLocation) location).getTaskRepository(); } return null; } }