/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Dapplo.HttpExtensions; using Dapplo.HttpExtensions.Extensions; using Dapplo.Jira; using Dapplo.Jira.Converters; using Dapplo.Jira.Entities; using Greenshot.IniFile; using GreenshotPlugin.Core; namespace GreenshotJiraPlugin { /// /// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed /// public class JiraConnector : IDisposable { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector)); private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); // Used to remove the wsdl information from the old SOAP Uri public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl"; private IJiraClient _jiraClient; private IssueTypeBitmapCache _issueTypeBitmapCache; /// /// Initialize some basic stuff, in the case the SVG to bitmap converter /// static JiraConnector() { CoreConfig.PropertyChanged += (sender, args) => { if (args.PropertyName == nameof(CoreConfig.IconSize)) { JiraPlugin.Instance.JiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height }); } }; } /// /// Dispose, logout the users /// public void Dispose() { if (_jiraClient != null) { Logout(); } FavIcon?.Dispose(); } /// /// Constructor /// public JiraConnector() { JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, ""); } /// /// Access the jira monitor /// public JiraMonitor Monitor { get; private set; } public Bitmap FavIcon { get; private set; } /// /// Internal login which catches the exceptions /// /// true if login was done successfully private async Task DoLoginAsync(string user, string password, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password)) { return false; } _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); _jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height }); _jiraClient.SetBasicAuthentication(user, password); _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient); try { Monitor = new JiraMonitor(); await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken); var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico"); try { FavIcon = await _jiraClient.Server.GetUriContentAsync(favIconUri, cancellationToken); } catch (Exception ex) { Log.WarnFormat("Couldn't load favicon from {0}", favIconUri); Log.Warn("Exception details: ", ex); } } catch (Exception ex2) { Log.WarnFormat("Couldn't connect to JIRA {0}", JiraConfig.Url); Log.Warn("Exception details: ", ex2); return false; } return true; } /// /// Use the credentials dialog, this will show if there are not correct credentials. /// If there are credentials, call the real login. /// /// Task public async Task LoginAsync(CancellationToken cancellationToken = default) { Logout(); try { // Get the system name, so the user knows where to login to var credentialsDialog = new CredentialsDialog(JiraConfig.Url) { Name = null }; while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) { if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) { if (credentialsDialog.SaveChecked) { credentialsDialog.Confirm(true); } IsLoggedIn = true; return; } // Login failed, confirm this try { credentialsDialog.Confirm(false); } catch (ApplicationException e) { // exception handling ... Log.Error("Problem using the credentials dialog", e); } // For every windows version after XP show an incorrect password baloon credentialsDialog.IncorrectPassword = true; // Make sure the dialog is display, the password was false! credentialsDialog.AlwaysDisplay = true; } } catch (ApplicationException e) { // exception handling ... Log.Error("Problem using the credentials dialog", e); } } /// /// End the session, if there was one /// public void Logout() { if (_jiraClient == null || !IsLoggedIn) return; Monitor.Dispose(); IsLoggedIn = false; } /// /// check the login credentials, to prevent timeouts of the session, or makes a login /// Do not use ConfigureAwait to call this, as it will move await from the UI thread. /// /// private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default) { if (!IsLoggedIn) { await LoginAsync(cancellationToken); } } /// /// Get the favorite filters /// /// List with filters public async Task> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default) { await CheckCredentialsAsync(cancellationToken); return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false); } /// /// Get the issue for a key /// /// Jira issue key /// CancellationToken /// Issue public async Task GetIssueAsync(string issueKey, CancellationToken cancellationToken = default) { await CheckCredentialsAsync(cancellationToken); try { return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken).ConfigureAwait(false); } catch { return null; } } /// /// Attach the content to the jira /// /// /// IBinaryContainer /// /// public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default) { await CheckCredentialsAsync(cancellationToken); using var memoryStream = new MemoryStream(); content.WriteToStream(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); await _jiraClient.Attachment.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false); } /// /// Add a comment to the supplied issue /// /// Jira issue key /// text /// the visibility role /// CancellationToken public async Task AddCommentAsync(string issueKey, string body, string visibility = null, CancellationToken cancellationToken = default) { await CheckCredentialsAsync(cancellationToken); await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false); } /// /// Get the search results for the specified filter /// /// Filter /// /// public async Task> SearchAsync(Filter filter, CancellationToken cancellationToken = default) { await CheckCredentialsAsync(cancellationToken); var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, 20, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, cancellationToken).ConfigureAwait(false); return searchResult.Issues; } /// /// Get the bitmap representing the issue type of an issue, from cache. /// /// Issue /// CancellationToken /// Bitmap public async Task GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default) { return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false); } /// /// Get the base uri /// public Uri JiraBaseUri => _jiraClient.JiraBaseUri; /// /// Is the user "logged in? /// public bool IsLoggedIn { get; private set; } } }