All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kitteh.irc.client.library.feature.CapabilityManager Maven / Gradle / Ivy

/*
 * * Copyright (C) 2013-2023 Matt Baxter https://kitteh.org
 *
 * 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.
 */
package org.kitteh.irc.client.library.feature;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.kitteh.irc.client.library.command.CapabilityRequestCommand;
import org.kitteh.irc.client.library.element.CapabilityState;
import org.kitteh.irc.client.library.element.Channel;
import org.kitteh.irc.client.library.element.MessageTag;
import org.kitteh.irc.client.library.element.ServerMessage;
import org.kitteh.irc.client.library.element.User;
import org.kitteh.irc.client.library.event.channel.ChannelInviteEvent;
import org.kitteh.irc.client.library.event.user.UserHostnameChangeEvent;
import org.kitteh.irc.client.library.event.user.UserUserStringChangeEvent;
import org.kitteh.irc.client.library.feature.auth.SaslEcdsaNist256PChallenge;
import org.kitteh.irc.client.library.feature.auth.SaslPlain;
import org.kitteh.irc.client.library.util.Resettable;
import org.kitteh.irc.client.library.util.RiskyBusiness;
import org.kitteh.irc.client.library.util.Sanity;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Provides information on IRCv3 extensions available and in use.
 */
public interface CapabilityManager {
    /**
     * Contains the capabilities natively supported by KICL, which will be
     * requested automatically upon availability. Defaults defined as
     * transient are not requested unless additional functionality is
     * enabled, as documented here.
     */
    final class Defaults {
        /**
         * Account change notification.
         *
         * @see User#getAccount()
         */
        public static final String ACCOUNT_NOTIFY = "account-notify";

        /**
         * Account message tags.
         *
         * @see User#getAccount()
         */
        public static final String ACCOUNT_TAG = "account-tag";

        /**
         * Away notification.
         *
         * @see User#isAway()
         */
        public static final String AWAY_NOTIFY = "away-notify";

        /**
         * Batched messages.
         */
        public static final String BATCH = "batch";

        /**
         * Capability change notification. Implicitly enabled by the server
         * when "CAP LS 302" (or higher version) is sent and therefore it
         * is not requested by the default capability manager.
         */
        public static final transient String CAP_NOTIFY = "cap-notify";

        /**
         * Self-sent message echoing, not utilized unless requested.
         */
        public static final transient String ECHO_MESSAGE = "echo-message";

        /**
         * Account listed in join message.
         *
         * @see User#getAccount()
         */
        public static final String EXTENDED_JOIN = "extended-join";

        /**
         * Extended monitoring, getting AWAY, ACCOUNT, CHGHOST, AND SETNAME notifications for a MONITOR target.
         */
        public static final String EXTENDED_MONITOR = "extended-monitor";

        /**
         * Invite notification, not utilized unless requested.
         *
         * @see ChannelInviteEvent
         */
        public static final transient String INVITE_NOTIFY = "invite-notify";

        /**
         * Labeled responses, which also requires the {@link #BATCH}
         * capability to function, via a "label" message id.
         */
        public static final String LABELED_RESPONSE = "labeled-response";

        /**
         * Message tags support, explicitly stating that client-only tags are
         * supported, required for msgid tag but not a necessary capability
         * for actually supporting tags through other capabilities like
         * account-tag, batch, or server-time.
         */
        public static final String MESSAGE_TAGS = "message-tags";

        /**
         * Multiple prefixes sent in NAMES and WHO output.
         *
         * @see Channel#getUserModes
         */
        public static final String MULTI_PREFIX = "multi-prefix";

        /**
         * Server time message tag.
         *
         * @see ServerMessage#getTags()
         * @see MessageTag.Time
         */
        public static final String SERVER_TIME = "server-time";

        /**
         * Setting realname (GECOS) without disconnecting.
         */
        public static final String SETNAME = "setname";

        /**
         * The chghost extension, allows users to change user string or
         * hostname.
         *
         * @see UserHostnameChangeEvent
         * @see UserUserStringChangeEvent
         */
        public static final String CHGHOST = "chghost";

        /**
         * SASL authentication, not utilized unless a SASL authentication
         * protocol is enabled.
         *
         * @see SaslPlain
         * @see SaslEcdsaNist256PChallenge
         */
        public static final transient String SASL = "sasl";

        /**
         * User hosts sent in NAMES, allowing User creation prior to WHO.
         */
        public static final String USERHOST_IN_NAMES = "userhost-in-names";

        private static final List DEFAULTS;
        private static final Supplier> SUPPLIER = ArrayList::new;

        private Defaults() {
        }

        /**
         * Gets all capabilities requested by KICL by default.
         *
         * @return all capability names
         */
        public static List getDefaults() {
            return Defaults.DEFAULTS;
        }

        static {
            DEFAULTS = Collections.unmodifiableList(Arrays.stream(Defaults.class.getDeclaredFields())
                    .filter(field -> Modifier.isPublic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers()))
                    .map(Defaults::getStringForCapabilityField).collect(Collectors.toCollection(Defaults.SUPPLIER)));
        }

        /**
         * Tries to get the String value of a capability field, provided
         * it's accessible.
         *
         * @param field the field to try and get the value for
         * @return the capability extension name
         */
        @SuppressWarnings("ConstantConditions")
        private static String getStringForCapabilityField(@NonNull Field field) {
            return RiskyBusiness.assertSafe(f -> (String) f.get(null), field);
        }
    }

    /**
     * A capability manager with management features.
     */
    interface WithManagement extends CapabilityManager, Resettable {
        /**
         * Gets if we are still in negotiation. True on construction and
         * after a {@link #reset()}.
         *
         * @return true if still negotiating
         */
        boolean isNegotiating();

        /**
         * Ends negotiation status, making {@link #isNegotiating()} false.
         */
        void endNegotiation();

        /**
         * Updates the current active capabilities, adding new and removing
         * any labeled with {@link CapabilityState#isDisabled()}.
         *
         * @param capabilityStates capability states
         */
        void updateCapabilities(@NonNull List capabilityStates);

        /**
         * Wipes the previously known active capabilities, setting only those
         * in the provided list.
         *
         * @param capabilityStates fresh set of capability states
         */
        void setCapabilities(@NonNull List capabilityStates);

        /**
         * Sets the supported capabilities as reported by the server.
         *
         * @param capabilityStates supported capabilities
         */
        void setSupportedCapabilities(@NonNull List capabilityStates);
    }

    /**
     * Gets capabilities currently enabled.
     *
     * @return the capabilities currently enabled
     * @see CapabilityRequestCommand
     */
    @NonNull List getCapabilities();

    /**
     * Gets an enabled capability by name.
     *
     * @param name capability name
     * @return the named capability if enabled
     */
    default @NonNull Optional getCapability(@NonNull String name) {
        Sanity.nullCheck(name, "Name");
        return this.getCapabilities().stream().filter(capabilityState -> capabilityState.getName().equals(name)).findFirst();
    }

    /**
     * Gets capabilities supported by the server.
     *
     * @return the capabilities supported
     * @see CapabilityRequestCommand
     */
    @NonNull List getSupportedCapabilities();

    /**
     * Gets a supported capability by name.
     *
     * @param name capability name
     * @return the named capability if supported
     */
    default @NonNull Optional getSupportedCapability(@NonNull String name) {
        Sanity.nullCheck(name, "Name");
        return this.getSupportedCapabilities().stream().filter(capabilityState -> capabilityState.getName().equals(name)).findFirst();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy