/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1990-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
/*
 * File: ifs_news.c
 */

#include "ifs_agent.h"
#include <stdio.h>

struct {
    char	*Group[ 256 ];
    int		version;
} NewsData;

/*
 *name: NewsCommand
 */
int
NewsCommand( nFile, cmd, arg )
NetFile	*nFile;
char	*cmd, *arg;
{
    char	buf[ STRLEN ];

    if( cmd == NULL )
	return -1;
    sprintf( buf, arg ? "%s %s\r\n" : "%s\r\n", cmd, arg );
    NetWrite( nFile, buf, strlen(buf) );
    debug_logit( buf );
    return 0;
}

/*
 *name: NewsReply
 */
int
NewsReply( nFile, buf, bsize )
NetFile *nFile;
char    *buf;
int     bsize;
{
    char        tmpbuf[ STRLEN ];
    char        token[4];

    if( buf == NULL ) {
	buf = tmpbuf;
	bsize = sizeof(tmpbuf);
    }
    if( NetGets( nFile, buf, bsize ) == NULL ) {
	debug_logit( "FtpReply: NetGets return NULL\n" );
	return -1;
    }
    debug_logit( buf );
    return (int)strtol( buf, (char**)0, 0 );
}

/*
 *name: NewsXferFile
 */
int
NewsXferFile( nFile, destfile )
NetFile	*nFile;
char	*destfile;
{
    FILE	*fp;
    char	buf[ STRLEN ], *ptr;

    fp = (destfile ? fopen( destfile, "w" ) : NULL);
    while( NetGets( nFile, buf, sizeof(buf) ) != NULL ) {
	if( (ptr = strchr( buf, '\r' )) != NULL && ptr[1] == '\n' ) {
	    *ptr = '\0';
	}
	if( buf[0] == '.' && buf[1] == '\0' ) {		/* end of file */
	    if( fp )  fclose( fp );
	    return 0;
	}
	if( fp )  fprintf( fp, "%s\n", buf );
    }
    cserrno = E_DATAXFER;
    return -1;
}

/*
 *name: NewsListGroups
 */
int
NewsListGroups( srv )
struct server_info *srv;
{
    struct mount_item	*mitem = srv->mitem;
    NetFile	*nFile = mitem->nFile;
    char	destfile[ STRLEN ];

    NewsCommand( nFile, "LIST", NULL );
    if( NewsReply( nFile, NULL, 0 ) != 215 ) {
	logit( "<news>: LIST error\n" );
	cserrno = E_DATAXFER;
	return -1;
    }

    /* 215 Newsgroups in form "group high low flags". */
    sprintf( destfile, "%s/._dir", srv->lpath );
    MakePath( destfile );
    return NewsXferFile( nFile, destfile );
}

/*
 *name: NewsEnterGroup
 */
int
NewsEnterGroup( srv, group, lpath )
struct server_info *srv;
char	*group;
char	*lpath;
{
    struct mount_item	*mitem = srv->mitem;
    NetFile	*nFile = mitem->nFile;
    FILE	*fp;
    char	buf[ STRLEN ];
    char	*arg[ 10 ];
    char	*oldgroup;

    oldgroup = NewsData.Group[ nFile->socket ];
    if( oldgroup != NULL ) {
	if( strcmp( group, oldgroup ) == 0 )
	    return 0;
	free( oldgroup );
    }
    NewsData.Group[ nFile->socket ] = strdup( group );
    NewsCommand( nFile, "GROUP", group );
    if( NewsReply( nFile, buf, sizeof(buf) ) != 211 ||
	SplitFields( arg, 5, buf, ' ' ) != 5 ) {
	logit( "<news>: unknown group\n" );
	cserrno = E_DATAXFER;
	return -1;
    }

    /* 211 num min max group-name */
    if( lpath == NULL )
	return 0;

    sprintf( buf, "%d-%d", (int)strtol( arg[2], (char**)0, 0 ), (int)strtol( arg[3], (char**)0, 0 ) );
    NewsCommand( nFile, "XOVER", buf );
    if( NewsReply( nFile, NULL, 0 ) != 224 ) {
	logit( "<news>: xover error\n" );
	cserrno = E_DATAXFER;
	return -1;
    }
    
    /* 224 data follows */
    sprintf( buf, "%s/._dir", lpath );
    MakePath( buf );
    if( NewsXferFile( nFile, buf ) == -1 ) {
	return -1;
    }
    chdir( lpath );
    if( (fp = fopen( "._dir", "r" )) != NULL ) {
	while( fgets( buf, sizeof(buf), fp ) != NULL ) {
	    if( SplitFields( arg, 7, buf, '\t' ) == 7 ) {
		MakeImageFile( arg[0], (int)strtol( arg[6], (char**)0, 0 ) );
	    }
	}
	fclose( fp );
    }
    return 0;
}

/*
 *name: NewsGetArticle
 */
int
NewsGetArticle( srv, group, article )
struct server_info *srv;
char	*group;
char	*article;
{
    struct mount_item	*mitem = srv->mitem;
    NetFile	*nFile = mitem->nFile;
    char	destfile[ STRLEN ];

    if( NewsEnterGroup( srv, group, NULL ) == -1 )
	return -1;
    NewsCommand( nFile, "ARTICLE", article );
    if( NewsReply( nFile, NULL, 0 ) != 220 ) {
	logit( "<news>: get article error\n" );
	cserrno = E_DATAXFER;
	return -1;
    }
    return NewsXferFile( nFile, srv->lpath );
}

/*
 *name: NewsConnect
 */
int
NewsConnect( srv )
struct server_info *srv;
{
    struct mount_item	*mitem = srv->mitem;
    NetFile	*nFile = mitem->nFile;
    int		port;

    if( nFile != NULL ) {
	NewsCommand( nFile, "MODE", "READER" );
	if( NewsReply( nFile, NULL, 0 ) == 200 ) {
	    return 0;
	}
	NetClose( nFile );
    }
    logit( "<news>: connect\n" );
    port = (mitem->port ? mitem->port : 119);
    if( (nFile = NetConnect( srv, mitem->host, port )) == NULL )
	return -1;
    if( NewsReply( nFile, NULL, 0 ) != 200 ) {
	NetClose( nFile );
	return -1;
    }
    NewsCommand( nFile, "MODE", "READER" );
    if( NewsReply( nFile, NULL, 0 ) != 200 ) {
	NetClose( nFile );
	return -1;
    }
    mitem->nFile = nFile;
    return 0;
}

/*
 *name: NewsDisconnect
 */
int
NewsDisconnect( srv )
struct server_info *srv;
{
    struct mount_item	*mitem = srv->mitem;
    NetFile	*nFile = mitem->nFile;

    if( nFile != NULL ) {
	NetClose( nFile );
	mitem->nFile = NULL;
    }
    return 0;
}

/*
 *name: NewsGetFile
 */
int
NewsGetFile( srv )
struct server_info *srv;
{
    struct mount_item	*mitem = srv->mitem;
    char	group[ STRLEN ];
    char	*rpath, *article;

    if( mitem->nFile == NULL )
	if( NewsConnect( srv ) == -1 )
	    return -1;
    if( NetDataReady( mitem->nFile ) ) { /* 503 Timeout or invalid mode */
	NewsDisconnect( srv );
	if( NewsConnect( srv ) == -1 )
	    return -1;
    }

    rpath = srv->rpath;
    if( *rpath == '\0' ) {
	return NewsListGroups( srv );
    }

    strcpy( group, rpath+1 );		/* group(/article) */
    if( (article = strchr( group, '/' )) == NULL ) {
	return NewsEnterGroup( srv, group, srv->lpath );
    }

    *article++ = '\0';
    if( strchr( article, '/' ) != NULL ) {	/* too many arguments */
	logit( "<news>: invalid path\n" );
	return -1;
    }
    return NewsGetArticle( srv, group, article );
}

/*
 *name: NewsPutFile
 */
int
NewsPutFile( srv )
struct server_info *srv;
{
    return 0;
}

/*
 *name: NewsNop
 */
int
NewsNop( srv )
struct server_info *srv;
{
    return 0;
}

/*
 *name: NewsInit
 *      Initial the data and functions in agent_item
 */
int
NewsInit( tbl )
struct agent_item       *tbl;
{
    tbl->localdata	= (char *) &NewsData;
    tbl->connect	= NewsConnect;
    tbl->disconnect	= NewsDisconnect;
    tbl->listdents	= NewsGetFile;
    tbl->getfile	= NewsGetFile;
    tbl->putfile	= NewsPutFile;
    tbl->userdef	= NewsNop;
    return 0;
}

