rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

rpmio/url.c
Go to the documentation of this file.

    00001
    00005 #include "system.h"
    00006
    00007 #include <netinet/in.h>
    00008
    00009 #include <rpmmacro.h>
    00010 #include <rpmcb.h>
    00011 #include <rpmio_internal.h>
    00012 #ifdef WITH_NEON
    00013 #include <rpmdav.h>
    00014 #endif
    00015
    00016 #include "debug.h"
    00017
    00018 /*@access FD_t@*/               /* XXX compared with NULL */
    00019 /*@access urlinfo@*/
    00020
    00021 #ifndef IPPORT_FTP
    00022 #define IPPORT_FTP      21
    00023 #endif
    00024 #ifndef IPPORT_HTTP
    00025 #define IPPORT_HTTP     80
    00026 #endif
    00027 #ifndef IPPORT_HTTPS
    00028 #define IPPORT_HTTPS    443
    00029 #endif
    00030 #ifndef IPPORT_PGPKEYSERVER
    00031 #define IPPORT_PGPKEYSERVER     11371
    00032 #endif
    00033
    00036 /*@-redecl@*/
    00037 int (*urlNotify) (const urlinfo u, unsigned status)
    00038         /*@*/;
    00039 /*@=redecl@*/
    00040
    00043 /*@unchecked@*/ /*@null@*/
    00044 void * urlNotifyArg;
    00045
    00048 /*@unchecked@*/
    00049 int _url_iobuf_size = RPMURL_IOBUF_SIZE;
    00050
    00053 /*@unchecked@*/
    00054 int _url_debug = 0;
    00055
    00056 #define URLDBG(_f, _m, _x)      if ((_url_debug | (_f)) & (_m)) fprintf _x
    00057
    00058 #define URLDBGIO(_f, _x)        URLDBG((_f), RPMURL_DEBUG_IO, _x)
    00059 #define URLDBGREFS(_f, _x)      URLDBG((_f), RPMURL_DEBUG_REFS, _x)
    00060
    00063 /*@unchecked@*/
    00064 /*@only@*/ /*@null@*/
    00065 urlinfo *_url_cache = NULL;
    00066
    00067 static void urlFini(void * _u)
    00068         /*@globals fileSystem, internalState @*/
    00069         /*@modifies _u, fileSystem, internalState @*/
    00070 {
    00071     urlinfo u =_u;
    00072     int xx;
    00073
    00074     if (u->ctrl) {
    00075 #ifndef NOTYET
    00076         void * fp = fdGetFp(u->ctrl);
    00077         if (fp) {
    00078             fdPush(u->ctrl, fpio, fp, -1);   /* Push fpio onto stack */
    00079             xx = Fclose(u->ctrl);
    00080         } else if (fdFileno(u->ctrl) >= 0)
    00081             xx = fdio->close(u->ctrl);
    00082 #else
    00083         xx = Fclose(u->ctrl);
    00084 #endif
    00085
    00086 /*@-usereleased@*/
    00087         u->ctrl = (FD_t)rpmioFreePoolItem((rpmioItem)u->ctrl, "persist ctrl (urlFree)", __FILE__, __LINE__);
    00088         if (u->ctrl)
    00089             fprintf(stderr, _("warning: u %p ctrl %p nrefs != 0 (%s %s)\n"),
    00090                         u, u->ctrl, (u->host ? u->host : ""),
    00091                         (u->scheme ? u->scheme : ""));
    00092 /*@=usereleased@*/
    00093     }
    00094     if (u->data) {
    00095 #ifndef NOTYET
    00096         void * fp = fdGetFp(u->data);
    00097         if (fp) {
    00098             fdPush(u->data, fpio, fp, -1);   /* Push fpio onto stack */
    00099             xx = Fclose(u->data);
    00100         } else if (fdFileno(u->data) >= 0)
    00101             xx = fdio->close(u->data);
    00102 #else
    00103         xx = Fclose(u->ctrl);
    00104 #endif
    00105
    00106 /*@-usereleased@*/
    00107         u->data = (FD_t)rpmioFreePoolItem((rpmioItem)u->data, "persist data (urlFree)", __FILE__, __LINE__);
    00108         if (u->data)
    00109             fprintf(stderr, _("warning: u %p data %p nrefs != 0 (%s %s)\n"),
    00110                         u, u->data, (u->host ? u->host : ""),
    00111                         (u->scheme ? u->scheme : ""));
    00112 /*@=usereleased@*/
    00113     }
    00114 #ifdef WITH_NEON
    00115     xx = davFree(u);
    00116 #endif
    00117     u->etag = _free(u->etag);
    00118     u->location = _free(u->location);
    00119     u->rop = _free(u->rop);
    00120     u->sop = _free(u->sop);
    00121     u->top = _free(u->top);
    00122     u->buf = _free(u->buf);
    00123     u->url = _free(u->url);
    00124     u->scheme = _free((void *)u->scheme);
    00125     u->user = _free((void *)u->user);
    00126     u->password = _free((void *)u->password);
    00127     u->host = _free((void *)u->host);
    00128     u->portstr = _free((void *)u->portstr);
    00129     u->query = _free(u->query);
    00130     u->fragment = _free(u->fragment);
    00131     u->proxyu = _free((void *)u->proxyu);
    00132     u->proxyh = _free((void *)u->proxyh);
    00133 }
    00134
    00137 /*@unchecked@*/
    00138 int _url_count = 0;
    00139
    00140 /*@unchecked@*/ /*@only@*/ /*@null@*/
    00141 rpmioPool _urlPool;
    00142
    00143 static urlinfo urlGetPool(/*@null@*/ rpmioPool pool)
    00144         /*@globals _urlPool, fileSystem @*/
    00145         /*@modifies pool, _urlPool, fileSystem @*/
    00146 {
    00147     urlinfo u;
    00148
    00149     if (_urlPool == NULL) {
    00150         _urlPool = rpmioNewPool("u", sizeof(*u), -1, _url_debug,
    00151                         NULL, NULL, urlFini);
    00152         pool = _urlPool;
    00153     }
    00154     return (urlinfo) rpmioGetPool(pool, sizeof(*u));
    00155 }
    00156
    00157 urlinfo XurlNew(const char *msg, const char *fn, unsigned ln)
    00158 {
    00159     urlinfo u = urlGetPool(_urlPool);
    00160
    00161     u->proxyp = -1;
    00162     u->port = -1;
    00163     u->urltype = URL_IS_UNKNOWN;
    00164     u->ctrl = NULL;
    00165     u->data = NULL;
    00166     u->location = NULL;
    00167     u->etag = NULL;
    00168     u->notify = urlNotify;
    00169 /*@-assignexpose@*/
    00170     u->arg = urlNotifyArg;
    00171 /*@=assignexpose@*/
    00172     u->rop = xcalloc(1, sizeof(*u->rop));
    00173     u->sop = xcalloc(1, sizeof(*u->sop));
    00174     u->top = xcalloc(1, sizeof(*u->top));
    00175     u->bufAlloced = 0;
    00176     u->buf = NULL;
    00177     u->allow = RPMURL_SERVER_HASRANGE;
    00178     u->httpVersion = 0;
    00179     u->magic = URLMAGIC;
    00180     return (urlinfo) rpmioLinkPoolItem((rpmioItem)u, msg, fn, ln);
    00181 }
    00182
    00183 void urlFreeCache(void)
    00184 {
    00185     if (_url_cache) {
    00186         int i;
    00187         for (i = 0; i < _url_count; i++) {
    00188             if (_url_cache[i] == NULL) continue;
    00189             _url_cache[i] = urlFree(_url_cache[i], "_url_cache");
    00190             if (_url_cache[i] == NULL)
    00191                 continue;
    00192             yarnPossess(_url_cache[i]->_item.use);
    00193             fprintf(stderr,
    00194                 _("warning: _url_cache[%d] %p nrefs(%ld) != 1 (%s %s)\n"),
    00195                 i, _url_cache[i], yarnPeekLock(_url_cache[i]->_item.use),
    00196                 (_url_cache[i]->host ? _url_cache[i]->host : ""),
    00197                 (_url_cache[i]->scheme ? _url_cache[i]->scheme : ""));
    00198             yarnRelease(_url_cache[i]->_item.use);
    00199         }
    00200     }
    00201     _url_cache = _free(_url_cache);
    00202     _url_count = 0;
    00203 }
    00204
    00205 static int urlStrcmp(/*@null@*/ const char * str1, /*@null@*/ const char * str2)
    00206         /*@*/
    00207 {
    00208     if (str1)
    00209         if (str2)
    00210             return strcmp(str1, str2);
    00211     if (str1 != str2)
    00212         return -1;
    00213     return 0;
    00214 }
    00215
    00216 /*@-mods@*/
    00217 static void urlFind(/*@null@*/ /*@in@*/ /*@out@*/ urlinfo * uret, int mustAsk)
    00218         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    00219         /*@modifies *uret, rpmGlobalMacroContext, fileSystem, internalState @*/
    00220 {
    00221     urlinfo u;
    00222     int ucx;
    00223     int i = 0;
    00224
    00225     if (uret == NULL)
    00226         return;
    00227
    00228     u = *uret;
    00229     URLSANE(u);
    00230
    00231     ucx = -1;
    00232     for (i = 0; i < _url_count; i++) {
    00233         urlinfo ou = NULL;
    00234         if (_url_cache == NULL || (ou = _url_cache[i]) == NULL) {
    00235             if (ucx < 0)
    00236                 ucx = i;
    00237             continue;
    00238         }
    00239
    00240         /* Check for cache-miss condition. A cache miss is
    00241          *    a) both items are not NULL and don't compare.
    00242          *    b) either of the items is not NULL.
    00243          */
    00244         if (urlStrcmp(u->scheme, ou->scheme))
    00245             continue;
    00246         if (urlStrcmp(u->host, ou->host))
    00247             continue;
    00248         if (urlStrcmp(u->user, ou->user))
    00249             continue;
    00250         if (urlStrcmp(u->portstr, ou->portstr))
    00251             continue;
    00252         break;  /* Found item in cache */
    00253     }
    00254
    00255     if (i == _url_count) {
    00256         if (ucx < 0) {
    00257             ucx = _url_count++;
    00258             _url_cache = xrealloc(_url_cache, sizeof(*_url_cache) * _url_count);
    00259         }
    00260         if (_url_cache)         /* XXX always true */
    00261             _url_cache[ucx] = urlLink(u, "_url_cache (miss)");
    00262         u = urlFree(u, "urlSplit (urlFind miss)");
    00263     } else {
    00264         ucx = i;
    00265         u = urlFree(u, "urlSplit (urlFind hit)");
    00266     }
    00267
    00268     /* This URL is now cached. */
    00269
    00270     if (_url_cache)             /* XXX always true */
    00271         u = urlLink(_url_cache[ucx], "_url_cache");
    00272     *uret = u;
    00273     /*@-usereleased@*/
    00274     u = urlFree(u, "_url_cache (urlFind)");
    00275     /*@=usereleased@*/
    00276 assert(u != NULL);
    00277
    00278     /* Zap proxy host and port in case they have been reset */
    00279     u->proxyp = -1;
    00280     u->proxyh = _free(u->proxyh);
    00281
    00282     /* Perform one-time FTP initialization */
    00283     if (u->urltype == URL_IS_FTP) {
    00284
    00285         if (mustAsk || (u->user != NULL && u->password == NULL)) {
    00286             const char * host = (u->host ? u->host : "");
    00287             const char * user = (u->user ? u->user : "");
    00288             char * prompt;
    00289             prompt = alloca(strlen(host) + strlen(user) + 256);
    00290             sprintf(prompt, _("Password for %s@%s: "), user, host);
    00291             u->password = _free(u->password);
    00292 /*@-dependenttrans -moduncon @*/
    00293             u->password = Getpass(prompt);
    00294 /*@=dependenttrans =moduncon @*/
    00295             if (u->password)
    00296                 u->password = xstrdup(u->password);
    00297         }
    00298
    00299         if (u->proxyh == NULL) {
    00300             const char *proxy = rpmExpand("%{_ftpproxy}", NULL);
    00301             if (proxy && *proxy != '%') {
    00302 /*@observer@*/
    00303                 const char * host = (u->host ? u->host : "");
    00304                 const char *uu = (u->user ? u->user : "anonymous");
    00305                 char *nu = xmalloc(strlen(uu) + sizeof("@") + strlen(host));
    00306                 (void) stpcpy( stpcpy( stpcpy(nu, uu), "@"), host);
    00307                 u->proxyu = nu;
    00308                 u->proxyh = xstrdup(proxy);
    00309             }
    00310             proxy = _free(proxy);
    00311         }
    00312
    00313         if (u->proxyp < 0) {
    00314             const char *proxy = rpmExpand("%{_ftpport}", NULL);
    00315             if (proxy && *proxy != '%') {
    00316                 char *end = NULL;
    00317                 int port = strtol(proxy, &end, 0);
    00318                 if (!(end && *end == '\0')) {
    00319                     fprintf(stderr, _("error: %sport must be a number\n"),
    00320                         (u->scheme ? u->scheme : ""));
    00321                     return;
    00322                 }
    00323                 u->proxyp = port;
    00324             }
    00325             proxy = _free(proxy);
    00326         }
    00327     }
    00328
    00329     /* Perform one-time HTTP initialization */
    00330     if (u->urltype == URL_IS_HTTP || u->urltype == URL_IS_HTTPS || u->urltype == URL_IS_HKP) {
    00331
    00332         if (u->proxyh == NULL) {
    00333             const char *proxy = rpmExpand("%{_httpproxy}", NULL);
    00334             if (proxy && *proxy != '%')
    00335                 u->proxyh = xstrdup(proxy);
    00336             proxy = _free(proxy);
    00337         }
    00338
    00339         if (u->proxyp < 0) {
    00340             const char *proxy = rpmExpand("%{_httpport}", NULL);
    00341             if (proxy && *proxy != '%') {
    00342                 char *end;
    00343                 int port = strtol(proxy, &end, 0);
    00344                 if (!(end && *end == '\0')) {
    00345                     fprintf(stderr, _("error: %sport must be a number\n"),
    00346                         (u->scheme ? u->scheme : ""));
    00347                     return;
    00348                 }
    00349                 u->proxyp = port;
    00350             }
    00351             proxy = _free(proxy);
    00352         }
    00353
    00354     }
    00355
    00356     return;
    00357 }
    00358 /*@=mods@*/
    00359
    00362 /*@observer@*/ /*@unchecked@*/
    00363 static struct urlstring {
    00364 /*@observer@*/ /*@null@*/
    00365     const char * leadin;
    00366     urltype     ret;
    00367 } urlstrings[] = {
    00368     { "file://",        URL_IS_PATH },
    00369     { "ftp://",         URL_IS_FTP },
    00370     { "hkp://",         URL_IS_HKP },
    00371     { "http://",        URL_IS_HTTP },
    00372     { "https://",       URL_IS_HTTPS },
    00373     { "-",              URL_IS_DASH },
    00374     { NULL,             URL_IS_UNKNOWN }
    00375 };
    00376
    00377 urltype urlIsURL(const char * url)
    00378 {
    00379     struct urlstring *us;
    00380
    00381     if (url && *url) {
    00382         for (us = urlstrings; us->leadin != NULL; us++) {
    00383             if (strncmp(url, us->leadin, strlen(us->leadin)))
    00384                 continue;
    00385             return us->ret;
    00386         }
    00387     }
    00388
    00389     return URL_IS_UNKNOWN;
    00390 }
    00391
    00392 /* Return path portion of url (or pointer to NUL if url == NULL) */
    00393 urltype urlPath(const char * url, const char ** pathp)
    00394 {
    00395     const char *path;
    00396     int urltype;
    00397
    00398     path = url;
    00399     urltype = urlIsURL(url);
    00400     switch (urltype) {
    00401     case URL_IS_FTP:
    00402         url += sizeof("ftp://") - 1;
    00403         path = strchr(url, '/');
    00404         if (path == NULL) path = url + strlen(url);
    00405         break;
    00406     case URL_IS_PATH:
    00407         url += sizeof("file://") - 1;
    00408         path = strchr(url, '/');
    00409         if (path == NULL) path = url + strlen(url);
    00410         break;
    00411     case URL_IS_HKP:
    00412         url += sizeof("hkp://") - 1;
    00413         path = strchr(url, '/');
    00414         if (path == NULL) path = url + strlen(url);
    00415         break;
    00416     case URL_IS_HTTP:
    00417         url += sizeof("http://") - 1;
    00418         path = strchr(url, '/');
    00419         if (path == NULL) path = url + strlen(url);
    00420         break;
    00421     case URL_IS_HTTPS:
    00422         url += sizeof("https://") - 1;
    00423         path = strchr(url, '/');
    00424         if (path == NULL) path = url + strlen(url);
    00425         break;
    00426     case URL_IS_UNKNOWN:
    00427         if (path == NULL) path = "";
    00428         break;
    00429     case URL_IS_DASH:
    00430         path = "";
    00431         break;
    00432     }
    00433     if (pathp)
    00434         /*@-observertrans@*/
    00435         *pathp = path;
    00436         /*@=observertrans@*/
    00437     return urltype;
    00438 }
    00439
    00443 static const char * urlStrdup(const char * url)
    00444         /*@*/
    00445 {
    00446     size_t nb = strlen(url);
    00447     char * t = xmalloc(nb + 1 + 1);
    00448     const char * nurl = t;
    00449     while (*url != '\0')
    00450         *t++ = *url++;
    00451     *t = '\0';
    00452     return nurl;
    00453 }
    00454
    00455 /*
    00456  * Split URL into components. The URL can look like
    00457  *      scheme://user:password@host:port/path
    00458   * or as in RFC2732 for IPv6 address
    00459   *    service://user:password@[ip:v6:ad:dr:es:s]:port/path?query#fragment
    00460  */
    00461 /*@-modfilesys@*/
    00462 int urlSplit(const char * url, urlinfo *uret)
    00463 {
    00464     urlinfo u;
    00465     char *myurl;
    00466     char *s, *se, *f, *fe;
    00467
    00468     if (uret == NULL)
    00469         return -1;
    00470     if ((u = urlNew("urlSplit")) == NULL)
    00471         return -1;
    00472
    00473     myurl = xstrdup(url);
    00474     if ((se = strrchr(myurl, '#')) != NULL) {
    00475         *se++ = '\0';
    00476         u->fragment = xstrdup(se);
    00477     }
    00478     if ((se = strrchr(myurl, '?')) != NULL) {
    00479         *se++ = '\0';
    00480         u->query = xstrdup(se);
    00481     }
    00482
    00483     u->url = urlStrdup(myurl);          /* XXX +1 byte for pesky trailing '/' */
    00484     u->urltype = urlIsURL(myurl);
    00485
    00486     se = s = myurl;
    00487     while (1) {
    00488         /* Point to end of next item */
    00489         while (*se && *se != '/') se++;
    00490         /* Item was scheme. Save scheme and go for the rest ...*/
    00491         if (*se && (se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') {
    00492                 se[-1] = '\0';
    00493             u->scheme = xstrdup(s);
    00494             se += 2;    /* skip over "//" */
    00495             s = se++;
    00496             continue;
    00497         }
    00498
    00499         /* Item was everything-but-path. Continue parse on rest */
    00500         *se = '\0';
    00501         break;
    00502     }
    00503
    00504     /* Look for ...@host... */
    00505     fe = f = s;
    00506     while (*fe && *fe != '@') fe++;
    00507     if (*fe == '@') {
    00508         s = fe + 1;
    00509         *fe = '\0';
    00510         /* Look for user:password@host... */
    00511         while (fe > f && *fe != ':') fe--;
    00512         if (*fe == ':') {
    00513             *fe++ = '\0';
    00514             u->password = xstrdup(fe);
    00515         }
    00516         u->user = xstrdup(f);
    00517     }
    00518
    00519     /* Look for ...host:port or [v6addr]:port*/
    00520     fe = f = s;
    00521     if (strchr(fe, '[') && strchr(fe, ']')) {
    00522         fe = strchr(f, ']');
    00523         *f++ = '\0';
    00524         *fe++ = '\0';
    00525     }
    00526 assert(fe != NULL);     /* XXX can't happen */
    00527     while (*fe && *fe != ':') fe++;
    00528     if (*fe == ':') {
    00529         *fe++ = '\0';
    00530         u->portstr = xstrdup(fe);
    00531         if (u->portstr != NULL && u->portstr[0] != '\0') {
    00532             char *end;
    00533             u->port = strtol(u->portstr, &end, 0);
    00534             if (!(end && *end == '\0')) {
    00535                 rpmlog(RPMLOG_ERR, _("url port must be a number\n"));
    00536                 myurl = _free(myurl);
    00537                 u = urlFree(u, "urlSplit (error #3)");
    00538                 return -1;
    00539             }
    00540         }
    00541     }
    00542     u->host = xstrdup(f);
    00543
    00544     if (u->port < 0 && u->scheme != NULL) {
    00545         struct servent *serv;
    00546 /*@-multithreaded -moduncon @*/
    00547         /* HACK hkp:// might lookup "pgpkeyserver" */
    00548         serv = getservbyname(u->scheme, "tcp");
    00549 /*@=multithreaded =moduncon @*/
    00550         if (serv != NULL)
    00551             u->port = (int) ntohs(serv->s_port);
    00552         else if (u->urltype == URL_IS_FTP)
    00553             u->port = IPPORT_FTP;
    00554         else if (u->urltype == URL_IS_HKP)
    00555             u->port = IPPORT_PGPKEYSERVER;
    00556         else if (u->urltype == URL_IS_HTTP)
    00557             u->port = IPPORT_HTTP;
    00558         else if (u->urltype == URL_IS_HTTPS)
    00559             u->port = IPPORT_HTTPS;
    00560     }
    00561
    00562     myurl = _free(myurl);
    00563     if (uret) {
    00564         *uret = u;
    00565 /*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in <rpmlib.h> */
    00566         urlFind(uret, 0);
    00567 /*@=globs =mods @*/
    00568     }
    00569     return 0;
    00570 }
    00571 /*@=modfilesys@*/
    00572
    00573 int urlGetFile(const char * url, const char * dest)
    00574 {
    00575     int rc;
    00576     FD_t sfd = NULL;
    00577     FD_t tfd = NULL;
    00578     const char * sfuPath = NULL;
    00579     int urlType = urlPath(url, &sfuPath);
    00580     char *result;
    00581
    00582     if (*sfuPath == '\0')
    00583         return FTPERR_UNKNOWN;
    00584
    00585     if (dest == NULL) {
    00586         if ((dest = strrchr(sfuPath, '/')) != NULL)
    00587             dest++;
    00588         else
    00589             dest = sfuPath;
    00590     }
    00591     if (dest == NULL)
    00592         return FTPERR_UNKNOWN;
    00593
    00594 /*@-globs -mods@*/      /* Avoid including <rpmmacro.h> everywhere for now */
    00595     if (rpmExpandNumeric("%{?__urlgetfile:1}%{!?__urlgetfile:0}")) {
    00596         result = rpmExpand("%{__urlgetfile ", url, " ", dest, "}", NULL);
    00597         if (result != NULL && strcmp(result, "OK") == 0)
    00598             rc = 0;
    00599         else {
    00600             rpmlog(RPMLOG_DEBUG, D_("failed to fetch URL %s via external command\n"), url);
    00601             rc = FTPERR_UNKNOWN;
    00602         }
    00603         result = _free(result);
    00604         goto exit;
    00605     }
    00606 /*@=globs =mods@*/
    00607
    00608     sfd = Fopen(url, "r.ufdio");
    00609     if (sfd == NULL || Ferror(sfd)) {
    00610         rpmlog(RPMLOG_DEBUG, D_("failed to open %s: %s\n"), url, Fstrerror(sfd));
    00611         rc = FTPERR_UNKNOWN;
    00612         goto exit;
    00613     }
    00614
    00615     /* XXX this can fail if directory in path does not exist. */
    00616     tfd = Fopen(dest, "w");
    00617 if (_url_debug)
    00618 fprintf(stderr, "*** urlGetFile sfd %p %s tfd %p %s\n", sfd, url, (tfd ? tfd : NULL), dest);
    00619     if (tfd == NULL || Ferror(tfd)) {
    00620         rpmlog(RPMLOG_DEBUG, D_("failed to create %s: %s\n"), dest, Fstrerror(tfd));
    00621         rc = FTPERR_UNKNOWN;
    00622         goto exit;
    00623     }
    00624
    00625     switch (urlType) {
    00626     case URL_IS_HTTPS:
    00627     case URL_IS_HTTP:
    00628     case URL_IS_HKP:
    00629     case URL_IS_FTP:
    00630     case URL_IS_PATH:
    00631     case URL_IS_DASH:
    00632     case URL_IS_UNKNOWN:
    00633         if ((rc = ufdGetFile(sfd, tfd))) {
    00634             (void) Unlink(dest);
    00635             /* XXX FIXME: sfd possibly closed by copyData */
    00636             /*@-usereleased@*/ (void) Fclose(sfd) /*@=usereleased@*/ ;
    00637         }
    00638         sfd = NULL;     /* XXX Fclose(sfd) done by ufdGetFile */
    00639         break;
    00640     default:
    00641         rc = FTPERR_UNKNOWN;
    00642         break;
    00643     }
    00644
    00645 exit:
    00646     if (tfd)
    00647         (void) Fclose(tfd);
    00648     if (sfd)
    00649         (void) Fclose(sfd);
    00650
    00651     return rc;
    00652 }