001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.config; 021 022import com.google.common.annotations.VisibleForTesting; 023import com.google.common.collect.Maps; 024import org.apache.commons.codec.binary.Base64; 025import org.apache.commons.io.IOUtils; 026import org.apache.commons.lang.StringUtils; 027import org.sonar.api.utils.DateUtils; 028 029import javax.annotation.Nullable; 030 031import java.io.IOException; 032import java.io.StringReader; 033import java.nio.charset.StandardCharsets; 034import java.util.Calendar; 035import java.util.Date; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039 040/** 041 * SonarSource license. This class aims to extract metadata but not to validate or - of course - 042 * to generate license 043 * 044 * @since 3.0 045 */ 046public final class License { 047 private String product; 048 private String organization; 049 private String expirationDate; 050 private String type; 051 private String server; 052 private Map<String, String> additionalProperties; 053 054 private License(Map<String, String> properties) { 055 this.additionalProperties = new HashMap<>(properties); 056 product = StringUtils.defaultString(get("Product", properties), get("Plugin", properties)); 057 organization = StringUtils.defaultString(get("Organisation", properties), get("Name", properties)); 058 expirationDate = StringUtils.defaultString(get("Expiration", properties), get("Expires", properties)); 059 type = get("Type", properties); 060 server = get("Server", properties); 061 // SONAR-4340 Don't expose Digest and Obeo properties 062 additionalProperties.remove("Digest"); 063 additionalProperties.remove("Obeo"); 064 } 065 066 private String get(String key, Map<String, String> properties) { 067 additionalProperties.remove(key); 068 return properties.get(key); 069 } 070 071 /** 072 * Get additional properties available on this license (like threshold conditions) 073 * @since 3.6 074 */ 075 public Map<String, String> additionalProperties() { 076 return additionalProperties; 077 } 078 079 @Nullable 080 public String getProduct() { 081 return product; 082 } 083 084 @Nullable 085 public String getOrganization() { 086 return organization; 087 } 088 089 @Nullable 090 public String getExpirationDateAsString() { 091 return expirationDate; 092 } 093 094 @Nullable 095 public Date getExpirationDate() { 096 return DateUtils.parseDateQuietly(expirationDate); 097 } 098 099 public boolean isExpired() { 100 return isExpired(new Date()); 101 } 102 103 @VisibleForTesting 104 boolean isExpired(Date now) { 105 Date date = getExpirationDate(); 106 if (date == null) { 107 return false; 108 } 109 // SONAR-6079 include last day 110 Calendar cal = Calendar.getInstance(); 111 cal.setTime(date); 112 cal.add(Calendar.DAY_OF_MONTH, 1); 113 cal.add(Calendar.SECOND, -1); 114 return now.after(cal.getTime()); 115 } 116 117 @Nullable 118 public String getType() { 119 return type; 120 } 121 122 @Nullable 123 public String getServer() { 124 return server; 125 } 126 127 public static License readBase64(String base64) { 128 return readPlainText(new String(Base64.decodeBase64(base64.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)); 129 } 130 131 @VisibleForTesting 132 static License readPlainText(String data) { 133 Map<String, String> props = Maps.newHashMap(); 134 StringReader reader = new StringReader(data); 135 try { 136 List<String> lines = IOUtils.readLines(reader); 137 for (String line : lines) { 138 if (StringUtils.isNotBlank(line) && line.indexOf(':') > 0) { 139 String key = StringUtils.substringBefore(line, ":"); 140 String value = StringUtils.substringAfter(line, ":"); 141 props.put(StringUtils.trimToEmpty(key), StringUtils.trimToEmpty(value)); 142 } 143 } 144 145 } catch (IOException e) { 146 // silently ignore 147 148 } finally { 149 IOUtils.closeQuietly(reader); 150 } 151 return new License(props); 152 } 153}