Blame src/FtpListInfo.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "FtpListInfo.h"
Packit 8f70b4
#include "FileSet.h"
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h>
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "ftpclass.h"
Packit 8f70b4
#include "ascii_ctype.h"
Packit 8f70b4
Packit 8f70b4
#define number_of_parsers 7
Packit 8f70b4
Packit 8f70b4
FileSet *FtpListInfo::Parse(const char *buf,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(mode==FA::LONG_LIST || mode==FA::MP_LIST)
Packit 8f70b4
   {
Packit 8f70b4
      if(len==0 && mode==FA::LONG_LIST
Packit 8f70b4
      && !ResMgr::QueryBool("ftp:list-empty-ok",session->GetHostName()))
Packit 8f70b4
      {
Packit 8f70b4
	 mode=FA::LIST;
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      int err;
Packit 8f70b4
      FileSet *set=session->ParseLongList(buf,len,&err;;
Packit 8f70b4
      if(!set || err>0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(mode==FA::MP_LIST)
Packit 8f70b4
	    mode=FA::LONG_LIST;
Packit 8f70b4
	 else
Packit 8f70b4
	    mode=FA::LIST;
Packit 8f70b4
      }
Packit 8f70b4
      return set;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      return ParseShortList(buf,len);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileSet *Ftp::ParseLongList(const char *buf,int len,int *err_ret) const
Packit 8f70b4
{
Packit 8f70b4
   if(err_ret)
Packit 8f70b4
      *err_ret=0;
Packit 8f70b4
Packit 8f70b4
   int err[number_of_parsers];
Packit 8f70b4
   FileSet *set[number_of_parsers];
Packit 8f70b4
   int i;
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      err[i]=0;
Packit 8f70b4
      set[i]=new FileSet;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   xstring line;
Packit 8f70b4
   xstring tmp_line;
Packit 8f70b4
Packit 8f70b4
   FtpLineParser guessed_parser=0;
Packit 8f70b4
   FileSet **the_set=0;
Packit 8f70b4
   int *the_err=0;
Packit 8f70b4
   int *best_err1=&err[0];
Packit 8f70b4
   int *best_err2=&err[1];
Packit 8f70b4
Packit 8f70b4
   const char *tz=Query("timezone",hostname);
Packit 8f70b4
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      const char *nl=(char*)memchr(buf,'\n',len);
Packit 8f70b4
      if(!nl)
Packit 8f70b4
	 break;
Packit 8f70b4
      line.nset(buf,nl-buf);
Packit 8f70b4
      line.chomp('\r');
Packit 8f70b4
      if(line.length()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 len-=nl+1-buf;
Packit 8f70b4
	 buf=nl+1;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      len-=nl+1-buf;
Packit 8f70b4
      buf=nl+1;
Packit 8f70b4
Packit 8f70b4
      if(!guessed_parser)
Packit 8f70b4
      {
Packit 8f70b4
	 for(i=0; i
Packit 8f70b4
	 {
Packit 8f70b4
	    tmp_line.set(line);	 // parser can clobber the line - work on a copy
Packit 8f70b4
	    FileInfo *info=(*line_parsers[i])(tmp_line.get_non_const(),&err[i],tz);
Packit 8f70b4
	    if(info && info->name.length()>1)
Packit 8f70b4
	       info->name.chomp('/');
Packit 8f70b4
	    if(info && !strchr(info->name,'/'))
Packit 8f70b4
	       set[i]->Add(info);
Packit 8f70b4
	    else
Packit 8f70b4
	       delete info;
Packit 8f70b4
Packit 8f70b4
	    if(*best_err1>err[i])
Packit 8f70b4
	       best_err1=&err[i];
Packit 8f70b4
	    if(*best_err2>err[i] && best_err1!=&err[i])
Packit 8f70b4
	       best_err2=&err[i];
Packit 8f70b4
	    if(*best_err1>16)
Packit 8f70b4
	       goto leave; // too many errors with best parser.
Packit 8f70b4
	 }
Packit 8f70b4
	 if(*best_err2 > (*best_err1+1)*16)
Packit 8f70b4
	 {
Packit 8f70b4
	    i=best_err1-err;
Packit 8f70b4
	    guessed_parser=line_parsers[i];
Packit 8f70b4
	    the_set=&set[i];
Packit 8f70b4
	    the_err=&err[i];
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *info=(*guessed_parser)(line.get_non_const(),the_err,tz);
Packit 8f70b4
	 if(info && info->name.length()>1)
Packit 8f70b4
	    info->name.chomp('/');
Packit 8f70b4
	 if(info && !strchr(info->name,'/'))
Packit 8f70b4
	    (*the_set)->Add(info);
Packit 8f70b4
	 else
Packit 8f70b4
	    delete info;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!the_set)
Packit 8f70b4
   {
Packit 8f70b4
      i=best_err1-err;
Packit 8f70b4
      the_set=&set[i];
Packit 8f70b4
      the_err=&err[i];
Packit 8f70b4
   }
Packit 8f70b4
leave:
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
      if(&set[i]!=the_set)
Packit 8f70b4
	 delete set[i];
Packit 8f70b4
   if(err_ret && the_err)
Packit 8f70b4
      *err_ret=*the_err;
Packit 8f70b4
   return the_set?*the_set:0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileSet *FtpListInfo::ParseShortList(const char *buf,int len)
Packit 8f70b4
{
Packit 8f70b4
   FileSet *set=new FileSet;
Packit 8f70b4
   char *line=0;
Packit 8f70b4
   int line_alloc=0;
Packit 8f70b4
   int line_len;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      // workaround for some ftp servers
Packit 8f70b4
      if(len>=2 && buf[0]=='.' && buf[1]=='/')
Packit 8f70b4
      {
Packit 8f70b4
	 buf+=2;
Packit 8f70b4
	 len-=2;
Packit 8f70b4
      }
Packit 8f70b4
#if 0 // not possible here
Packit 8f70b4
      if(len>=2 && buf[0]=='/' && buf[1]=='/')
Packit 8f70b4
      {
Packit 8f70b4
	 buf++;
Packit 8f70b4
	 len--;
Packit 8f70b4
      }
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
      const char *nl=(const char*)memchr(buf,'\n',len);
Packit 8f70b4
      if(!nl)
Packit 8f70b4
	 break;
Packit 8f70b4
      line_len=nl-buf;
Packit 8f70b4
      if(line_len>0 && buf[line_len-1]=='\r')
Packit 8f70b4
	 line_len--;
Packit 8f70b4
      FileInfo::type type=FileInfo::UNKNOWN;
Packit 8f70b4
      const char *slash=(const char*)memchr(buf,'/',line_len);
Packit 8f70b4
      if(slash)
Packit 8f70b4
      {
Packit 8f70b4
	 type=FileInfo::DIRECTORY;
Packit 8f70b4
	 line_len=slash-buf;
Packit 8f70b4
      }
Packit 8f70b4
      if(line_len==0)
Packit 8f70b4
      {
Packit 8f70b4
	 len-=nl+1-buf;
Packit 8f70b4
	 buf=nl+1;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(line_alloc
Packit 8f70b4
	 line=string_alloca(line_alloc=line_len+128);
Packit 8f70b4
      memcpy(line,buf,line_len);
Packit 8f70b4
      line[line_len]=0;
Packit 8f70b4
Packit 8f70b4
      len-=nl+1-buf;
Packit 8f70b4
      buf=nl+1;
Packit 8f70b4
Packit 8f70b4
      if(!strchr(line,'/'))
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=new FileInfo(line);
Packit 8f70b4
	 if(type!=fi->UNKNOWN)
Packit 8f70b4
	    fi->SetType(type);
Packit 8f70b4
	 set->Add(fi);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return set;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static
Packit 8f70b4
FileInfo *ParseFtpLongList_UNIX(char *line,int *err,const char *tz)
Packit 8f70b4
{
Packit 8f70b4
   int	 tmp;
Packit 8f70b4
   if(sscanf(line,"total %d",&tmp)==1)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(!strncasecmp(line,"Status of ",10))
Packit 8f70b4
      return 0;	  // STAT output.
Packit 8f70b4
   if(strchr("bcpsD",line[0])) // block, char, pipe, socket, Door.
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   FileInfo *fi=FileInfo::parse_ls_line(line,tz);
Packit 8f70b4
   if(!fi)
Packit 8f70b4
   {
Packit 8f70b4
      (*err)++;
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#define FIRST_TOKEN strtok(line," \t")
Packit 8f70b4
#define NEXT_TOKEN  strtok(NULL," \t")
Packit 8f70b4
#define ERR do{(*err)++;delete fi;return(0);}while(0)
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
07-13-98  09:06PM       <DIR>          aix
Packit 8f70b4
07-13-98  09:06PM       <DIR>          hpux
Packit 8f70b4
07-13-98  09:06PM       <DIR>          linux
Packit 8f70b4
07-13-98  09:06PM       <DIR>          ncr
Packit 8f70b4
07-13-98  09:06PM       <DIR>          solaris
Packit 8f70b4
03-18-98  06:01AM              2109440 nlxb318e.tar
Packit 8f70b4
07-02-98  11:17AM                13844 Whatsnew.txt
Packit 8f70b4
*/
Packit 8f70b4
static
Packit 8f70b4
FileInfo *ParseFtpLongList_NT(char *line,int *err,const char *tz)
Packit 8f70b4
{
Packit 8f70b4
   char *t = FIRST_TOKEN;
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   int month,day,year;
Packit 8f70b4
   if(sscanf(t,"%2d-%2d-%2d",&month,&day,&year)!=3)
Packit 8f70b4
      ERR;
Packit 8f70b4
   if(year>=70)
Packit 8f70b4
      year+=1900;
Packit 8f70b4
   else
Packit 8f70b4
      year+=2000;
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   int hour,minute;
Packit 8f70b4
   char am='A'; // AM/PM is optional
Packit 8f70b4
   if(sscanf(t,"%2d:%2d%c",&hour,&minute,&am)<2)
Packit 8f70b4
      ERR;
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   if(am=='P') // PM - after noon
Packit 8f70b4
   {
Packit 8f70b4
      hour+=12;
Packit 8f70b4
      if(hour==24)
Packit 8f70b4
	 hour=0;
Packit 8f70b4
   }
Packit 8f70b4
   struct tm tms;
Packit 8f70b4
   tms.tm_sec=30;	   /* seconds after the minute [0, 61]  */
Packit 8f70b4
   tms.tm_min=minute;      /* minutes after the hour [0, 59] */
Packit 8f70b4
   tms.tm_hour=hour;	   /* hour since midnight [0, 23] */
Packit 8f70b4
   tms.tm_mday=day;	   /* day of the month [1, 31] */
Packit 8f70b4
   tms.tm_mon=month-1;     /* months since January [0, 11] */
Packit 8f70b4
   tms.tm_year=year-1900;  /* years since 1900 */
Packit 8f70b4
   tms.tm_isdst=-1;
Packit 8f70b4
Packit 8f70b4
   fi=new FileInfo();
Packit 8f70b4
   fi->SetDate(mktime_from_tz(&tms,tz),30);
Packit 8f70b4
Packit 8f70b4
   long long size;
Packit 8f70b4
   if(!strcmp(t,"<DIR>"))
Packit 8f70b4
      fi->SetType(fi->DIRECTORY);
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      fi->SetType(fi->NORMAL);
Packit 8f70b4
      if(sscanf(t,"%lld",&size)!=1)
Packit 8f70b4
	 ERR;
Packit 8f70b4
      fi->SetSize(size);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   t=strtok(NULL,"");
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   while(*t==' ')
Packit 8f70b4
      t++;
Packit 8f70b4
   if(*t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   fi->SetName(t);
Packit 8f70b4
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
ASUSER          8192 04/26/05 13:54:16 *DIR       dir/
Packit 8f70b4
ASUSER          8192 04/26/05 13:57:34 *DIR       dir1/
Packit 8f70b4
ASUSER        365255 02/28/01 15:41:40 *STMF      readme.txt
Packit 8f70b4
ASUSER       8489625 03/18/03 09:37:00 *STMF      saved.zip
Packit 8f70b4
ASUSER        365255 02/28/01 15:41:40 *STMF      unist.old
Packit 8f70b4
*/
Packit 8f70b4
static
Packit 8f70b4
FileInfo *ParseFtpLongList_AS400(char *line,int *err,const char *tz)
Packit 8f70b4
{
Packit 8f70b4
   char *t = FIRST_TOKEN;
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   char *user=t;
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   long long size;
Packit 8f70b4
   if(sscanf(t,"%lld",&size)!=1)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   int month,day,year;
Packit 8f70b4
   if(sscanf(t,"%2d/%2d/%2d",&month,&day,&year)!=3)
Packit 8f70b4
      ERR;
Packit 8f70b4
   if(year>=70)
Packit 8f70b4
      year+=1900;
Packit 8f70b4
   else
Packit 8f70b4
      year+=2000;
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   int hour,minute,second;
Packit 8f70b4
   if(sscanf(t,"%2d:%2d:%2d",&hour,&minute,&second)!=3)
Packit 8f70b4
      ERR;
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   struct tm tms;
Packit 8f70b4
   tms.tm_sec=second;	   /* seconds after the minute [0, 61]  */
Packit 8f70b4
   tms.tm_min=minute;      /* minutes after the hour [0, 59] */
Packit 8f70b4
   tms.tm_hour=hour;	   /* hour since midnight [0, 23] */
Packit 8f70b4
   tms.tm_mday=day;	   /* day of the month [1, 31] */
Packit 8f70b4
   tms.tm_mon=month-1;     /* months since January [0, 11] */
Packit 8f70b4
   tms.tm_year=year-1900;  /* years since 1900 */
Packit 8f70b4
   tms.tm_isdst=-1;
Packit 8f70b4
   time_t mtime=mktime_from_tz(&tms,tz);
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   FileInfo::type type=FileInfo::UNKNOWN;
Packit 8f70b4
   if(!strcmp(t,"*DIR"))
Packit 8f70b4
      type=FileInfo::DIRECTORY;
Packit 8f70b4
   else
Packit 8f70b4
      type=FileInfo::NORMAL;
Packit 8f70b4
Packit 8f70b4
   t=strtok(NULL,"");
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   while(*t==' ')
Packit 8f70b4
      t++;
Packit 8f70b4
   if(*t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   char *slash=strchr(t,'/');
Packit 8f70b4
   if(slash)
Packit 8f70b4
   {
Packit 8f70b4
      if(slash==t)
Packit 8f70b4
	 return 0;
Packit 8f70b4
      *slash=0;
Packit 8f70b4
      type=FileInfo::DIRECTORY;
Packit 8f70b4
      if(slash[1])
Packit 8f70b4
      {
Packit 8f70b4
	 fi=new FileInfo(t);
Packit 8f70b4
	 fi->SetType(type);
Packit 8f70b4
	 return fi;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   fi=new FileInfo(t);
Packit 8f70b4
   fi->SetType(type);
Packit 8f70b4
   fi->SetSize(size);
Packit 8f70b4
   fi->SetDate(mtime,0);
Packit 8f70b4
   fi->SetUser(user);
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
+i774.71425,m951188401,/,	users
Packit 8f70b4
+i774.49602,m917883130,r,s79126,	jgr_www2.exe
Packit 8f70b4
Packit 8f70b4
starts with +
Packit 8f70b4
comma separated
Packit 8f70b4
first character of field is type:
Packit 8f70b4
 i - ?
Packit 8f70b4
 m - modification time
Packit 8f70b4
 / - means directory
Packit 8f70b4
 r - means plain file
Packit 8f70b4
 s - size
Packit 8f70b4
 up - permissions in octal
Packit 8f70b4
 \t - file name follows.
Packit 8f70b4
*/
Packit 8f70b4
FileInfo *ParseFtpLongList_EPLF(char *line,int *err,const char *)
Packit 8f70b4
{
Packit 8f70b4
   int len=strlen(line);
Packit 8f70b4
   const char *b=line;
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
Packit 8f70b4
   if(len<2 || b[0]!='+')
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   const char *name=0;
Packit 8f70b4
   int name_len=0;
Packit 8f70b4
   off_t size=NO_SIZE;
Packit 8f70b4
   time_t date=NO_DATE;
Packit 8f70b4
   long date_l;
Packit 8f70b4
   long long size_ll;
Packit 8f70b4
   bool dir=false;
Packit 8f70b4
   bool type_known=false;
Packit 8f70b4
   int perms=-1;
Packit 8f70b4
Packit 8f70b4
   const char *scan=b+1;
Packit 8f70b4
   int scan_len=len-1;
Packit 8f70b4
   while(scan && scan_len>0)
Packit 8f70b4
   {
Packit 8f70b4
      switch(*scan)
Packit 8f70b4
      {
Packit 8f70b4
	 case '\t':  // the rest is file name.
Packit 8f70b4
	    name=scan+1;
Packit 8f70b4
	    name_len=scan_len-1;
Packit 8f70b4
	    scan=0;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 's':
Packit 8f70b4
	    if(1 != sscanf(scan+1,"%lld",&size_ll))
Packit 8f70b4
	       break;
Packit 8f70b4
	    size = size_ll;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 'm':
Packit 8f70b4
	    if(1 != sscanf(scan+1,"%ld",&date_l))
Packit 8f70b4
	       break;
Packit 8f70b4
	    date = date_l;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case '/':
Packit 8f70b4
	    dir=true;
Packit 8f70b4
	    type_known=true;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 'r':
Packit 8f70b4
	    dir=false;
Packit 8f70b4
	    type_known=true;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 'i':
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 'u':
Packit 8f70b4
	    if(scan[1]=='p')  // permissions.
Packit 8f70b4
	       if(sscanf(scan+2,"%o",&perms)!=1)
Packit 8f70b4
		  perms=-1;
Packit 8f70b4
	    break;
Packit 8f70b4
	 default:
Packit 8f70b4
	    name=0;
Packit 8f70b4
	    scan=0;
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
      if(scan==0 || scan_len==0)
Packit 8f70b4
	 break;
Packit 8f70b4
      const char *comma=find_char(scan,scan_len,',');
Packit 8f70b4
      if(comma)
Packit 8f70b4
      {
Packit 8f70b4
	 scan_len-=comma+1-scan;
Packit 8f70b4
	 scan=comma+1;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   if(name==0 || !type_known)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   fi=new FileInfo(xstring::get_tmp(name,name_len));
Packit 8f70b4
   if(size!=NO_SIZE)
Packit 8f70b4
      fi->SetSize(size);
Packit 8f70b4
   if(date!=NO_DATE)
Packit 8f70b4
      fi->SetDate(date,0);
Packit 8f70b4
   if(type_known)
Packit 8f70b4
   {
Packit 8f70b4
      if(dir)
Packit 8f70b4
	 fi->SetType(fi->DIRECTORY);
Packit 8f70b4
      else
Packit 8f70b4
	 fi->SetType(fi->NORMAL);
Packit 8f70b4
   }
Packit 8f70b4
   if(perms!=-1)
Packit 8f70b4
      fi->SetMode(perms);
Packit 8f70b4
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
                 0          DIR  06-27-96  11:57  PROTOCOL
Packit 8f70b4
               169               11-29-94  09:20  SYSLEVEL.MPT
Packit 8f70b4
*/
Packit 8f70b4
static
Packit 8f70b4
FileInfo *ParseFtpLongList_OS2(char *line,int *err,const char *tz)
Packit 8f70b4
{
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
Packit 8f70b4
   char *t = FIRST_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   long long size;
Packit 8f70b4
   if(sscanf(t,"%lld",&size)!=1)
Packit 8f70b4
      ERR;
Packit 8f70b4
   fi=new FileInfo;
Packit 8f70b4
   fi->SetSize(size);
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   fi->SetType(fi->NORMAL);
Packit 8f70b4
   if(!strcmp(t,"DIR"))
Packit 8f70b4
   {
Packit 8f70b4
      fi->SetType(fi->DIRECTORY);
Packit 8f70b4
      t = NEXT_TOKEN;
Packit 8f70b4
      if(t==0)
Packit 8f70b4
	 ERR;
Packit 8f70b4
   }
Packit 8f70b4
   int month,day,year;
Packit 8f70b4
   if(sscanf(t,"%2d-%2d-%2d",&month,&day,&year)!=3)
Packit 8f70b4
      ERR;
Packit 8f70b4
   if(year>=70)
Packit 8f70b4
      year+=1900;
Packit 8f70b4
   else
Packit 8f70b4
      year+=2000;
Packit 8f70b4
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   int hour,minute;
Packit 8f70b4
   if(sscanf(t,"%2d:%2d",&hour,&minute)!=3)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   struct tm tms;
Packit 8f70b4
   tms.tm_sec=30;	   /* seconds after the minute [0, 61]  */
Packit 8f70b4
   tms.tm_min=minute;      /* minutes after the hour [0, 59] */
Packit 8f70b4
   tms.tm_hour=hour;	   /* hour since midnight [0, 23] */
Packit 8f70b4
   tms.tm_mday=day;	   /* day of the month [1, 31] */
Packit 8f70b4
   tms.tm_mon=month-1;     /* months since January [0, 11] */
Packit 8f70b4
   tms.tm_year=year-1900;  /* years since 1900 */
Packit 8f70b4
   tms.tm_isdst=-1;
Packit 8f70b4
   fi->SetDate(mktime_from_tz(&tms,tz),30);
Packit 8f70b4
Packit 8f70b4
   t=strtok(NULL,"");
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   while(*t==' ')
Packit 8f70b4
      t++;
Packit 8f70b4
   if(*t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
   fi->SetName(t);
Packit 8f70b4
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static
Packit 8f70b4
FileInfo *ParseFtpLongList_MacWebStar(char *line,int *err,const char *tz)
Packit 8f70b4
{
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
Packit 8f70b4
   char *t = FIRST_TOKEN;
Packit 8f70b4
   if(t==0)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   fi=new FileInfo;
Packit 8f70b4
   switch(t[0])
Packit 8f70b4
   {
Packit 8f70b4
   case('l'):  // symlink
Packit 8f70b4
      fi->SetType(fi->SYMLINK);
Packit 8f70b4
      break;
Packit 8f70b4
   case('d'):  // directory
Packit 8f70b4
      fi->SetType(fi->DIRECTORY);
Packit 8f70b4
      break;
Packit 8f70b4
   case('-'):  // plain file
Packit 8f70b4
      fi->SetType(fi->NORMAL);
Packit 8f70b4
      break;
Packit 8f70b4
   case('b'): // block
Packit 8f70b4
   case('c'): // char
Packit 8f70b4
   case('p'): // pipe
Packit 8f70b4
   case('s'): // sock
Packit 8f70b4
      return 0;  // ignore
Packit 8f70b4
   default:
Packit 8f70b4
      ERR;
Packit 8f70b4
   }
Packit 8f70b4
   mode_t mode=parse_perms(t+1);
Packit 8f70b4
   if(mode==(mode_t)-1)
Packit 8f70b4
      ERR;
Packit 8f70b4
   // permissions are meaningless here.
Packit 8f70b4
Packit 8f70b4
   // "folder" or 0
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(!t)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   if(strcmp(t,"folder"))
Packit 8f70b4
   {
Packit 8f70b4
      // size?
Packit 8f70b4
      t = NEXT_TOKEN;
Packit 8f70b4
      if(!t)
Packit 8f70b4
	 ERR;
Packit 8f70b4
      // size
Packit 8f70b4
      t = NEXT_TOKEN;
Packit 8f70b4
      if(!t)
Packit 8f70b4
	 ERR;
Packit 8f70b4
      if(isdigit((unsigned char)*t))
Packit 8f70b4
      {
Packit 8f70b4
	 long long size;
Packit 8f70b4
	 if(sscanf(t,"%lld",&size)==1)
Packit 8f70b4
	    fi->SetSize(size);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 ERR;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      // ??
Packit 8f70b4
      t = NEXT_TOKEN;
Packit 8f70b4
      if(!t)
Packit 8f70b4
	 ERR;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // month
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(!t)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   struct tm date;
Packit 8f70b4
   memset(&date,0,sizeof(date));
Packit 8f70b4
Packit 8f70b4
   date.tm_mon=parse_month(t);
Packit 8f70b4
   if(date.tm_mon==-1)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   const char *day_of_month = NEXT_TOKEN;
Packit 8f70b4
   if(!day_of_month)
Packit 8f70b4
      ERR;
Packit 8f70b4
   date.tm_mday=atoi(day_of_month);
Packit 8f70b4
Packit 8f70b4
   // time or year
Packit 8f70b4
   t = NEXT_TOKEN;
Packit 8f70b4
   if(!t)
Packit 8f70b4
      ERR;
Packit 8f70b4
   if(parse_year_or_time(t,&date.tm_year,&date.tm_hour,&date.tm_min)==-1)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   date.tm_isdst=-1;
Packit 8f70b4
   date.tm_sec=30;
Packit 8f70b4
   int prec=30;
Packit 8f70b4
Packit 8f70b4
   if(date.tm_year==-1)
Packit 8f70b4
      date.tm_year=guess_year(date.tm_mon,date.tm_mday,date.tm_hour,date.tm_min) - 1900;
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      date.tm_hour=12;
Packit 8f70b4
      prec=12*60*60;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   fi->SetDate(mktime_from_tz(&date,tz),prec);
Packit 8f70b4
Packit 8f70b4
   char *name=strtok(NULL,"");
Packit 8f70b4
   if(!name)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   // no symlinks on Mac, but anyway.
Packit 8f70b4
   if(fi->filetype==fi->SYMLINK)
Packit 8f70b4
   {
Packit 8f70b4
      char *arrow=name;
Packit 8f70b4
      while((arrow=strstr(arrow," -> "))!=0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(arrow!=name && arrow[4]!=0)
Packit 8f70b4
	 {
Packit 8f70b4
	    *arrow=0;
Packit 8f70b4
	    fi->SetSymlink(arrow+4);
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 arrow++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   fi->SetName(name);
Packit 8f70b4
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; /
Packit 8f70b4
Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; ..
Packit 8f70b4
Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin
Packit 8f70b4
Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub
Packit 8f70b4
Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ
Packit 8f70b4
modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name
Packit 8f70b4
modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name
Packit 8f70b4
modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www
Packit 8f70b4
*/
Packit 8f70b4
FileInfo *ParseFtpLongList_MLSD(char *line,int *err,const char *)
Packit 8f70b4
{
Packit 8f70b4
   FileInfo *fi=0;
Packit 8f70b4
Packit 8f70b4
   const char *name=0;
Packit 8f70b4
   off_t size=NO_SIZE;
Packit 8f70b4
   time_t date=NO_DATE;
Packit 8f70b4
   const char *owner=0;
Packit 8f70b4
   const char *group=0;
Packit 8f70b4
   FileInfo::type type=FileInfo::UNKNOWN;
Packit 8f70b4
   int perms=-1;
Packit 8f70b4
Packit 8f70b4
   char *space=strstr(line,"; ");
Packit 8f70b4
   if(space) {
Packit 8f70b4
      name=space+2;
Packit 8f70b4
      *space=0;
Packit 8f70b4
   } else {
Packit 8f70b4
      /* NcFTPd does not put a semicolon after last fact, workaround it. */
Packit 8f70b4
      space=strchr(line,' ');
Packit 8f70b4
      if(!space)
Packit 8f70b4
	 ERR;
Packit 8f70b4
      name=space+1;
Packit 8f70b4
      *space=0;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   for(char *tok=strtok(line,";"); tok; tok=strtok(0,";"))
Packit 8f70b4
   {
Packit 8f70b4
      if(!strcasecmp(tok,"Type=cdir")
Packit 8f70b4
      || !strcasecmp(tok,"Type=pdir")
Packit 8f70b4
      || !strcasecmp(tok,"Type=dir"))
Packit 8f70b4
      {
Packit 8f70b4
	 type=FileInfo::DIRECTORY;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strcasecmp(tok,"Type=file"))
Packit 8f70b4
      {
Packit 8f70b4
	 type=FileInfo::NORMAL;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strcasecmp(tok,"Type=OS.unix=symlink"))
Packit 8f70b4
      {
Packit 8f70b4
	 type=FileInfo::SYMLINK;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"Modify=",7))
Packit 8f70b4
      {
Packit 8f70b4
	 date=Ftp::ConvertFtpDate(tok+7);
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"Size=",5))
Packit 8f70b4
      {
Packit 8f70b4
	 long long size_ll;
Packit 8f70b4
	 if(sscanf(tok+5,"%lld",&size_ll)==1)
Packit 8f70b4
	    size=size_ll;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"Perm=",5))
Packit 8f70b4
      {
Packit 8f70b4
	 perms=0;
Packit 8f70b4
	 for(tok+=5; *tok; tok++)
Packit 8f70b4
	 {
Packit 8f70b4
	    switch(to_ascii_lower(*tok))
Packit 8f70b4
	    {
Packit 8f70b4
	    case 'e': perms|=0111; break;
Packit 8f70b4
	    case 'l': perms|=0444; break;
Packit 8f70b4
	    case 'r': perms|=0444; break;
Packit 8f70b4
	    case 'c': perms|=0200; break;
Packit 8f70b4
	    case 'w': perms|=0200; break;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"UNIX.mode=",10))
Packit 8f70b4
      {
Packit 8f70b4
	 if(sscanf(tok+10,"%o",&perms)!=1)
Packit 8f70b4
	    perms=-1;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"UNIX.owner=",11))
Packit 8f70b4
      {
Packit 8f70b4
	 owner=tok+11;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"UNIX.group=",11))
Packit 8f70b4
      {
Packit 8f70b4
	 group=tok+11;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"UNIX.uid=",9))
Packit 8f70b4
      {
Packit 8f70b4
	 if(!owner)
Packit 8f70b4
	    owner=tok+9;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(tok,"UNIX.gid=",9))
Packit 8f70b4
      {
Packit 8f70b4
	 if(!group)
Packit 8f70b4
	    group=tok+9;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(name==0 || !*name || type==FileInfo::UNKNOWN)
Packit 8f70b4
      ERR;
Packit 8f70b4
Packit 8f70b4
   fi=new FileInfo(name);
Packit 8f70b4
   if(size!=NO_SIZE)
Packit 8f70b4
      fi->SetSize(size);
Packit 8f70b4
   if(date!=NO_DATE)
Packit 8f70b4
      fi->SetDate(date,0);
Packit 8f70b4
   fi->SetType(type);
Packit 8f70b4
   if(perms!=-1)
Packit 8f70b4
      fi->SetMode(perms);
Packit 8f70b4
   if(owner)
Packit 8f70b4
      fi->SetUser(owner);
Packit 8f70b4
   if(group)
Packit 8f70b4
      fi->SetGroup(group);
Packit 8f70b4
Packit 8f70b4
   return fi;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Ftp::FtpLineParser Ftp::line_parsers[number_of_parsers]={
Packit 8f70b4
   ParseFtpLongList_UNIX,
Packit 8f70b4
   ParseFtpLongList_NT,
Packit 8f70b4
   ParseFtpLongList_EPLF,
Packit 8f70b4
   ParseFtpLongList_MLSD,
Packit 8f70b4
   ParseFtpLongList_AS400,
Packit 8f70b4
   ParseFtpLongList_OS2,
Packit 8f70b4
   ParseFtpLongList_MacWebStar,
Packit 8f70b4
};