<commit_before>/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.james.nntpserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
import org.apache.avalon.cornerstone.services.scheduler.Target;
import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.james.BaseConnectionHandler;
import org.apache.james.nntpserver.repository.NNTPArticle;
import org.apache.james.nntpserver.repository.NNTPGroup;
import org.apache.james.nntpserver.repository.NNTPLineReaderImpl;
import org.apache.james.nntpserver.repository.NNTPRepository;
import org.apache.log.Logger;

/**
 * The NNTP protocol is defined by RFC 977.
 * This implementation is based on IETF draft 13, posted on 2nd April '2001.
 * URL: http://www.ietf.org/internet-drafts/draft-ietf-nntpext-base-13.txt
 *
 * Common NNTP extensions are in RFC 2980.
 *
 * @author <NAME>
 * @author Harmeet <<EMAIL>>
 */
public class NNTPHandler extends BaseConnectionHandler
    implements ConnectionHandler, Composable, Configurable, Target {

    // timeout controllers
    private TimeScheduler scheduler;

    // communciation.
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter writer;

    // authentication & authorization.
    private AuthService auth;

    // data abstractions.
    private NNTPGroup group;
    private NNTPRepository repo;

    private static final boolean DEBUG_PROTOCOL = 
        Boolean.getBoolean("apache.nntpserver.debug");

    public void compose( final ComponentManager componentManager )
        throws ComponentException
    {
        //System.out.println(getClass().getName()+": compose - "+authRequired);
        auth = (AuthService)componentManager.
            lookup( "org.apache.james.nntpserver.AuthService" );
        scheduler = (TimeScheduler)componentManager.
            lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" );
        repo = (NNTPRepository)componentManager
            .lookup("org.apache.james.nntpserver.repository.NNTPRepository");
    }

    public void handleConnection( Socket connection ) throws IOException {
        final Logger logger = getLogger();
        try {
            this.socket = connection;
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream())) {
                    public String readLine() throws IOException {
                        String s = super.readLine();
                        if ( DEBUG_PROTOCOL ) 
                            logger.debug("C: "+s);
                        return s;
                    }
                };
            writer = new PrintWriter(socket.getOutputStream()) {
                    public void println() {
                        // lines must end with CRLF, irrespective of the OS
                        print("\r\n");
                        flush();
                    }
                    public void println(String s) {
                        super.println(s);
                        if ( DEBUG_PROTOCOL )
                            logger.debug("S: "+s);
                    }
                };
            logger.info( "Connection from " + socket.getInetAddress());
        } catch (Exception e) {
            logger.error( "Cannot open connection from: " + e.getMessage(), e );
        }

        try {
            final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
            scheduler.addTrigger( this.toString(), trigger, this );

            // section 7.1
            if ( repo.isReadOnly() )
                writer.println("201 "+helloName+" NNTP Service Ready, posting prohibited");
            else
                writer.println("200 "+helloName+" NNTP Service Ready, posting permitted");

            while (parseCommand(reader.readLine()))
                scheduler.resetTrigger(this.toString());

            reader.close();
            writer.close();
            socket.close();
            scheduler.removeTrigger(this.toString());
            logger.info("Connection closed");
        } catch (Exception e) {
            doQUIT();
            //writer.println("502 Error closing connection.");
            //writer.flush();
            logger.error( "Exception during connection:" + e.getMessage(), e );
            try { socket.close();   } catch (IOException ioe) {  }
        }
    }

    public void targetTriggered( final String triggerName ) {
        getLogger().error("Connection timeout on socket");
        try {
            writer.println("Connection timeout. Closing connection");
            socket.close();
        } catch (IOException e) { }
    }

    private boolean parseCommand(String commandRaw) {
        if (commandRaw == null)
            return false;
        getLogger().info("Command received: " + commandRaw);
        //System.out.println("NNTPHandler> "+commandRaw);

        StringTokenizer tokens = new StringTokenizer(commandRaw);
        if (!tokens.hasMoreTokens())
            return false;
        final String command = tokens.nextToken();

        if (!auth.isAuthorized(command) ) {
            writer.println("502 User is not authenticated");
            getLogger().debug("Command not allowed.");
            return true;
        }
        if ( command.equalsIgnoreCase("MODE") && tokens.hasMoreTokens() &&
             tokens.nextToken().equalsIgnoreCase("READER") )
            doMODEREADER();
        else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
                  tokens.nextToken().equalsIgnoreCase("EXTENSIONS") )
            doLISTEXTENSIONS();
        else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
                  tokens.nextToken().equalsIgnoreCase("OVERVIEW.FMT") )
            doLISTOVERVIEWFMT();
        else if ( command.equalsIgnoreCase("GROUP") )
            doGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("LAST") )
            doLAST();
        else if ( command.equalsIgnoreCase("ARTICLE") )
            doARTICLE(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("HEAD") )
            doHEAD(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("BODY") )
            doBODY(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("STAT") )
            doSTAT(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("POST") )
            doPOST();
        else if ( command.equalsIgnoreCase("IHAVE") )
            doIHAVE(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("LIST") )
            doLIST(tokens);
        else if ( command.equalsIgnoreCase("QUIT") )
            doQUIT();
        else if ( command.equalsIgnoreCase("DATE") )
            doDATE();
        else if ( command.equalsIgnoreCase("HELP") )
            doHELP();
        else if ( command.equalsIgnoreCase("NEWNEWS") )
            doNEWSGROUPS(tokens);
        else if ( command.equalsIgnoreCase("NEWNEWS") )
            doNEWNEWS(tokens);
        else if ( command.equalsIgnoreCase("LISTGROUP") )
            doLISTGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("OVER") )
            doOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("XOVER") )
            doXOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("PAT") )
            doPAT();
        else if ( command.equalsIgnoreCase("HDR") )
            doHDR(tokens);
        else if ( command.equalsIgnoreCase("XHDR") )
            doXHDR(tokens);
        else if ( command.equalsIgnoreCase("AUTHINFO") )
            doAUTHINFO(tokens);
        else
            writer.println("501 Syntax error");
        return (command.equalsIgnoreCase("QUIT") == false);
    }

    // implements only the originnal AUTHINFO
    // for simple and generic AUTHINFO, 501 is sent back. This is as
    // per article 3.1.3 of RFC 2980
    private void doAUTHINFO(StringTokenizer tok) {
        String command = tok.nextToken();
        if ( command.equalsIgnoreCase("USER") ) {<commit_msg>NEWGROUPS command was not recognized.<commit_after>/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.james.nntpserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
import org.apache.avalon.cornerstone.services.scheduler.Target;
import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.james.BaseConnectionHandler;
import org.apache.james.nntpserver.repository.NNTPArticle;
import org.apache.james.nntpserver.repository.NNTPGroup;
import org.apache.james.nntpserver.repository.NNTPLineReaderImpl;
import org.apache.james.nntpserver.repository.NNTPRepository;
import org.apache.log.Logger;

/**
 * The NNTP protocol is defined by RFC 977.
 * This implementation is based on IETF draft 13, posted on 2nd April '2001.
 * URL: http://www.ietf.org/internet-drafts/draft-ietf-nntpext-base-13.txt
 *
 * Common NNTP extensions are in RFC 2980.
 *
 * @author <NAME>
 * @author Harmeet <<EMAIL>>
 */
public class NNTPHandler extends BaseConnectionHandler
    implements ConnectionHandler, Composable, Configurable, Target {

    // timeout controllers
    private TimeScheduler scheduler;

    // communciation.
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter writer;

    // authentication & authorization.
    private AuthService auth;

    // data abstractions.
    private NNTPGroup group;
    private NNTPRepository repo;

    private static final boolean DEBUG_PROTOCOL = 
        Boolean.getBoolean("apache.nntpserver.debug");

    public void compose( final ComponentManager componentManager )
        throws ComponentException
    {
        //System.out.println(getClass().getName()+": compose - "+authRequired);
        auth = (AuthService)componentManager.
            lookup( "org.apache.james.nntpserver.AuthService" );
        scheduler = (TimeScheduler)componentManager.
            lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" );
        repo = (NNTPRepository)componentManager
            .lookup("org.apache.james.nntpserver.repository.NNTPRepository");
    }

    public void handleConnection( Socket connection ) throws IOException {
        final Logger logger = getLogger();
        try {
            this.socket = connection;
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream())) {
                    public String readLine() throws IOException {
                        String s = super.readLine();
                        if ( DEBUG_PROTOCOL ) 
                            logger.debug("C: "+s);
                        return s;
                    }
                };
            writer = new PrintWriter(socket.getOutputStream()) {
                    public void println() {
                        // lines must end with CRLF, irrespective of the OS
                        print("\r\n");
                        flush();
                    }
                    public void println(String s) {
                        super.println(s);
                        if ( DEBUG_PROTOCOL )
                            logger.debug("S: "+s);
                    }
                };
            logger.info( "Connection from " + socket.getInetAddress());
        } catch (Exception e) {
            logger.error( "Cannot open connection from: " + e.getMessage(), e );
        }

        try {
            final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
            scheduler.addTrigger( this.toString(), trigger, this );

            // section 7.1
            if ( repo.isReadOnly() )
                writer.println("201 "+helloName+" NNTP Service Ready, posting prohibited");
            else
                writer.println("200 "+helloName+" NNTP Service Ready, posting permitted");

            while (parseCommand(reader.readLine()))
                scheduler.resetTrigger(this.toString());

            reader.close();
            writer.close();
            socket.close();
            scheduler.removeTrigger(this.toString());
            logger.info("Connection closed");
        } catch (Exception e) {
            doQUIT();
            //writer.println("502 Error closing connection.");
            //writer.flush();
            logger.error( "Exception during connection:" + e.getMessage(), e );
            try { socket.close();   } catch (IOException ioe) {  }
        }
    }

    public void targetTriggered( final String triggerName ) {
        getLogger().error("Connection timeout on socket");
        try {
            writer.println("Connection timeout. Closing connection");
            socket.close();
        } catch (IOException e) { }
    }

    private boolean parseCommand(String commandRaw) {
        if (commandRaw == null)
            return false;
        getLogger().info("Command received: " + commandRaw);
        //System.out.println("NNTPHandler> "+commandRaw);

        StringTokenizer tokens = new StringTokenizer(commandRaw);
        if (!tokens.hasMoreTokens())
            return false;
        final String command = tokens.nextToken();

        if (!auth.isAuthorized(command) ) {
            writer.println("502 User is not authenticated");
            getLogger().debug("Command not allowed.");
            return true;
        }
        if ( command.equalsIgnoreCase("MODE") && tokens.hasMoreTokens() &&
             tokens.nextToken().equalsIgnoreCase("READER") )
            doMODEREADER();
        else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
                  tokens.nextToken().equalsIgnoreCase("EXTENSIONS") )
            doLISTEXTENSIONS();
        else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
                  tokens.nextToken().equalsIgnoreCase("OVERVIEW.FMT") )
            doLISTOVERVIEWFMT();
        else if ( command.equalsIgnoreCase("GROUP") )
            doGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("LAST") )
            doLAST();
        else if ( command.equalsIgnoreCase("ARTICLE") )
            doARTICLE(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("HEAD") )
            doHEAD(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("BODY") )
            doBODY(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("STAT") )
            doSTAT(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("POST") )
            doPOST();
        else if ( command.equalsIgnoreCase("IHAVE") )
            doIHAVE(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("LIST") )
            doLIST(tokens);
        else if ( command.equalsIgnoreCase("QUIT") )
            doQUIT();
        else if ( command.equalsIgnoreCase("DATE") )
            doDATE();
        else if ( command.equalsIgnoreCase("HELP") )
            doHELP();
        else if ( command.equalsIgnoreCase("NEWSGROUPS") )
            doNEWSGROUPS(tokens);
        else if ( command.equalsIgnoreCase("NEWNEWS") )
            doNEWNEWS(tokens);
        else if ( command.equalsIgnoreCase("LISTGROUP") )
            doLISTGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("OVER") )
            doOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("XOVER") )
            doXOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
        else if ( command.equalsIgnoreCase("PAT") )
            doPAT();
        else if ( command.equalsIgnoreCase("HDR") )
            doHDR(tokens);
        else if ( command.equalsIgnoreCase("XHDR") )
            doXHDR(tokens);
        else if ( command.equalsIgnoreCase("AUTHINFO") )
            doAUTHINFO(tokens);
        else
            writer.println("501 Syntax error");
        return (command.equalsIgnoreCase("QUIT") == false);
    }

    // implements only the originnal AUTHINFO
    // for simple and generic AUTHINFO, 501 is sent back. This is as
    // per article 3.1.3 of RFC 2980
    private void doAUTHINFO(StringTokenizer tok) {
        String command = tok.nextToken();
        if ( command.equalsIgnoreCase("USER") ) {
{'fraction_non_alphanumeric': 0.11093341862572632, 'fraction_numerical': 0.004904312596620289, 'mean_word_length': 2.456153279292557, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2401968', 'n_tokens_mistral': 5022, 'n_tokens_neox': 4873, 'n_words': 1066}