001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.net.finger;
018
019import java.io.BufferedOutputStream;
020import java.io.DataOutputStream;
021import java.io.IOException;
022import java.io.InputStream;
023
024import org.apache.commons.io.Charsets;
025import org.apache.commons.io.IOUtils;
026import org.apache.commons.net.SocketClient;
027
028/**
029 * The FingerClient class implements the client side of the Internet Finger Protocol defined in RFC 1288. To finger a host you create a FingerClient instance,
030 * connect to the host, query the host, and finally disconnect from the host. If the finger service you want to query is on a non-standard port, connect to the
031 * host at that port. Here's a sample use:
032 *
033 * <pre>
034 * FingerClient finger;
035 * finger = new FingerClient();
036 * try {
037 *     finger.connect("foo.bar.com");
038 *     System.out.println(finger.query("foobar", false));
039 *     finger.disconnect();
040 * } catch (IOException e) {
041 *     System.err.println("Error I/O exception: " + e.getMessage());
042 *     return;
043 * }
044 * </pre>
045 */
046public class FingerClient extends SocketClient {
047
048    /**
049     * The default FINGER port. Set to {@value} according to RFC 1288.
050     */
051    public static final int DEFAULT_PORT = 79;
052
053    private static final String LONG_FLAG = "/W ";
054
055    /**
056     * The default FingerClient constructor. Initializes the default port to {@code DEFAULT_PORT}.
057     */
058    public FingerClient() {
059        setDefaultPort(DEFAULT_PORT);
060    }
061
062    /**
063     * Fingers the connected host and returns the input stream from the network connection of the finger query. This is equivalent to calling
064     * getInputStream(longOutput, ""). You must first connect to a finger server before calling this method, and you should disconnect after finishing reading
065     * the stream.
066     *
067     * @param longOutput Set to true if long output is requested, false if not.
068     * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results.
069     * @throws IOException If an I/O error during the operation.
070     */
071    public InputStream getInputStream(final boolean longOutput) throws IOException {
072        return getInputStream(longOutput, "");
073    }
074
075    /**
076     * Fingers a user and returns the input stream from the network connection of the finger query. You must first connect to a finger server before calling
077     * this method, and you should disconnect after finishing reading the stream.
078     *
079     * @param longOutput Set to true if long output is requested, false if not.
080     * @param user   The name of the user to finger.
081     * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results.
082     * @throws IOException If an I/O error during the operation.
083     */
084    public InputStream getInputStream(final boolean longOutput, final String user) throws IOException {
085        return getInputStream(longOutput, user, null);
086    }
087
088    /**
089     * Fingers a user and returns the input stream from the network connection of the finger query. You must first connect to a finger server before calling
090     * this method, and you should disconnect after finishing reading the stream.
091     *
092     * @param longOutput Set to true if long output is requested, false if not.
093     * @param user   The name of the user to finger.
094     * @param encoding   the character encoding that should be used for the query, null for the platform's default encoding
095     * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results.
096     * @throws IOException If an I/O error during the operation.
097     */
098    public InputStream getInputStream(final boolean longOutput, final String user, final String encoding) throws IOException {
099        final DataOutputStream output;
100        final StringBuilder buffer = new StringBuilder(64);
101        if (longOutput) {
102            buffer.append(LONG_FLAG);
103        }
104        buffer.append(user);
105        buffer.append(NETASCII_EOL);
106
107        // Note: Charsets.toCharset() returns the platform default for null input
108        final byte[] encodedQuery = buffer.toString().getBytes(Charsets.toCharset(encoding));
109
110        output = new DataOutputStream(new BufferedOutputStream(checkOpenOutputStream(), 1024));
111        output.write(encodedQuery, 0, encodedQuery.length);
112        output.flush();
113
114        return _input_;
115    }
116
117    /**
118     * Fingers the connected host and returns the output as a String. You must first connect to a finger server before calling this method, and you should
119     * disconnect afterward. This is equivalent to calling {@code query(longOutput, "")}.
120     *
121     * @param longOutput Set to true if long output is requested, false if not.
122     * @return The result of the finger query.
123     * @throws IOException If an I/O error occurs while reading the socket.
124     */
125    public String query(final boolean longOutput) throws IOException {
126        return query(longOutput, "");
127    }
128
129    /**
130     * Fingers a user at the connected host and returns the output as a String. You must first connect to a finger server before calling this method, and you
131     * should disconnect afterward.
132     *
133     * @param longOutput Set to true if long output is requested, false if not.
134     * @param user   The name of the user to finger.
135     * @return The result of the finger query.
136     * @throws IOException If an I/O error occurs while reading the socket.
137     */
138    public String query(final boolean longOutput, final String user) throws IOException {
139        return IOUtils.toString(() -> getInputStream(longOutput, user), getCharset());
140    }
141
142}