Merge branch 'stable-2.12'
* stable-2.12: Add 2.12 release notes to release notes index Make email validation case insensitive Add tooltip with subject message in related changes tab Correct timezone logged for DST changes Add hmac-sha2-256 and hmac-sha2-512 as MACs for sshd Fix double slash on URL when switching account. commit-msg: Do not add Change-Id to temp commits Use image instead of Unicode Character for Copy Button Change-Id: I6dc9c93e0962a19e2c32dbcf86f0389b5e2a7acc
This commit is contained in:
@@ -3515,7 +3515,8 @@ configuration file, one MAC per key. MAC names starting with `+`
|
|||||||
are enabled in addition to the default MACs, MAC names starting with
|
are enabled in addition to the default MACs, MAC names starting with
|
||||||
`-` are removed from the default MACs.
|
`-` are removed from the default MACs.
|
||||||
+
|
+
|
||||||
Supported MACs: hmac-md5, hmac-md5-96, hmac-sha1, hmac-sha1-96.
|
Supported MACs: hmac-md5, hmac-md5-96, hmac-sha1, hmac-sha1-96,
|
||||||
|
hmac-sha2-256, hmac-sha2-512.
|
||||||
+
|
+
|
||||||
By default, all supported MACs are available.
|
By default, all supported MACs are available.
|
||||||
|
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
Gerrit Code Review - Release Notes
|
Gerrit Code Review - Release Notes
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
[[2_12]]
|
||||||
|
Version 2.12.x
|
||||||
|
--------------
|
||||||
|
* link:ReleaseNotes-2.12.html[2.12]
|
||||||
|
|
||||||
[[2_11]]
|
[[2_11]]
|
||||||
Version 2.11.x
|
Version 2.11.x
|
||||||
--------------
|
--------------
|
||||||
|
@@ -7,6 +7,7 @@ gwt_module(
|
|||||||
resources = [
|
resources = [
|
||||||
SRC + 'clippy/client/clippy.css',
|
SRC + 'clippy/client/clippy.css',
|
||||||
SRC + 'clippy/client/clippy.swf',
|
SRC + 'clippy/client/clippy.swf',
|
||||||
|
SRC + 'clippy/client/clipboard-16.png',
|
||||||
SRC + 'clippy/client/CopyableLabelText.properties',
|
SRC + 'clippy/client/CopyableLabelText.properties',
|
||||||
],
|
],
|
||||||
provided_deps = ['//lib/gwt:user'],
|
provided_deps = ['//lib/gwt:user'],
|
||||||
@@ -14,6 +15,7 @@ gwt_module(
|
|||||||
':SafeHtml',
|
':SafeHtml',
|
||||||
':UserAgent',
|
':UserAgent',
|
||||||
'//lib:LICENSE-clippy',
|
'//lib:LICENSE-clippy',
|
||||||
|
'//lib:LICENSE-drifty',
|
||||||
],
|
],
|
||||||
visibility = ['PUBLIC'],
|
visibility = ['PUBLIC'],
|
||||||
)
|
)
|
||||||
|
@@ -18,6 +18,7 @@ import com.google.gwt.core.client.GWT;
|
|||||||
import com.google.gwt.resources.client.ClientBundle;
|
import com.google.gwt.resources.client.ClientBundle;
|
||||||
import com.google.gwt.resources.client.DataResource;
|
import com.google.gwt.resources.client.DataResource;
|
||||||
import com.google.gwt.resources.client.DataResource.DoNotEmbed;
|
import com.google.gwt.resources.client.DataResource.DoNotEmbed;
|
||||||
|
import com.google.gwt.resources.client.ImageResource;
|
||||||
|
|
||||||
public interface ClippyResources extends ClientBundle {
|
public interface ClippyResources extends ClientBundle {
|
||||||
public static final ClippyResources I = GWT.create(ClippyResources.class);
|
public static final ClippyResources I = GWT.create(ClippyResources.class);
|
||||||
@@ -28,4 +29,7 @@ public interface ClippyResources extends ClientBundle {
|
|||||||
@Source("clippy.swf")
|
@Source("clippy.swf")
|
||||||
@DoNotEmbed
|
@DoNotEmbed
|
||||||
DataResource swf();
|
DataResource swf();
|
||||||
|
|
||||||
|
@Source("clipboard-16.png")
|
||||||
|
ImageResource clipboard();
|
||||||
}
|
}
|
||||||
|
@@ -119,7 +119,12 @@ public class CopyableLabel extends Composite implements HasText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (UserAgent.hasJavaScriptClipboard()) {
|
if (UserAgent.hasJavaScriptClipboard()) {
|
||||||
copier = new Button("📋"); // CLIPBOARD
|
copier = new Button(new SafeHtmlBuilder()
|
||||||
|
.openElement("img")
|
||||||
|
.setAttribute("src", ClippyResources.I.clipboard().getSafeUri().asString())
|
||||||
|
.setWidth(14)
|
||||||
|
.setHeight(14)
|
||||||
|
.closeSelf());
|
||||||
copier.setStyleName(ClippyResources.I.css().copier());
|
copier.setStyleName(ClippyResources.I.css().copier());
|
||||||
Tooltip.addStyle(copier);
|
Tooltip.addStyle(copier);
|
||||||
Tooltip.setLabel(copier, CopyableLabelText.I.tooltip());
|
Tooltip.setLabel(copier, CopyableLabelText.I.tooltip());
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 623 B |
@@ -54,7 +54,7 @@ public class UserPopupPanel extends PopupPanel {
|
|||||||
switchAccount.setHref(Gerrit.info().auth().switchAccountUrl());
|
switchAccount.setHref(Gerrit.info().auth().switchAccountUrl());
|
||||||
} else if (Gerrit.info().auth().isDev()
|
} else if (Gerrit.info().auth().isDev()
|
||||||
|| Gerrit.info().auth().isOpenId()) {
|
|| Gerrit.info().auth().isOpenId()) {
|
||||||
switchAccount.setHref(Gerrit.selfRedirect("/login/"));
|
switchAccount.setHref(Gerrit.selfRedirect("/login"));
|
||||||
} else {
|
} else {
|
||||||
switchAccount.removeFromParent();
|
switchAccount.removeFromParent();
|
||||||
switchAccount = null;
|
switchAccount = null;
|
||||||
|
@@ -292,6 +292,7 @@ class RelatedChangesTab implements IsWidget {
|
|||||||
if (url.startsWith("#")) {
|
if (url.startsWith("#")) {
|
||||||
sb.setAttribute("onclick", OPEN);
|
sb.setAttribute("onclick", OPEN);
|
||||||
}
|
}
|
||||||
|
sb.setAttribute("title", info.commit().subject());
|
||||||
if (showProjects) {
|
if (showProjects) {
|
||||||
sb.append(info.project()).append(": ");
|
sb.append(info.project()).append(": ");
|
||||||
}
|
}
|
||||||
|
@@ -196,7 +196,8 @@ public class IdentifiedUser extends CurrentUser {
|
|||||||
private final GroupBackend groupBackend;
|
private final GroupBackend groupBackend;
|
||||||
private final String anonymousCowardName;
|
private final String anonymousCowardName;
|
||||||
private final Boolean disableReverseDnsLookup;
|
private final Boolean disableReverseDnsLookup;
|
||||||
private final Set<String> validEmails = Sets.newHashSetWithExpectedSize(4);
|
private final Set<String> validEmails =
|
||||||
|
Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Provider<SocketAddress> remotePeerProvider;
|
private final Provider<SocketAddress> remotePeerProvider;
|
||||||
@@ -284,7 +285,7 @@ public class IdentifiedUser extends CurrentUser {
|
|||||||
validEmails.add(email);
|
validEmails.add(email);
|
||||||
return true;
|
return true;
|
||||||
} else if (invalidEmails == null) {
|
} else if (invalidEmails == null) {
|
||||||
invalidEmails = Sets.newHashSetWithExpectedSize(4);
|
invalidEmails = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
|
||||||
}
|
}
|
||||||
invalidEmails.add(email);
|
invalidEmails.add(email);
|
||||||
return false;
|
return false;
|
||||||
|
@@ -25,7 +25,6 @@ import com.google.inject.Inject;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** Basic implementation of {@link Realm}. */
|
/** Basic implementation of {@link Realm}. */
|
||||||
@@ -57,7 +56,7 @@ public abstract class AbstractRealm implements Realm {
|
|||||||
@Override
|
@Override
|
||||||
public boolean hasEmailAddress(IdentifiedUser user, String email) {
|
public boolean hasEmailAddress(IdentifiedUser user, String email) {
|
||||||
for (AccountExternalId ext : user.state().getExternalIds()) {
|
for (AccountExternalId ext : user.state().getExternalIds()) {
|
||||||
if (Objects.equals(ext.getEmailAddress(), email)) {
|
if (email != null && email.equalsIgnoreCase(ext.getEmailAddress())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,12 @@ add_ChangeId() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Do not add Change-Id to temp commits
|
||||||
|
if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!'
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
if test "false" = "`git config --bool --get gerrit.createChangeId`"
|
if test "false" = "`git config --bool --get gerrit.createChangeId`"
|
||||||
then
|
then
|
||||||
return
|
return
|
||||||
|
@@ -0,0 +1,131 @@
|
|||||||
|
// Copyright (C) 2015 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.inject.Scopes.SINGLETON;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.TimeUtil;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
|
import com.google.gerrit.server.account.CapabilityControl;
|
||||||
|
import com.google.gerrit.server.account.FakeRealm;
|
||||||
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
|
import com.google.gerrit.server.account.Realm;
|
||||||
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
|
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||||
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
|
import com.google.gerrit.server.config.DisableReverseDnsLookup;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
|
import com.google.gerrit.testutil.ConfigSuite;
|
||||||
|
import com.google.gerrit.testutil.FakeAccountCache;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.util.Providers;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@RunWith(ConfigSuite.class)
|
||||||
|
public class IdentifiedUserTest {
|
||||||
|
@ConfigSuite.Parameter
|
||||||
|
public Config config;
|
||||||
|
|
||||||
|
private IdentifiedUser identifiedUser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifiedUser.GenericFactory identifiedUserFactory;
|
||||||
|
|
||||||
|
private static final String[] TEST_CASES = {
|
||||||
|
"",
|
||||||
|
"FirstName.LastName@Corporation.com",
|
||||||
|
"!#$%&'+-/=.?^`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]"
|
||||||
|
};
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
final FakeAccountCache accountCache = new FakeAccountCache();
|
||||||
|
final Realm mockRealm = new FakeRealm() {
|
||||||
|
HashSet<String> emails = new HashSet<>(Arrays.asList(TEST_CASES));
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasEmailAddress(IdentifiedUser who, String email) {
|
||||||
|
return emails.contains(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getEmailAddresses(IdentifiedUser who) {
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AbstractModule mod = new AbstractModule() {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
|
||||||
|
.toInstance(Boolean.FALSE);
|
||||||
|
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(config);
|
||||||
|
bind(String.class).annotatedWith(AnonymousCowardName.class)
|
||||||
|
.toProvider(AnonymousCowardNameProvider.class);
|
||||||
|
bind(String.class).annotatedWith(CanonicalWebUrl.class)
|
||||||
|
.toInstance("http://localhost:8080/");
|
||||||
|
bind(AccountCache.class).toInstance(accountCache);
|
||||||
|
bind(GroupBackend.class).to(SystemGroupBackend.class).in(SINGLETON);
|
||||||
|
bind(CapabilityControl.Factory.class)
|
||||||
|
.toProvider(Providers.<CapabilityControl.Factory>of(null));
|
||||||
|
bind(Realm.class).toInstance(mockRealm);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Injector injector = Guice.createInjector(mod);
|
||||||
|
injector.injectMembers(this);
|
||||||
|
|
||||||
|
Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
|
||||||
|
Account.Id ownerId = account.getId();
|
||||||
|
|
||||||
|
identifiedUser = identifiedUserFactory.create(ownerId);
|
||||||
|
|
||||||
|
/* Trigger identifiedUser to load the email addresses from mockRealm */
|
||||||
|
identifiedUser.getEmailAddresses();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmailsExistence() {
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[0])).isTrue();
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[1].toLowerCase())).isTrue();
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[1])).isTrue();
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[1].toUpperCase())).isTrue();
|
||||||
|
/* assert again to test cached email address by IdentifiedUser.validEmails */
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[1])).isTrue();
|
||||||
|
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[2])).isTrue();
|
||||||
|
assertThat(identifiedUser.hasEmailAddress(TEST_CASES[2].toLowerCase())).isTrue();
|
||||||
|
|
||||||
|
|
||||||
|
assertThat(identifiedUser.hasEmailAddress("non-exist@email.com")).isFalse();
|
||||||
|
/* assert again to test cached email address by IdentifiedUser.invalidEmails */
|
||||||
|
assertThat(identifiedUser.hasEmailAddress("non-exist@email.com")).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -83,6 +83,8 @@ import org.apache.sshd.common.mac.HMACMD5;
|
|||||||
import org.apache.sshd.common.mac.HMACMD596;
|
import org.apache.sshd.common.mac.HMACMD596;
|
||||||
import org.apache.sshd.common.mac.HMACSHA1;
|
import org.apache.sshd.common.mac.HMACSHA1;
|
||||||
import org.apache.sshd.common.mac.HMACSHA196;
|
import org.apache.sshd.common.mac.HMACSHA196;
|
||||||
|
import org.apache.sshd.common.mac.HMACSHA256;
|
||||||
|
import org.apache.sshd.common.mac.HMACSHA512;
|
||||||
import org.apache.sshd.common.random.BouncyCastleRandom;
|
import org.apache.sshd.common.random.BouncyCastleRandom;
|
||||||
import org.apache.sshd.common.random.JceRandom;
|
import org.apache.sshd.common.random.JceRandom;
|
||||||
import org.apache.sshd.common.random.SingletonRandomFactory;
|
import org.apache.sshd.common.random.SingletonRandomFactory;
|
||||||
@@ -552,9 +554,13 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initMacs(final Config cfg) {
|
private void initMacs(final Config cfg) {
|
||||||
setMacFactories(filter(cfg, "mac", new HMACMD5.Factory(),
|
setMacFactories(filter(cfg, "mac",
|
||||||
new HMACSHA1.Factory(), new HMACMD596.Factory(),
|
new HMACMD5.Factory(),
|
||||||
new HMACSHA196.Factory()));
|
new HMACSHA1.Factory(),
|
||||||
|
new HMACMD596.Factory(),
|
||||||
|
new HMACSHA196.Factory(),
|
||||||
|
new HMACSHA256.Factory(),
|
||||||
|
new HMACSHA512.Factory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
|
@@ -20,7 +20,6 @@ import org.eclipse.jgit.util.QuotedString;
|
|||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
public final class SshLogLayout extends Layout {
|
public final class SshLogLayout extends Layout {
|
||||||
@@ -36,15 +35,15 @@ public final class SshLogLayout extends Layout {
|
|||||||
private final Calendar calendar;
|
private final Calendar calendar;
|
||||||
private long lastTimeMillis;
|
private long lastTimeMillis;
|
||||||
private final char[] lastTimeString = new char[20];
|
private final char[] lastTimeString = new char[20];
|
||||||
private final char[] timeZone;
|
private final SimpleDateFormat tzFormat;
|
||||||
|
private char[] timeZone;
|
||||||
|
|
||||||
public SshLogLayout() {
|
public SshLogLayout() {
|
||||||
final TimeZone tz = TimeZone.getDefault();
|
final TimeZone tz = TimeZone.getDefault();
|
||||||
calendar = Calendar.getInstance(tz);
|
calendar = Calendar.getInstance(tz);
|
||||||
|
|
||||||
final SimpleDateFormat sdf = new SimpleDateFormat("Z");
|
tzFormat = new SimpleDateFormat("Z");
|
||||||
sdf.setTimeZone(tz);
|
tzFormat.setTimeZone(tz);
|
||||||
timeZone = sdf.format(new Date()).toCharArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,8 +52,6 @@ public final class SshLogLayout extends Layout {
|
|||||||
|
|
||||||
buf.append('[');
|
buf.append('[');
|
||||||
formatDate(event.getTimeStamp(), buf);
|
formatDate(event.getTimeStamp(), buf);
|
||||||
buf.append(' ');
|
|
||||||
buf.append(timeZone);
|
|
||||||
buf.append(']');
|
buf.append(']');
|
||||||
|
|
||||||
req(P_SESSION, buf, event);
|
req(P_SESSION, buf, event);
|
||||||
@@ -94,11 +91,14 @@ public final class SshLogLayout extends Layout {
|
|||||||
sbuf.append(',');
|
sbuf.append(',');
|
||||||
sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
|
sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
|
||||||
lastTimeMillis = rounded;
|
lastTimeMillis = rounded;
|
||||||
|
timeZone = tzFormat.format(calendar.getTime()).toCharArray();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sbuf.append(lastTimeString);
|
sbuf.append(lastTimeString);
|
||||||
}
|
}
|
||||||
sbuf.append(String.format("%03d", millis));
|
sbuf.append(String.format("%03d", millis));
|
||||||
|
sbuf.append(' ');
|
||||||
|
sbuf.append(timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toTwoDigits(int input) {
|
private String toTwoDigits(int input) {
|
||||||
|
1
lib/BUCK
1
lib/BUCK
@@ -12,6 +12,7 @@ define_license(name = 'bouncycastle')
|
|||||||
define_license(name = 'clippy')
|
define_license(name = 'clippy')
|
||||||
define_license(name = 'codemirror')
|
define_license(name = 'codemirror')
|
||||||
define_license(name = 'diffy')
|
define_license(name = 'diffy')
|
||||||
|
define_license(name = 'drifty')
|
||||||
define_license(name = 'freebie_application_icon_set')
|
define_license(name = 'freebie_application_icon_set')
|
||||||
define_license(name = 'h2')
|
define_license(name = 'h2')
|
||||||
define_license(name = 'jgit')
|
define_license(name = 'jgit')
|
||||||
|
21
lib/LICENSE-drifty
Normal file
21
lib/LICENSE-drifty
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Drifty (http://drifty.com/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
Reference in New Issue
Block a user