// Copyright 1997-2002 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import <OWF/OWUnixFTPProcessor.h>

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import <OWF/OWAddress.h>
#import <OWF/OWContentCache.h>
#import <OWF/OWDataStreamCharacterCursor.h>
#import <OWF/OWFileInfo.h>
#import <OWF/OWObjectStream.h>
#import <OWF/OWPipeline.h>
#import <OWF/OWURL.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OWF/Processors.subproj/Protocols.subproj/FTP.subproj/OWUnixFTPProcessor.m,v 1.10 2002/03/09 01:53:52 kc Exp $")

@interface OWUnixFTPProcessor (Private)
+ (void)registerForContentTypeString:(NSString *)sourceType cost:(int)cost;
@end

@implementation OWUnixFTPProcessor

+ (void)didLoad;
{
    [self registerForContentTypeString:@"OWFTPDirectory/UNIX" cost:1.0];
    [self registerForContentTypeString:@"OWFTPDirectory/RHAPSODY" cost:1.0];
    [self registerForContentTypeString:@"OWFTPDirectory/unknown" cost:10.0];
    [self registerForContentTypeString:@"OWFTPDirectory/Windows_NT" cost:2.0];
    [self registerForContentTypeString:@"OWFTPDirectory/MacOS-MachTen" cost:2.0];
}

+ (void)registerForContentTypeString:(NSString *)sourceType cost:(int)cost;
{
    [self registerProcessorClass:self fromContentTypeString:sourceType toContentTypeString:@"ObjectStream/OWFileInfoList" cost:cost];
}

- initWithPipeline:(OWPipeline *)aPipeline;
{
    if (![super initWithPipeline:aPipeline])
        return nil;

    baseAddress = [(OWAddress *)[pipeline lastAddress] retain];

    objectStream = [[OWObjectStream alloc] init];
    [objectStream setContentTypeString:@"ObjectStream/OWFileInfoList"];
    [pipeline addContent:objectStream];
    [pipeline cacheContent];
    [pipeline startProcessingContent];
    return self;
}

- (void)dealloc;
{
    [objectStream release];
    [baseAddress release];
    [super dealloc];
}

- (void)addFileForLine:(NSString *)line;
{
    OWFileInfo *fileInfo;
    NSScanner *lineScanner;
    NSString *directoryFlag, *permissions = nil;
    unsigned int linkCount = 0;
    NSString *owner = nil, *group = nil;
    unsigned long long int size = 0;
    NSString *month = nil, *day = nil, *timeOrYear = nil;
    NSString *dateString, *name = nil;
    NSCharacterSet *whitespace;
    NSCalendarDate *changeDate;
    NSRange range;
    NSAutoreleasePool *pool;

    pool = [[NSAutoreleasePool alloc] init];
    whitespace = [NSCharacterSet whitespaceCharacterSet];
    lineScanner = [NSScanner scannerWithString:line];
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&permissions];
    directoryFlag = [permissions substringToIndex:1];
    permissions = [permissions substringFromIndex:1];
    [lineScanner scanInt:&linkCount];
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&owner];
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&group];
    if (![lineScanner scanLongLong:&size]) {
	size = [group intValue];
	group = nil;
    }
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&month];
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&day];
    [lineScanner scanUpToCharactersFromSet:whitespace intoString:&timeOrYear];
    range = [timeOrYear rangeOfString:@":"];
    if (range.length) {
        NSCalendarDate *now = (NSCalendarDate *)[NSCalendarDate date];
	dateString = [NSString stringWithFormat:@"%@ %@ %@ %d", month, day, timeOrYear, [now yearOfCommonEra]];
	changeDate = [NSCalendarDate dateWithString:dateString calendarFormat:@"%b %d %H:%M %Y"];

        // deal with year boundaries
        if ([changeDate monthOfYear] > [now monthOfYear])
            changeDate = [changeDate dateByAddingYears:-1 months:0 days:0 hours:0 minutes:0 seconds:0];
    } else {
	dateString = [NSString stringWithFormat:@"%@ %@ %@", month, day, timeOrYear];
	changeDate = [NSCalendarDate dateWithString:dateString calendarFormat:@"%b %d %Y"];
    }
    [lineScanner setCharactersToBeSkipped:nil];
    [lineScanner scanCharactersFromSet:whitespace intoString:NULL];
    [lineScanner scanUpToString:@" -> " intoString:&name];

    if ([name isEqualToString:@"."] || [name isEqualToString:@".."]) {
        [pool release];
        return;
    }

    fileInfo = [[OWFileInfo alloc] initWithAddress:[baseAddress addressForRelativeString:[NSString encodeURLString:name asQuery:NO leaveSlashes:NO leaveColons:NO]] size:[NSNumber numberWithUnsignedLong:size] isDirectory:[directoryFlag isEqualToString:@"d"] isShortcut:[directoryFlag isEqualToString:@"l"] lastChangeDate:changeDate];
    [objectStream writeObject:fileInfo];
    [[OWContentCache contentCacheForAddress:[fileInfo address]] addContent:fileInfo];
    [fileInfo release];
    [pool release];
}

- (void)process;
{
    BOOL firstLine = YES;
    NSString *line;

    while ((line = [characterCursor readLine])) {
        if (firstLine && [line hasPrefix:@"total "]) {
            // On some servers, the first line gives the total size, so we just skip it.
        } else {
            [self addFileForLine:line];
        }
        firstLine = NO;
    }
    [objectStream dataEnd];
}

- (void)processAbort;
{
    [objectStream dataAbort];
    [super processAbort];
}

@end
