Blame src/GetFileInfo.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2012 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
/* Get information about a path (dir or file).  If _dir is a file, get
Packit 8f70b4
 * information about that file only.  If it's a directory, get information
Packit 8f70b4
 * about files in it.  If _showdir is true, act like ls -d: get information
Packit 8f70b4
 * about the directory itself.
Packit 8f70b4
 *
Packit 8f70b4
 * To find out if a path is a directory, attempt to chdir into it.  If it
Packit 8f70b4
 * succeeds it's a directory, otherwise it's a file (or there was an error).
Packit 8f70b4
 *
Packit 8f70b4
 * If the cache knows the file type of _dir, avoid changing directories if
Packit 8f70b4
 * possible, so cached listings don't touch the connection at all.
Packit 8f70b4
 *
Packit 8f70b4
 * List of cases:
Packit 8f70b4
 *
Packit 8f70b4
 * showdirs off:
Packit 8f70b4
 * 1. We CD into the directory.
Packit 8f70b4
 *    a. We get a listing.  Success.
Packit 8f70b4
 *    b. We fail to get a listing.  Fail.
Packit 8f70b4
 * 2. We fail to CD into the path. We fail to CD to the parent. Fail.
Packit 8f70b4
 * 3. We fail to CD into the path. We CD to the parent and fail to get a listing.
Packit 8f70b4
 *    Do GetInfoArray case.
Packit 8f70b4
 * 4. We fail to CD into the path. We CD to the parent and get a listing.
Packit 8f70b4
 *    a. The path we were looking for is a directory.  Fail.  (If it's a directory,
Packit 8f70b4
 *       we need to get its contents or nothing at all.)
Packit 8f70b4
 *    b. The path we were looking for is not a directory.  Success.
Packit 8f70b4
 *    c. The path we were looking for isn't there.  Do GetInfoArray case.
Packit 8f70b4
 *
Packit 8f70b4
 * showdirs on:
Packit 8f70b4
 *
Packit 8f70b4
 * 1. We CD to the parent and get a listing.  The path we were looking for
Packit 8f70b4
 *    is there.  Success.
Packit 8f70b4
 * 2. We CD to the parent and fail to get a listing.
Packit 8f70b4
 *    a. We CD to the path. (Partial success: we know it exists and is a directory.)
Packit 8f70b4
 *    b. We fail to CD to the path. Do GetInfoArray case.
Packit 8f70b4
 * 3. We fail to CD to the parent.
Packit 8f70b4
 *    a. We CD to the path.  The path is a directory.  Success (don't try to
Packit 8f70b4
 *       get more information.)
Packit 8f70b4
 *    b. We fail to CD to the path.  Fail.
Packit 8f70b4
 * 4. We CD to the parent and fail to get a listing OR
Packit 8f70b4
 *    We CD to the parent and the listing does not contain the path.
Packit 8f70b4
 *    a. We CD to the path.  The path is a directory.  Success (don't try to
Packit 8f70b4
 *       get more information.)
Packit 8f70b4
 *    b. We fail to CD to the path.  Do GetInfoArray case.
Packit 8f70b4
 *
Packit 8f70b4
 * GetInfoArray case:
Packit 8f70b4
 * A. We GetInfoArray in the parent, which tells us something.  We
Packit 8f70b4
 *    know the path exists, and is not a directory that we have access
Packit 8f70b4
 *    to.  Success.
Packit 8f70b4
 * B. We GetInfoArray in the parent, which doesn't tell us anything.
Packit 8f70b4
 *    We have no evidence the path exists at all. Fail.
Packit 8f70b4
 *
Packit 8f70b4
 * If we fail from something in cache, we don't know why, so turn cache off
Packit 8f70b4
 * and attempt to CD into the path to get an error message.
Packit 8f70b4
 *
Packit 8f70b4
 * All of these cases can operate out of cache, so be sure to test both, as
Packit 8f70b4
 * the code flow is often different.  (GetInfoArray never operates out of cache.)
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
Packit 8f70b4
#include "GetFileInfo.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
Packit 8f70b4
GetFileInfo::GetFileInfo(const FileAccessRef& a, const char *_dir, bool _showdir)
Packit 8f70b4
   : ListInfo(0,0), session(a), dir(_dir?_dir:""), origdir(a->GetCwd())
Packit 8f70b4
{
Packit 8f70b4
   showdir=_showdir;
Packit 8f70b4
   state=INITIAL;
Packit 8f70b4
   tried_dir=tried_file=tried_info=false;
Packit 8f70b4
   result=0;
Packit 8f70b4
   li=0;
Packit 8f70b4
   from_cache=0;
Packit 8f70b4
   was_directory=false;
Packit 8f70b4
   prepend_path=true;
Packit 8f70b4
Packit 8f70b4
   const char *bn=basename_ptr(dir);
Packit 8f70b4
   if((bn[0]=='.' && (bn[1]==0 || bn[1]=='/' ||
Packit 8f70b4
                     (bn[1]=='.' && (bn[2]==0 || bn[2]=='/'))))
Packit 8f70b4
   || (bn[0]=='/'))
Packit 8f70b4
   {
Packit 8f70b4
      // . .. / are directories, don't try them as a file.
Packit 8f70b4
      tried_file=true;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
GetFileInfo::~GetFileInfo() {}
Packit 8f70b4
Packit 8f70b4
void GetFileInfo::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   if(session)
Packit 8f70b4
   {
Packit 8f70b4
      session->Close();
Packit 8f70b4
      session->SetCwd(origdir);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int GetFileInfo::Do()
Packit 8f70b4
{
Packit 8f70b4
   int res;
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case INITIAL:
Packit 8f70b4
      state=CHANGE_DIR;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
      if(use_cache)
Packit 8f70b4
      {
Packit 8f70b4
	 int is_dir=-1;
Packit 8f70b4
	 if(last_char(dir)=='/')
Packit 8f70b4
	    is_dir=1;
Packit 8f70b4
	 else
Packit 8f70b4
	    is_dir=FileAccess::cache->IsDirectory(session,dir);
Packit 8f70b4
	 switch(is_dir)
Packit 8f70b4
	 {
Packit 8f70b4
	 case 0:
Packit 8f70b4
	    tried_dir = true; /* it's a file */
Packit 8f70b4
	    from_cache = true;
Packit 8f70b4
	    break;
Packit 8f70b4
	 case 1:
Packit 8f70b4
	    if(!showdir)
Packit 8f70b4
	       tried_file = true; /* it's a dir */
Packit 8f70b4
	    from_cache = true;
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      assert(!tried_dir || !tried_file || !tried_info); /* always do at least one */
Packit 8f70b4
Packit 8f70b4
   case CHANGE_DIR:
Packit 8f70b4
   {
Packit 8f70b4
      if(tried_dir && tried_file && tried_info) {
Packit 8f70b4
	 /* We tried everything; no luck.  Fail. */
Packit 8f70b4
	 if(saved_error_text)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(saved_error_text);
Packit 8f70b4
	    state=DONE;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 /* We don't have an error message.  We may have done everything
Packit 8f70b4
	  * out of cache. */
Packit 8f70b4
	 tried_dir=false;  // this will get error message.
Packit 8f70b4
	 from_cache=false;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      session->SetCwd(origdir);
Packit 8f70b4
      const char *cd_path=0;
Packit 8f70b4
      if(!tried_dir && (tried_file || !showdir))
Packit 8f70b4
      {
Packit 8f70b4
	 /* First, try to treat the path as a directory,
Packit 8f70b4
	  * if we are going to show its contents */
Packit 8f70b4
	 tried_dir=true;
Packit 8f70b4
	 cd_path = dir;
Packit 8f70b4
	 path_to_prefix.set(dir);
Packit 8f70b4
	 was_directory=true;
Packit 8f70b4
      }
Packit 8f70b4
      else if(!tried_file)
Packit 8f70b4
      {
Packit 8f70b4
	 /* Try to treat the path as a file.  If showdir is true,
Packit 8f70b4
	  * this is done first. */
Packit 8f70b4
	 tried_file=true;
Packit 8f70b4
Packit 8f70b4
	 /* Chdir to the parent directory of the path: */
Packit 8f70b4
	 session->Chdir(dir, false);
Packit 8f70b4
	 cd_path = "..";
Packit 8f70b4
Packit 8f70b4
	 path_to_prefix.set(dirname(dir));
Packit 8f70b4
	 was_directory=false;
Packit 8f70b4
      }
Packit 8f70b4
      else if(!tried_info)
Packit 8f70b4
      {
Packit 8f70b4
	 tried_info=true;
Packit 8f70b4
	 /* This is always done after tried_file or a failed tried_dir,
Packit 8f70b4
	  * so we should be in the parent, but let's make sure: */
Packit 8f70b4
	 session->Chdir(dir, false);
Packit 8f70b4
	 session->Chdir("..", false);
Packit 8f70b4
Packit 8f70b4
	 path_to_prefix.set(dirname(dir));
Packit 8f70b4
	 was_directory=false;
Packit 8f70b4
Packit 8f70b4
	 /* We tried both; no luck. Fall back on ARRAY_INFO. */
Packit 8f70b4
	 state=GETTING_INFO_ARRAY;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      /* We still need to Chdir() if we're operating out of cache (that's how you
Packit 8f70b4
       * look up cache entries).  However, we don't really want to change the
Packit 8f70b4
       * directory of the session (ie. send a CWD if we're FTP), so set verify to
Packit 8f70b4
       * false if we're operating out of cache.  */
Packit 8f70b4
      bool cd_verify = !from_cache;
Packit 8f70b4
Packit 8f70b4
      /* We can *not* do this out of cache if 1: dir starts with a ~ and 2: we don't
Packit 8f70b4
       * know the home path.
Packit 8f70b4
       *
Packit 8f70b4
       * Yes we can, usually--we may know the home path in home_auto (FTP), but
Packit 8f70b4
       * GetHome won't return that.  We need to do this if we *really* don't know
Packit 8f70b4
       * it. */
Packit 8f70b4
      /* if(dir[0] == '~' && !session->GetHome())
Packit 8f70b4
	 cd_verify = true; */
Packit 8f70b4
Packit 8f70b4
      session->Chdir(cd_path, cd_verify);
Packit 8f70b4
      state=CHANGING_DIR;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   case CHANGING_DIR:
Packit 8f70b4
      res=session->Done();
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(res<0)
Packit 8f70b4
      {
Packit 8f70b4
	 /* Failed.  Save the error, then go back and try to CD again.
Packit 8f70b4
	  * Only save the first error, so error texts contain the full
Packit 8f70b4
	  * path. */
Packit 8f70b4
	 if(!saved_error_text)
Packit 8f70b4
	    saved_error_text.set(session->StrError(res));
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 if(res==FA::NO_FILE)
Packit 8f70b4
	 {
Packit 8f70b4
	    /* If this is a CWD to the parent, and it failed, we
Packit 8f70b4
	     * can't do GetInfoArray. */
Packit 8f70b4
	    if(!was_directory)
Packit 8f70b4
	       tried_info=true;
Packit 8f70b4
Packit 8f70b4
	    state=CHANGE_DIR;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 SetError(saved_error_text);
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      session->Close();
Packit 8f70b4
      if(!from_cache)
Packit 8f70b4
	 FileAccess::cache->SetDirectory(session,"",true);
Packit 8f70b4
Packit 8f70b4
      /* Now that we've connected, we should have the home directory path. Find out
Packit 8f70b4
       * the real name of the path.  (We may have something like "~/..".) */
Packit 8f70b4
      if(!verify_fn)
Packit 8f70b4
      {
Packit 8f70b4
	 FileAccess::Path pwd(session->GetCwd());
Packit 8f70b4
Packit 8f70b4
	 session->SetCwd(origdir);
Packit 8f70b4
	 session->Chdir(dir, false);
Packit 8f70b4
Packit 8f70b4
	 verify_fn.set(basename_ptr(session->GetCwd()));
Packit 8f70b4
Packit 8f70b4
	 /* go back */
Packit 8f70b4
	 session->SetCwd(pwd);
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      /* Special case: looking up "/". Make a phony entry. */
Packit 8f70b4
      if(showdir && !strcmp(verify_fn, "/"))
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi = new FileInfo(verify_fn);
Packit 8f70b4
	 fi->SetType(fi->DIRECTORY);
Packit 8f70b4
Packit 8f70b4
	 result = new FileSet;
Packit 8f70b4
	 result->Add(fi);
Packit 8f70b4
Packit 8f70b4
	 path_to_prefix.set(dirname(dir));
Packit 8f70b4
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(was_directory && showdir)
Packit 8f70b4
      {
Packit 8f70b4
	 /* We could chdir to the dir, but we should not get dir listing.
Packit 8f70b4
	  * We got here because either we could not get dir listing of
Packit 8f70b4
	  * parent directory or the file name was not found in parent
Packit 8f70b4
	  * directory index. */
Packit 8f70b4
	 FileInfo *fi = new FileInfo(dir);
Packit 8f70b4
	 fi->SetType(fi->DIRECTORY);
Packit 8f70b4
	 path_to_prefix.set(dirname(dir));
Packit 8f70b4
Packit 8f70b4
	 result = new FileSet;
Packit 8f70b4
	 result->Add(fi);
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      /* Get a listing: */
Packit 8f70b4
      li=session->MakeListInfo();
Packit 8f70b4
      if(follow_symlinks) li->FollowSymlinks();
Packit 8f70b4
      li->UseCache(use_cache);
Packit 8f70b4
      li->NoNeed(FileInfo::ALL_INFO); /* clear need */
Packit 8f70b4
      li->Need(need);
Packit 8f70b4
      li->SetExclude(exclude_prefix, exclude);
Packit 8f70b4
      state=GETTING_LIST;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
   case GETTING_LIST:
Packit 8f70b4
      if(li->Error()) {
Packit 8f70b4
	 /* If we're listing contents of dirs, and this was listing
Packit 8f70b4
	  * a path (as a directory), fail: */
Packit 8f70b4
	 if(!showdir && was_directory)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(li->ErrorText());
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 if(!saved_error_text)
Packit 8f70b4
	    saved_error_text.set(li->ErrorText());
Packit 8f70b4
Packit 8f70b4
	 /* Otherwise, go on to try the next mode. */
Packit 8f70b4
	 state=CHANGE_DIR;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!li->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      state=DONE;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
      /* Got the list.  Steal it from the listinfo: */
Packit 8f70b4
      result=li->GetResult();
Packit 8f70b4
      li=0;
Packit 8f70b4
Packit 8f70b4
      /* If this was a listing of the basename: */
Packit 8f70b4
      if(!was_directory) {
Packit 8f70b4
	 verify_fn.rtrim('/');
Packit 8f70b4
Packit 8f70b4
	 /* Find the file with our filename: */
Packit 8f70b4
	 const FileInfo *file = result->FindByName(verify_fn);
Packit 8f70b4
Packit 8f70b4
	 if(!file) {
Packit 8f70b4
	    /* It doesn't exist, or we have no result (failed). */
Packit 8f70b4
	    result=0;
Packit 8f70b4
	    tried_file=true;
Packit 8f70b4
	    from_cache=false;
Packit 8f70b4
	    state=CHANGE_DIR;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 /* If we're not listing directories as files, and the file is a
Packit 8f70b4
	  * directory, we should have been able to Chdir into it to begin
Packit 8f70b4
	  * with.  We probably got Access Denied.  Fail. */
Packit 8f70b4
	 if(!showdir && (file->defined&file->TYPE) && file->filetype==FileInfo::DIRECTORY) {
Packit 8f70b4
	    result=0;
Packit 8f70b4
	    if(saved_error_text)
Packit 8f70b4
	    {
Packit 8f70b4
	       SetError(saved_error_text);
Packit 8f70b4
	       goto done;
Packit 8f70b4
	    }
Packit 8f70b4
	    tried_file=true;
Packit 8f70b4
	    from_cache=false;
Packit 8f70b4
	    state=CHANGE_DIR;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 FileInfo *copy = new FileInfo(*file);
Packit 8f70b4
	 result=new FileSet();
Packit 8f70b4
	 result->Add(copy);
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      goto done;
Packit 8f70b4
Packit 8f70b4
   case GETTING_INFO_ARRAY:
Packit 8f70b4
      if(session->IsClosed())
Packit 8f70b4
      {
Packit 8f70b4
	 /*
Packit 8f70b4
	  * Try to get requested information with GetInfoArray.  This
Packit 8f70b4
	  * also serves as a last attempt to see if the file exists--we
Packit 8f70b4
	  * only get here if everything else thinks the path doesn't exist.
Packit 8f70b4
	  */
Packit 8f70b4
	 FileInfo *fi=new FileInfo(verify_fn);
Packit 8f70b4
	 fi->need=need;
Packit 8f70b4
	 /* We need to do at least one. */
Packit 8f70b4
	 if(!fi->need)
Packit 8f70b4
	    fi->need=fi->DATE;
Packit 8f70b4
	 get_info.Empty();
Packit 8f70b4
	 get_info.Add(fi);
Packit 8f70b4
	 session->GetInfoArray(&get_info);
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      res=session->Done();
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(res < 0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(!saved_error_text)
Packit 8f70b4
	    saved_error_text.set(session->StrError(res));
Packit 8f70b4
	 state=CHANGE_DIR;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      session->Close();
Packit 8f70b4
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=get_info[0];
Packit 8f70b4
	 if(!fi->HasAny(fi->SIZE|fi->DATE))
Packit 8f70b4
	 {
Packit 8f70b4
	    /* We didn't get any information.  The file probably doesn't
Packit 8f70b4
	     * exist.  Not necessarily: it might have been a directory
Packit 8f70b4
	     * that we don't have access to CD into.  Some servers will
Packit 8f70b4
	     * refuse to give even an MDTM for directories.  We could
Packit 8f70b4
	     * scan the MDTM and/or SIZE responses for "not a plain file"
Packit 8f70b4
	     * for some servers (proftpd). */
Packit 8f70b4
	    state=CHANGE_DIR;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 /* We got at least one, so the file exists. Return what we know. */
Packit 8f70b4
	 was_directory = false;
Packit 8f70b4
Packit 8f70b4
	 result = new FileSet;
Packit 8f70b4
	 result->Add(new FileInfo(*fi));
Packit 8f70b4
      }
Packit 8f70b4
      state=DONE;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
done:
Packit 8f70b4
   case DONE:
Packit 8f70b4
      if(!done)
Packit 8f70b4
      {
Packit 8f70b4
	 if(result && showdir && result->get_fnum()>0)
Packit 8f70b4
	 {
Packit 8f70b4
	    FileInfo *f = (*result)[0];
Packit 8f70b4
	    /* Make sure the filename is what was requested (ie ".."). */
Packit 8f70b4
	    const char *fn = basename_ptr(dir);
Packit 8f70b4
	    f->SetName(*fn? fn:".");
Packit 8f70b4
Packit 8f70b4
	    /* If we're in show_dir mode, was_directory will always be false;
Packit 8f70b4
	     * set it to whether the single file is actually a directory or not. */
Packit 8f70b4
	    if(f->defined&f->TYPE)
Packit 8f70b4
	       was_directory = (f->filetype == f->DIRECTORY);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(result && prepend_path)
Packit 8f70b4
	    result->PrependPath(path_to_prefix);
Packit 8f70b4
	 done=true;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   abort();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *GetFileInfo::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return "";
Packit 8f70b4
Packit 8f70b4
   if(li && !li->Done()) return li->Status();
Packit 8f70b4
Packit 8f70b4
   if(session->IsOpen())
Packit 8f70b4
      return session->CurrentStatus();
Packit 8f70b4
Packit 8f70b4
   return "";
Packit 8f70b4
}