"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "lurkftp/ftpsupt.c" of archive lurkftp-1.00.0.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Support routines for lurkftp
    3  *
    4  * (C) 1997 Thomas J. Moore
    5  * questions/comments -> dark@mama.indstate.edu
    6  *
    7  * This is free software.  No warranty for applicability or fitness is
    8  * expressed or implied.
    9  *
   10  * This code may be modified and distributed freely so long as the original
   11  * author is credited and any changes are documented as such.
   12  *
   13  */
   14 
   15 #include "lurkftp.h"
   16 #include <setjmp.h>
   17 #include <dirent.h>
   18 #include <sys/types.h>
   19 #include <sys/socket.h>
   20 #include <sys/time.h>
   21 #include <pwd.h>
   22 
   23 /* some generic scratch buffers that prevent this */
   24 /* from ever being thread-safe */
   25 static char buf[4096];
   26 static char name[4096];
   27 
   28 #define ret_lo(x) do{ftp_closeconn(site);return (x);}while(0)
   29 
   30 #define copyname(s) copystr(s,strlen(s),&dir->names)
   31 
   32 /* read remote dir */
   33 static int doftpdir(struct dirmem *dir, struct ftpsite *site,
   34 		    const char **dirs, int ndirs, int recurse,
   35 		    const regex_t *xfilt)
   36 {
   37     char *s;
   38     const char *cd;
   39     unsigned long i, j;
   40     int noparm = 0;
   41     struct fnamemem *fm = NULL; /* initialized to satisfy GCC */
   42     struct fname *f;
   43     int res;
   44 
   45     if((res = ftp_openconn(site)) != ERR_OK)
   46       ret_lo(res);
   47     if((res = ftp_type(site, "A")) != ERR_OK)
   48       ret_lo(res);
   49     if(dir->count) {
   50 	f = lastname(&dir->files);
   51 	do {
   52 	    s = buf;
   53 	    if(**dirs != '/') {
   54 		if(ftp_getwd(site, buf) != ERR_OK)
   55 		  ret_lo(ERR_PWD);
   56 		s = buf+strlen(buf);
   57 		if(s == buf || s[-1] != '/')
   58 		  *s++ = '/';
   59 	    }
   60 	    strcpy(s,*dirs);
   61 	    s += strlen(s);
   62 	    if(s == buf || s[-1] != '/') {
   63 		*s++ = '/';
   64 		*s = 0;
   65 	    }
   66 	} while(strcmp(f->root, buf) && dirs++ && --ndirs);
   67 	cd = f->root;
   68     } else
   69       cd = NULL;
   70     for(;ndirs--;dirs++, cd = NULL) {
   71 	if(!cd) {
   72 	    s = buf;
   73 	    if(recurse > 1)
   74 	      recurse -= 2;
   75 	    if(**dirs != '/') {
   76 		if(ftp_getwd(site, buf) != ERR_OK)
   77 		  ret_lo(ERR_PWD);
   78 		s = buf+strlen(buf);
   79 		if(s == buf || s[-1] != '/')
   80 		  *s++ = '/';
   81 	    }
   82 	    strcpy(s,*dirs);
   83 	    s += strlen(s);
   84 	    if(s == buf || s[-1] != '/') {
   85 		*s++ = '/';
   86 		*s = 0;
   87 	    }
   88 	    cd = copyname(buf);
   89 	    if(!cd)
   90 	      ret_lo(ERR_MEM);
   91 	} else
   92 	    recurse += 2;
   93 	if(!recurse) {
   94 	    /* first try single recursive list command */
   95 	    strcpy(buf,cd);
   96 	    /* strip trailing / for command */
   97 	    if(buf[1])
   98 	      buf[strlen(buf)-1] = 0;
   99 	    if((i = ftp_port(site)) == ERR_OK &&
  100 	       (i=ftp_cmd2(site, "LIST -lRa ", buf)) == ERR_OK &&
  101 	       (i=parsels(dir,(read_func)ftp_read,site,cd,cd)) == ERR_OK &&
  102 	       dir->count > 1) {
  103 		/* recover already-read dirs under the assumption */
  104 		/* that this recovery is faster than re-FTPing the dir */
  105 		const char *lastdir, *curdir = NULL;
  106 
  107 		res = ftp_endport(site);
  108 		/* need to reread lastdir if endport returns an error */
  109 		if(res == ERR_OK)
  110 		  lastdir = NULL;
  111 		else {
  112 		    f = lastname(&dir->files);
  113 		    lastdir = f->dir;
  114 		}
  115 		if(debug & DB_TRACE)
  116 		  fprintf(stderr, "Restarting at %s\n", lastdir? lastdir: "skipped");
  117 		fm=dir->files;
  118 		f=&fm->names[NSKIP-1];
  119 		curdir = f->dir; /* skip root directory */
  120 		for(i = 0; i < dir->count; i++, f--) {
  121 		    if(i && !(i % NSKIP)) {
  122 			fm = fm->nxt;
  123 			f = &fm->names[NSKIP-1];
  124 		    }
  125 		    if(f->dir == lastdir)
  126 		      break;
  127 		    if(f->dir != curdir) {
  128 			struct fnamemem *fm2;
  129 			struct fname *f2;
  130 
  131 			curdir = f->dir;
  132 			/* I wish there was a better way to do this... */
  133 			/* perhaps store as real tree; future enhancement */
  134 			fm2 = dir->files;
  135 			f2 = &fm2->names[NSKIP-1];
  136 			for(j=0; j < dir->count; j++, f2--) {
  137 			    if(j && !(j % NSKIP)) {
  138 				fm2 = fm2->nxt;
  139 				f2 = &fm2->names[NSKIP-1];
  140 			    }
  141 			    if(f2->flags & FNF_DODIR &&
  142 			       !strncmp(f2->dir, curdir, strlen(f2->dir)) &&
  143 			       !strncmp(f2->name, curdir+strlen(f2->dir), strlen(f2->name)) &&
  144 			       curdir[strlen(f2->dir)+strlen(f2->name)+1] == 0) {
  145 				if(debug & DB_TRACE)
  146 				  fprintf(stderr, "Closing %s%s\n", f2->dir, f2->name);
  147 				f2->flags &= ~FNF_DODIR;
  148 				recurse--;
  149 				break;
  150 			    }
  151 			}
  152 		    }
  153 		    if(f->flags & FNF_DODIR) {
  154 			if(debug & DB_TRACE)
  155 			  fprintf(stderr, "Checking out %s%s\n", f->dir, f->name);
  156 			recurse++;
  157 		    }
  158 		}
  159 		/* remove [partial] directory read */
  160 		if(lastdir) {
  161 		    while(dir->count > 0 &&
  162 			  (f = lastname(&dir->files)) &&
  163 			  f->dir == curdir) {
  164 			freelastname(&dir->files);
  165 			dir->count--;
  166 		    }
  167 		    ret_lo(res);
  168 		}
  169 		/* at this point, I could ret_lo(ERR_DIRR), but maybe the */
  170 		/* skipped dirs really are empty, so read recursively */
  171 		if(recurse) /* more dirs left */
  172 		  recurse = 2;
  173 	    }
  174 	}
  175 	/* special hack for non-dirs */
  176 	/* this will force an error if non-dir isn't a symlink to a dir */
  177 	if(!recurse && i == ERR_OK && dir->count == 1 &&
  178 	   (f = lastname(&dir->files)) && /* should always be true */
  179 	   !S_ISDIR(f->mode) &&
  180 	   !strncmp(f->dir,f->name,strlen(f->dir)-1)) {
  181 	    recurse = 1;
  182 	    /* remove the non-dir from listing */
  183 	    freelastname(&dir->files);
  184 	    dir->count--;
  185 	}
  186 	/* try manual recursion if recursive list failed */
  187 	if(recurse || i || !dir->count) {
  188 	    const char *root = cd;
  189 
  190 	    i = -1;
  191 	    f = NULL;
  192 	    while(1) {
  193 		/* find new dir to cd to */
  194 		while(++i<dir->count) {
  195 		    if(!f) {
  196 			for(j=i/NSKIP,fm=dir->files;j;j--)
  197 			  fm = fm->nxt;
  198 			f = &fm->names[NSKIP-1-i%NSKIP];
  199 		    } else if(!(i % NSKIP)) {
  200 			fm = fm->nxt;
  201 			f = &fm->names[NSKIP-1];
  202 		    } else
  203 		      f--;
  204 		    if(f->flags & FNF_DODIR) {
  205 			strcpy(buf,f->dir);
  206 			strcat(buf,f->name);
  207 			s = buf+strlen(buf);
  208 			if(s[-1] != '/') {
  209 			    *s++ = '/';
  210 			    *s = 0;
  211 			}
  212 			if(!xfilt || regexec(xfilt, buf, 0, NULL, 0)) {
  213 			    cd = copyname(buf);
  214 			    if(!cd)
  215 			      ret_lo(ERR_MEM);
  216 			    break;
  217 			}
  218 		    }
  219 
  220 		}
  221 		if(i && i >= dir->count)
  222 		  break;
  223 		/* strip trailing / for command */
  224 		strcpy(buf,cd);
  225 		if(buf[1])
  226 		  buf[strlen(buf)-1] = 0;
  227 		if((j = ftp_chdir(site, buf)) == ERR_OK) {
  228 		    if(noparm || (j = ftp_port(site)) != ERR_OK ||
  229 		       (j = ftp_cmd(site,"list -a")) != ERR_OK) {
  230 			noparm = 1;
  231 			if((j = ftp_port(site)) == ERR_OK)
  232 			  j = ftp_cmd(site,"list");
  233 		    }
  234 		    if(j == ERR_OK) {
  235 			if((j=parsels(dir,(read_func)ftp_read,site,cd,root)) != ERR_OK)
  236 			  ret_lo(j);
  237 			if((j=ftp_endport(site)) != ERR_OK) {
  238 			    /* remove [partial] directory read */
  239 			    if(dir->count > 0) {
  240 				f = lastname(&dir->files);
  241 				if(!strcmp(f->dir, cd)) {
  242 				    cd = f->dir;
  243 				    while(dir->count > 0 &&
  244 					  (f = lastname(&dir->files)) &&
  245 					  f->dir == cd) {
  246 					freelastname(&dir->files);
  247 					dir->count--;
  248 				    }
  249 				}
  250 				ret_lo(res);
  251 			    }
  252 			    ret_lo(j);
  253 			}
  254 		    } else
  255 		      ret_lo(j);
  256 		} else {
  257 		    lockof();
  258 		    fprintf(of, "*** %s: Skipping %s: %s\n", site->host, buf, site->lastresp);
  259 		    unlockof();
  260 		}
  261 		/* the next stuff is in case server doesn't take parameters */
  262 		/* for list, but doesn't report an error */
  263 		if(!noparm && !dir->count)
  264 		    noparm = 1;
  265 		/* at this point, the read must have been "successful", so */
  266 		/* just mark it as done even if it was empty */
  267 		else if(f)
  268 		  f->flags &= ~FNF_DODIR;
  269 	    }
  270 	}
  271     }
  272     i = toarray(dir);
  273     if(i)
  274       ret_lo(i);
  275     return i;
  276 }
  277 
  278 /* read remote dir w/ retry */
  279 int readftpdir(struct dirmem *dir, struct ftpsite *site, const char **dirs,
  280 	       int ndirs, int recurse, const regex_t *xfilt)
  281 {
  282     int i,err = 0;
  283     struct timeval rt;
  284 
  285     for(i=site->to.retrycnt?site->to.retrycnt:-1;i;i--) {
  286 	if(debug & DB_TRACE)
  287 	  fprintf(stderr,"Trying %s [#%d]\n",site->host,site->to.retrycnt-i+1);
  288 	if((err=doftpdir(dir, site, dirs, ndirs, recurse, xfilt)) && err != ERR_DIR) {
  289 	    if(debug & DB_TRACE)
  290 	      fprintf(stderr,"%s; waiting %d seconds\n",ftperr(err),site->to.retrytime);
  291 	    rt.tv_sec = site->to.retrytime;
  292 	    rt.tv_usec = 0;
  293 	    select(0,NULL,NULL,NULL,&rt);
  294 	} else
  295 	  break;
  296     }
  297     /* return specific error even when max retries */
  298     /* this is so report shows exact error */
  299     return !err && !i ? ERR_MAXRETR : err;
  300 }
  301 
  302 /* read remote dir from remote file w/ retry */
  303 /* FIXME: this should read directly instead of using temp file */
  304 int readftplsf(struct dirmem *dir, struct ftpsite *site, const char *dirs,
  305 	       const char *rname)
  306 {
  307     FILE *f;
  308     int err;
  309     char *s;
  310 
  311     strcpy(name,dirs);
  312     if((s = strchr(name,' ')))
  313       *s = 0;
  314     else
  315       s = name+strlen(name);
  316     if(s == name || s[-1] != '/') {
  317 	*s++ = '/';
  318 	*s = 0;
  319     }
  320     if(!(s = copyname(name)))
  321       return ERR_MEM;
  322     if(!(f = tmpfile())) {
  323 	perror("tmp file for ftplsf");
  324 	return ERR_OS;
  325     }
  326     if((err = getfile(f,site,rname))) {
  327 	fclose(f);
  328 	return err;
  329     }
  330     rewind(f);
  331     /* root may be inaccurate if dirs contains more than one dir, but */
  332     /* root is only used by mirroring, which guarantees only one dir */
  333     err = parsels(dir,(read_func)readf,f,s,s);
  334     fclose(f);
  335     if(err == ERR_OK)
  336       err = toarray(dir);
  337     if(err)
  338       ret_lo(err);
  339     return err;
  340 }
  341 
  342 /* retrieve a file: log in if necessary, try restart if f has data */
  343 /* log off if error, else keep connection open */
  344 /* return: 0 == ftp failure; -1 == write failure; 1 = success */
  345 static int retrfile(FILE *f, struct ftpsite *site, const char *rf)
  346 {
  347     long n;
  348     int ret;
  349 
  350     if((ret = ftp_openconn(site)) != ERR_OK)
  351       ret_lo(ret);
  352     if((ret = ftp_type(site, "I")) != ERR_OK)
  353       ret_lo(ret);
  354     if((ret = ftp_port(site)) != ERR_OK)
  355       ret_lo(ret);
  356     if((n = ftell(f)) > 0) {
  357 	sprintf(buf,"%ld",n);
  358 	if(ftp_cmd2(site, "REST ", buf) != ERR_OK)
  359 	  rewind(f);
  360     }
  361     if((ret = ftp_cmd2(site, "RETR ", rf)) != ERR_OK && ret != ERR_CMD)
  362       ret_lo(ret);
  363     n = 0;
  364     do {
  365 	if(n && nosig_fwrite(buf,n,1,f) != 1) {
  366 	    perror("fwrite");
  367 	    ret_lo(ERR_OS);
  368 	}
  369     } while((n=ftp_read(site,buf,4096,0)) > 0 ||
  370 	    errno == EAGAIN || errno == EINTR);
  371     if(n<0)
  372       ret_lo((int)-n);
  373     return ftp_endport(site); /* don't log out for next file */
  374 }
  375 
  376 /* get a file with retry */
  377 int getfile(FILE *f, struct ftpsite *site, const char *fname)
  378 {
  379     int i,err;
  380     struct timeval rt;
  381 
  382     for(err=0,i=site->to.retrycnt?site->to.retrycnt:-1;i;i--) {
  383 	/* retrfile will set f to correct file position */
  384 	if((err=retrfile(f, site, fname)) && (err != ERR_OS || errno == EINTR) && err != ERR_CMD) {
  385 	    if(fflush(f) && errno != EINTR) {
  386 		err = ERR_OS;
  387 		break;
  388 	    }
  389 	    if(debug & DB_TRACE)
  390 	      fprintf(stderr,"%s; waiting %d seconds\n",ftperr(err),site->to.retrytime);
  391 	    rt.tv_sec = site->to.retrytime;
  392 	    rt.tv_usec = 0;
  393 	    select(0,NULL,NULL,NULL,&rt);
  394 	} else
  395 	  break;
  396     }
  397     /* always return MAXRETR if max retries reached */
  398     /* no specific error will be reported, anyway */
  399     /* return !i?ERR_MAXRETR:err; */
  400     return !i && !err?ERR_MAXRETR:err;
  401 }
  402 
  403 /* misc functions */
  404 const char *ftperr(int err)
  405 {
  406     switch(err) {
  407       case ERR_OK:
  408 	return "No error";
  409       case ERR_EOF:
  410 	return "End of File";
  411       case ERR_TO:
  412 	return "Timed out";
  413       case ERR_MEM:
  414 	return "Out of memory";
  415       case ERR_DIR:
  416 	return "Nothing in directory";
  417       case ERR_LOGIN:
  418 	return "Can't log in";
  419       case ERR_PWD:
  420 	return "Can't get current working directory";
  421       case ERR_OS:
  422 	return "OS error";
  423       case ERR_CMD:
  424 	return "Error sending command";
  425       case ERR_MAXRETR:
  426 	return "Maximum retry count reached";
  427     }
  428     return "Unknown error";
  429 }