Back to index

avfs  1.0.1
ucftp.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 2000  Miklos Szeredi <miklos@szeredi.hu>
00004     Copyright (C) 2010  Ralf Hoffmann <ralf@boomerangsworld.de>
00005 
00006     This program can be distributed under the terms of the GNU GPL.
00007     See the file COPYING.
00008 */
00009 /*
00010  * This file is based on volatile.c and ftp.c and the write patch for
00011  * ftp.c from the Zemljanka Commander team
00012  */
00013 
00014 #include "avfs.h"
00015 #include "version.h"
00016 #include "state.h"
00017 #include "socket.h"
00018 #include "filebuf.h"
00019 #include "parsels.h"
00020 
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <fcntl.h>
00025 
00026 #define USER_SEP_STR  "@"
00027 #define USER_SEP_CHAR (USER_SEP_STR[0])
00028 
00029 #define FTP_REPLY_TIMEOUT 30000
00030 #define FTP_READ_TIMEOUT 60000
00031 
00032 #define UCFTP_ST_VALID 20
00033 #define UCFTP_DIR_VALID 10
00034 
00035 /***************************************
00036  * some internal structures
00037  ***************************************/
00038 
00039 struct ucftpconn {
00040     char *host;
00041     char *user;
00042     char *password;
00043     int busy;
00044     int sock;
00045     struct filebuf *sockfb;
00046     struct ucftpconn *next;
00047     int binary;
00048     char *cwd;
00049     short ft_cancel_ok;
00050 
00051     struct ucftpentry *root;
00052 };
00053 
00054 struct ucftpfile {
00055     struct ucftpentry *ent;     /* corresponding entry */
00056     int flags;
00057     
00058     /* the following entries are used for files */
00059     int sock;
00060     struct filebuf *sockfb;
00061     avoff_t numbytes;
00062     struct ucftpconn *conn;
00063     int writing;
00064     short eof;
00065 };
00066 
00067 /* a generic information node */
00068 /* analogous to the "on-disk inode" in a disk filesystem */
00069 struct ucftpnode {
00070     struct avstat st;
00071     char *linkname;
00072     avtime_t valid;
00073     struct ucftpentry *subdir;  /* only dir */
00074     struct ucftpentry *parent;  /* only dir */
00075 };
00076 
00077 /* our ventry.data handle */
00078 /* represents a named reference to a ucftpnode */
00079 struct ucftpentry {
00080     char *name;
00081     struct ucftpnode *node;
00082     struct ucftpentry *next;
00083     struct ucftpentry **prevp;
00084     struct ucftpentry *parent;
00085 };
00086 
00087 struct ucftpsession {
00088     char *account;
00089     char *password;
00090     struct ucftpsession *next;
00091     struct ucftpsession *prev;
00092 };
00093 
00094 /* our vmount.data handle */
00095 struct ucftpfs {
00096     struct avfs *avfs;
00097     struct ucftpconn *conns;
00098     struct ucftpsession sessions;
00099 };
00100 
00101 enum ucftp_op { OP_DELE = 0, OP_MKD = 1, OP_RMD = 2};
00102 char *ucftp_op_cmd[3] = { "DELE ", "MKD ", "RMD " };
00103     
00104 /***************************************
00105  * some access functions for generic
00106  * avfs structure
00107  ***************************************/
00108 
00109 static struct ucftpentry *ucftp_ventry_ucftpentry(ventry *ve)
00110 {
00111     return (struct ucftpentry *) ve->data;
00112 }
00113 
00114 static struct ucftpfs *ucftp_ventry_ucftpfs(ventry *ve)
00115 {
00116     return (struct ucftpfs *) ve->mnt->avfs->data;
00117 }
00118 
00119 static struct ucftpfile *ucftp_vfile_ucftpfile(vfile *vf)
00120 {
00121     return (struct ucftpfile *) vf->data;
00122 }
00123 
00124 static struct ucftpfs *ucftp_vfile_fs(vfile *vf)
00125 {
00126     return (struct ucftpfs *) vf->mnt->avfs->data;
00127 }
00128 
00129 /***************************************
00130  * some generic code
00131  ***************************************/
00132 
00133 static void strip_crlf(char *line)
00134 {
00135     avsize_t len = strlen(line);
00136     
00137     if(len > 0 && line[len-1] == '\n') {
00138         if(len > 1 && line[len-2] == '\r')
00139             line[len-2] = '\0';
00140         else
00141             line[len-1] = '\0';
00142     }
00143 }
00144 
00145 static int write_socket(int sock, const char *buf, avsize_t buflen)
00146 {
00147     int res;
00148 
00149     while(buflen > 0) {
00150         res = write(sock, buf, buflen);
00151         if(res == -1)
00152             return -errno;
00153         
00154         buf += res;
00155         buflen -= res;
00156     }
00157 
00158     return 0;
00159 }
00160 
00161 static int ucftp_get_addrbytes(const char *line, int addrbytes[6])
00162 {
00163     int i;
00164     int j;
00165     int val;
00166     const char *s;
00167 
00168     for(s = line; *s && *s != '('; s++);
00169     if(!*s)
00170         return -1;
00171 
00172     s++;
00173     for(i = 0; i < 6; i++) {
00174         val = 0;
00175         for(j = 0; j < 3; j++) {
00176             if(!isdigit((int) *s))
00177                 return -1;
00178             val = val * 10 + (*s - '0');
00179             s++;
00180             if(*s == ',' || *s == ')')
00181                 break;
00182         }
00183         if(*s != ',' && *s != ')')
00184             return -1;
00185         addrbytes[i] = val;
00186         if(*s == ')')
00187             break;
00188         s++;        
00189     }
00190     if(i != 5 || *s != ')')
00191         return -1;
00192 
00193     return 0;
00194 }
00195 
00196 /***************************************
00197  * session code
00198  ***************************************/
00199 
00200 static struct ucftpsession *ucftp_find_session(struct ucftpfs *fs,
00201                                                const char *account)
00202 {
00203     struct ucftpsession *fts;
00204 
00205     for(fts = fs->sessions.next; fts != &fs->sessions; fts = fts->next) {
00206         if(strcmp(account, fts->account) == 0)
00207             return fts;
00208     }
00209     
00210     return NULL;
00211 }
00212 
00213 static struct ucftpsession *ucftp_get_session(struct ucftpfs *fs,
00214                                               const char *account)
00215 {
00216     struct ucftpsession *fts;
00217     
00218     fts = ucftp_find_session(fs, account);
00219     if(fts == NULL) {
00220         struct ucftpsession *next;
00221         struct ucftpsession *prev;
00222 
00223         AV_NEW(fts);
00224         fts->account = av_strdup(account);
00225         fts->password = NULL;
00226 
00227         fts->next = next = fs->sessions.next;
00228         fts->prev = prev = &fs->sessions;
00229         next->prev = fts;
00230         prev->next = fts;
00231     }
00232     
00233     return fts;
00234 }
00235 
00236 static void ucftp_remove_session(struct ucftpsession *fts)
00237 {
00238     struct ucftpsession *next = fts->next;
00239     struct ucftpsession *prev = fts->prev;
00240     
00241     next->prev = prev;
00242     prev->next = next;
00243 
00244     av_free(fts->account);
00245     av_free(fts->password);
00246     av_free(fts);
00247 }
00248 
00249 /***************************************
00250  * code for password/hostname/path splitting
00251  ***************************************/
00252 
00253 static const char *ucftp_get_password(struct ucftpfs *fs, const char *host,
00254                                       const char *user)
00255 {
00256     struct ucftpsession *fts;
00257     char *account;
00258 
00259     account = av_stradd(NULL, user, USER_SEP_STR, host, NULL);
00260     fts = ucftp_find_session(fs, account);
00261     av_free(account);
00262     if(fts == NULL) {
00263         account = av_stradd(NULL, user, USER_SEP_STR, NULL);
00264         fts = ucftp_find_session(fs, account);
00265         av_free(account);
00266     }
00267     
00268     if(fts != NULL)
00269         return fts->password;
00270     else
00271         return NULL;
00272 }
00273 
00274 static int ucftp_split_path(struct ucftpfs *fs, char *hostpart,
00275                             const char **hostp, const char **userp,
00276                             const char **passp)
00277 {
00278     char *s, *t;
00279     const char *host;
00280     const char *user;
00281     const char *pass;
00282 
00283     for(s = hostpart; *s && *s != USER_SEP_CHAR; s++);
00284     for(t = s; *t; t++) if (*t == USER_SEP_CHAR) s = t;
00285     if(*s != '\0') {
00286         *s = '\0';
00287         host = s + 1;
00288         user = hostpart;
00289         pass = ucftp_get_password(fs, host, user);
00290         if(pass == NULL)
00291             return -EACCES;
00292     }
00293     else {
00294         host = hostpart;
00295         user = "ftp";
00296         pass = "avfs@";
00297     }
00298 
00299     if(host[0] == '\0')
00300         return -ENOENT;
00301 
00302     *hostp = host;
00303     *userp = user;
00304     *passp = pass;
00305 
00306     return 0;
00307 }
00308 
00309 /***************************************
00310  * ucftpentry constructor and destructor
00311  ***************************************/
00312 
00313 /* av_obj.destr for ucftpentry */
00314 static void ucftp_entry_destr(struct ucftpentry *ent)
00315 {
00316     if(ent->prevp != NULL)
00317         *ent->prevp = ent->next;
00318     if(ent->next != NULL)
00319         ent->next->prevp = ent->prevp;
00320     av_unref_obj(ent->parent);
00321     av_free(ent->name);
00322 
00323     ent->prevp = NULL;
00324     ent->next = NULL;
00325     ent->parent = NULL;
00326     ent->name = NULL;
00327 }
00328 
00329 /* constructor for ucftpentry */
00330 static struct ucftpentry *ucftp_new_entry(const char *name)
00331 {
00332     struct ucftpentry *ent;
00333 
00334     AV_NEW_OBJ(ent, ucftp_entry_destr);
00335 
00336     ent->node = NULL;
00337     ent->next = NULL;
00338     ent->prevp = NULL;
00339     ent->parent = NULL;
00340     ent->name = av_strdup(name);
00341 
00342     return ent;
00343 }
00344 
00345 /***************************************
00346  * ucftpnode constructor and destructor
00347  ***************************************/
00348 
00349 /* av_obj.destr for ucftpnode */
00350 static void ucftp_node_destr(struct ucftpnode *nod)
00351 {
00352     if(nod->linkname != NULL)
00353         av_free(nod->linkname);
00354 }
00355 
00356 /* constructor for ucftpnode */
00357 static struct ucftpnode *ucftp_new_node(struct avstat *initstat)
00358 {
00359     struct ucftpnode *nod;
00360 
00361     AV_NEW_OBJ(nod, ucftp_node_destr);
00362     
00363     nod->st = *initstat;
00364     nod->subdir = NULL;
00365     nod->parent = NULL;
00366     nod->valid = 0;
00367     nod->linkname = NULL;
00368 
00369     return nod;
00370 }
00371 
00372 /***************************************
00373  * code for node handling
00374  ***************************************/
00375 
00376 /* link ent to nod */
00377 static void ucftp_link_node(struct ucftpentry *ent, struct ucftpnode *nod)
00378 {
00379     //TODO why do we get an ref from ent but not ent->parent (in case it exists)?
00380     av_ref_obj(ent);
00381     av_ref_obj(nod);
00382     ent->node = nod;
00383     
00384     if(AV_ISDIR(nod->st.mode)) {
00385         nod->st.nlink = 2;
00386         if(ent->parent != NULL) {
00387             nod->parent = ent->parent;
00388             ent->parent->node->st.nlink ++;
00389         }
00390         else 
00391             nod->parent = ent;
00392     }
00393     else
00394         nod->st.nlink ++;
00395 
00396     if(ent->parent != NULL)
00397         ent->parent->node->st.size ++;    
00398 }
00399 
00400 static void ucftp_unlink_node(struct ucftpentry *ent)
00401 {
00402     struct ucftpnode *nod = ent->node;
00403     
00404     if(AV_ISDIR(nod->st.mode)) {
00405         nod->st.nlink = 0;
00406         if(nod->parent != NULL)
00407             nod->parent->node->st.nlink --;
00408     }
00409     else
00410         nod->st.nlink --;
00411 
00412     if(ent->parent != NULL)
00413         ent->parent->node->st.size --;
00414 
00415     ent->node = NULL;
00416     av_unref_obj(nod);
00417     av_unref_obj(ent);
00418 }
00419 
00420 static int ucftp_make_node(struct ucftpfs *fs, struct ucftpentry *ent, avmode_t mode)
00421 {
00422     struct ucftpnode *nod;
00423     struct avstat initstat;
00424 
00425     if(ent->name == NULL)
00426         return -ENOENT;
00427 
00428     av_default_stat(&initstat);
00429     
00430     initstat.dev = fs->avfs->dev;
00431     initstat.ino = av_new_ino(fs->avfs);
00432 
00433     nod = ucftp_new_node(&initstat);
00434     nod->st.mode = mode;
00435     
00436     ucftp_link_node(ent, nod);
00437     av_unref_obj(nod);
00438 
00439     return 0;
00440 }
00441 
00442 static int ucftp_is_valid_node(struct ucftpnode *node)
00443 {
00444     avtime_t now = av_time();
00445 
00446     if(now < node->valid)
00447         return 1;
00448     return 0;
00449 }
00450 
00451 static void ucftp_truncate_node(struct ucftpnode *nod, avoff_t length)
00452 {
00453     nod->st.size = length;
00454     nod->st.blocks = AV_DIV(nod->st.size, 512);
00455     av_curr_time(&nod->st.mtime);
00456 }
00457 
00458 /***************************************
00459  * entry handling
00460  ***************************************/
00461 
00462 static int ucftp_add_subentry(struct ucftpentry *parent, struct ucftpentry *ent, struct ucftpentry **startp)
00463 {
00464     struct ucftpentry **entp;
00465 
00466     if(startp == NULL) {
00467         entp = &parent->node->subdir;
00468     } else {
00469         entp = startp;
00470     }
00471 
00472     for(; *entp != NULL; entp = &(*entp)->next);
00473 
00474     *entp = ent;
00475     ent->prevp = entp;
00476     
00477     ent->parent = parent;
00478     av_ref_obj(parent);
00479     
00480     ent->next = NULL;
00481     
00482     return 0;
00483 }
00484 
00485 static struct ucftpentry *find_list_entry(struct ucftpentry *oldlist, const char *filename)
00486 {
00487     for(; oldlist != NULL; oldlist = oldlist->next) {
00488         if(strcmp(oldlist->name, filename) == 0)
00489             break;
00490     }
00491     return oldlist;
00492 }
00493 
00494 static void list_remove_entry(struct ucftpentry *ent)
00495 {
00496     if(ent == NULL)
00497         return;
00498     
00499     if(ent->prevp != NULL)
00500         *ent->prevp = ent->next;
00501     if(ent->next != NULL)
00502         ent->next->prevp = ent->prevp;
00503     
00504     ent->prevp = NULL;
00505     ent->next = NULL;
00506 }
00507 
00508 /* free a all subentries from a given entry */
00509 static void ucftp_free_tree(struct ucftpentry *ent)
00510 {
00511     struct ucftpnode *nod = ent->node;
00512 
00513     if(nod != NULL) {
00514         while(nod->subdir != NULL)
00515             ucftp_free_tree(nod->subdir);
00516         
00517         ucftp_unlink_node(ent);
00518     } else {
00519         av_unref_obj(ent);
00520     }
00521 }
00522 
00523 /* called by ucftp_getpath */
00524 static char *ucftp_create_path(struct ucftpentry *ent)
00525 {
00526     char *path;
00527     
00528     if(ent->parent == NULL)
00529         return av_strdup("");
00530     
00531     path = ucftp_create_path(ent->parent);
00532 
00533     return av_stradd(path, "/", ent->name, NULL);
00534 }
00535 
00536 static int ucftp_getpath(ventry *ve, char **resp)
00537 {
00538     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
00539 
00540     *resp = ucftp_create_path(ent);
00541 
00542     return 0;
00543 }
00544 
00545 static void ucftp_free_dirlist(struct ucftpentry *ent)
00546 {
00547     if(ent->node != NULL) {
00548         struct ucftpentry *subent = ent->node->subdir;
00549         
00550         while(subent != NULL) {
00551             ucftp_free_tree(subent);
00552 
00553             subent = ent->node->subdir;
00554         }
00555     }
00556 }
00557 
00558 static void unref_list_parent(struct ucftpentry *list)
00559 {
00560     while(list != NULL) {
00561         av_unref_obj(list->parent);
00562         list->parent = NULL;
00563         list = list->next;
00564     }
00565 }
00566 
00567 static void free_entry_list(struct ucftpentry *list)
00568 {
00569     while(list != NULL) {
00570         struct ucftpentry *ent = list;
00571         list = list->next;
00572         
00573         list_remove_entry(ent);
00574         ucftp_free_tree(ent);
00575     }
00576 }
00577 
00578 static struct ucftpconn *ucftp_find_conn(struct ucftpfs *fs, struct ucftpentry *ent)
00579 {
00580     struct ucftpconn *conn;
00581 
00582     while(ent->parent != NULL)
00583         ent = ent->parent;
00584 
00585     conn = fs->conns;
00586     while(conn != NULL) {
00587         if(conn->root == ent)
00588             break;
00589         conn = conn->next;
00590     }
00591     return conn;
00592 }
00593 
00594 static struct ucftpconn *ucftp_find_conn_ventry(ventry *ve)
00595 {
00596     struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
00597     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
00598     return ucftp_find_conn(fs, ent);
00599 }
00600 
00601 static void ucftp_putent(ventry *ve)
00602 {
00603     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
00604 
00605     av_unref_obj(ent);
00606 }
00607 
00608 static int ucftp_copyent(ventry *ve, void **resp)
00609 {
00610     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
00611     
00612     av_ref_obj(ent);
00613 
00614     *resp = (void *) ent;
00615 
00616     return 0;
00617 }
00618 
00619 /***************************************
00620  * connection code
00621  ***************************************/
00622 
00623 static void ucftp_release_conn(struct ucftpconn *conn)
00624 {
00625     conn->busy = 0;
00626 }
00627 
00628 static void ucftp_close_conn(struct ucftpconn *conn)
00629 {
00630     av_unref_obj(conn->sockfb);
00631     conn->sockfb = NULL;
00632     conn->sock = -1;
00633     conn->binary = -1;
00634     conn->cwd[0] = '\0';
00635 }
00636 
00637 /* get a line from a connection */
00638 static int ucftp_get_line(struct ucftpconn *conn, char **linep)
00639 {
00640     int res;
00641     char *line;
00642     
00643     res = av_filebuf_getline(conn->sockfb, &line, FTP_REPLY_TIMEOUT);
00644     if(res <= 0 || line == NULL) {
00645         ucftp_close_conn(conn);
00646         if(res < 0)
00647             return res;
00648 
00649         if(res == 0)
00650             av_log(AVLOG_ERROR, "FTP: timeout waiting for reply");
00651         else
00652             av_log(AVLOG_ERROR, "FTP: server closed ftpconn");
00653 
00654         return -EIO;
00655     }
00656 
00657     strip_crlf(line);
00658     *linep = line;
00659 
00660     return 0;
00661 }
00662 
00663 static int ucftp_check_reply(struct ucftpconn *conn, const char *line)
00664 {
00665     int reply;
00666 
00667     if(strlen(line) < 4 || !isdigit((int) line[0]) ||
00668        !isdigit((int) line[1]) || !isdigit((int) line[2]) ||
00669        (line[3] != ' ' && line[3] != '-')) {
00670         ucftp_close_conn(conn);
00671         av_log(AVLOG_ERROR, "FTP: malformed reply: %s", line);
00672         return -EIO;
00673     }
00674 
00675     reply = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
00676 
00677     if(reply == 421)
00678         ucftp_close_conn(conn);
00679 
00680     return reply;
00681 }
00682 
00683 static int ucftp_wait_reply_code(struct ucftpconn *conn)
00684 {
00685     int res;
00686     char *line;
00687     char replystr[4];
00688     int firstline = 1;
00689     int cont;
00690     int replycode = 0;
00691     
00692     do {
00693         cont = 0;
00694         res = ucftp_get_line(conn, &line);
00695         if(res < 0)
00696             return res;
00697 
00698         if(firstline) {
00699             res = ucftp_check_reply(conn, line);
00700             if(res < 0) {
00701                 av_free(line);
00702                 return res;
00703             }
00704 
00705             replycode = res;
00706 
00707             if(line[3] == '-') {
00708                 strncpy(replystr, line, 3);
00709                 replystr[3] = ' ';
00710                 firstline = 0;
00711                 cont = 1;
00712             }
00713         }
00714         else if(strncmp(line, replystr, 4) != 0)
00715             cont = 1;
00716 
00717         if(replycode >= 400)
00718             av_log(AVLOG_ERROR, "FTP: %s", line);
00719         else
00720             av_log(AVLOG_DEBUG, "FTP: %s", line);
00721 
00722         av_free(line);
00723     } while(cont);
00724  
00725     return replycode;
00726 }
00727 
00728 static int ucftp_write_command(struct ucftpconn *conn, const char *cmd)
00729 {
00730     char *line;
00731     int res;
00732 
00733     if(strncmp(cmd, "PASS ", 5) == 0)
00734         av_log(AVLOG_DEBUG, "FTP: PASS *");
00735     else
00736         av_log(AVLOG_DEBUG, "FTP: %s", cmd);
00737 
00738     line = av_stradd(NULL, cmd, "\r\n", NULL);
00739     res = write_socket(conn->sock, line, strlen(line));
00740     av_free(line);
00741 
00742     return res;
00743 }
00744 
00745 static int ucftp_command(struct ucftpconn *conn, const char *cmd)
00746 {
00747     int res;
00748 
00749     res = ucftp_write_command(conn, cmd);
00750     if(res < 0)
00751         return res;
00752 
00753     res = ucftp_wait_reply_code(conn);
00754     
00755     return res;
00756 }
00757 
00758 static int ucftp_set_ascii(struct ucftpconn *conn)
00759 {
00760     int res;
00761 
00762     if(conn->binary != 0) {
00763         res = ucftp_command(conn, "TYPE A");
00764         if(res < 0)
00765             return res;
00766 
00767         conn->binary = 0;
00768     }
00769 
00770     return 0;
00771 }
00772 
00773 static int ucftp_set_binary(struct ucftpconn *conn)
00774 {
00775     int res;
00776 
00777     if(conn->binary != 1) {
00778         res = ucftp_command(conn, "TYPE I");
00779         if(res < 0)
00780             return res;
00781 
00782         conn->binary = 1;
00783     }
00784 
00785     return 0;
00786 }
00787 
00788 static int ucftp_set_cwd(struct ucftpconn *conn, const char *dir)
00789 {
00790     int res;
00791     char *cmd;
00792 
00793     if(strcmp(conn->cwd, dir) != 0) {
00794         cmd = av_stradd(NULL, "CWD ", ( dir[0] == '\0' ) ? "/" : dir, NULL);
00795         res = ucftp_command(conn, cmd);
00796         av_free(cmd);
00797         if(res < 0)
00798             return res;
00799         
00800         if(res == 550)
00801             return -ENOENT;
00802         if(res / 100 != 2)
00803             return -EIO;
00804 
00805         av_free(conn->cwd);
00806         conn->cwd = av_strdup(dir);
00807     }
00808 
00809     return 0;
00810 }
00811 
00812 static int ucftp_login(struct ucftpconn *conn)
00813 {
00814     int res;
00815     char *cmd;
00816 
00817     cmd = av_stradd(NULL, "USER ", conn->user, NULL);
00818     res = ucftp_command(conn, cmd);
00819     av_free(cmd);
00820 
00821     if(res == 331) {
00822         cmd = av_stradd(NULL, "PASS ", conn->password, NULL);
00823         res = ucftp_command(conn, cmd);
00824         av_free(cmd);
00825         if(res < 0)
00826             return res;
00827     }
00828 
00829     if(res != 230)
00830         return -EACCES;
00831 
00832     return 0;
00833 }
00834 
00835 static int ucftp_init_conn(struct ucftpconn *conn)
00836 {
00837     int res;
00838 
00839     res = ucftp_wait_reply_code(conn);
00840     if(res == 120)
00841         res = ucftp_wait_reply_code(conn);
00842     
00843     if(res < 0)
00844         return res;
00845 
00846     if(res != 220)
00847         return -EIO;
00848     
00849     res = ucftp_login(conn);
00850     if(res < 0)
00851         return res;
00852 
00853     ucftp_command(conn, "PWD");
00854     ucftp_command(conn, "SYST");
00855 
00856     return 0;
00857 }
00858 
00859 static int ucftp_open_conn(struct ucftpconn *conn)
00860 {
00861     int res;
00862 
00863     if(conn->sock != -1) {
00864         res = ucftp_command(conn, "NOOP");
00865         if(res < 0)
00866             return res;
00867         
00868         if(res != 421)
00869             return 0;
00870     }
00871 
00872     res = av_sock_connect(conn->host, 21);
00873     if(res < 0)
00874         return res;
00875 
00876     conn->sock = res;
00877     conn->sockfb = av_filebuf_new(conn->sock, 0);
00878 
00879     res = ucftp_init_conn(conn);
00880     if(res < 0) {
00881         ucftp_close_conn(conn);
00882         return res;
00883     }
00884 
00885     return 0;
00886 }
00887 
00888 static int ucftp_check_passv_reply(struct ucftpconn *conn, const char *line,
00889                                    char **resp)
00890 {
00891     int res;
00892     int replycode;
00893     int addrbytes[6];
00894     char addrbuf[128];
00895     int port;
00896     
00897     res = ucftp_check_reply(conn, line);
00898     if(res < 0)
00899         return res;
00900 
00901     replycode = res;
00902     
00903     if(replycode != 227) {
00904         av_log(AVLOG_ERROR, "FTP: %s", line);
00905         ucftp_close_conn(conn);
00906         return -EIO;
00907     }
00908     
00909     av_log(AVLOG_DEBUG, "FTP: %s", line);
00910         
00911     if(line[3] != ' ') {
00912         av_log(AVLOG_ERROR, "FTP: Multiline reply to PASV: %s", line);
00913         ucftp_close_conn(conn);
00914         return -EIO;
00915     }
00916 
00917     res = ucftp_get_addrbytes(line, addrbytes);
00918     if(res < 0) {
00919         av_log(AVLOG_ERROR, "FTP: Bad reply to PASV: %s", line);
00920         ucftp_close_conn(conn);
00921         return -EIO;
00922     }
00923 
00924     port = addrbytes[4] * 0x100 + addrbytes[5];
00925     sprintf(addrbuf, "%i.%i.%i.%i:%i", 
00926             addrbytes[0], addrbytes[1], addrbytes[2], addrbytes[3], port);
00927 
00928     *resp = av_strdup(addrbuf);
00929 
00930     return 0;
00931 }
00932 
00933 static int ucftp_open_dataconn(struct ucftpconn *conn)
00934 {
00935     int res;
00936     char *line;
00937     char *host;
00938 
00939     res = ucftp_write_command(conn, "PASV");
00940     if(res < 0)
00941         return res;
00942 
00943     res = ucftp_get_line(conn, &line);
00944     if(res < 0)
00945         return res;
00946 
00947     res = ucftp_check_passv_reply(conn, line, &host);
00948     av_free(line);
00949     if(res < 0)
00950         return res;
00951 
00952     av_log(AVLOG_DEBUG,"FTP: remote data address: %s", host);
00953     
00954     res = av_sock_connect(host, -1);
00955     if(res >= 0)
00956        av_registerfd(res);
00957 
00958     av_free(host);
00959     
00960     return res;
00961 }
00962 
00963 static struct ucftpconn *ucftp_lookup_conn(struct ucftpfs *fs, const char *host,
00964                                            const char *user, const char *password)
00965 {
00966     struct ucftpconn *conn;
00967     struct ucftpconn **cp;
00968 
00969     for(cp = &fs->conns; *cp != NULL; cp = &(*cp)->next) {
00970         conn = *cp;
00971 
00972         if(strcmp(conn->host, host) == 0 && strcmp(conn->user, user) == 0 &&
00973            strcmp(conn->password, password) == 0 && !conn->busy) {
00974             conn->busy = 1;
00975             return conn;
00976         }
00977     }
00978 
00979     AV_NEW(conn);
00980     
00981     conn->host = av_strdup(host);
00982     conn->user = av_strdup(user);
00983     conn->password = av_strdup(password);
00984     conn->busy = 1;
00985     conn->sock = -1;
00986     conn->sockfb = NULL;
00987     conn->next = NULL;
00988     conn->binary = -1;
00989     conn->cwd = av_strdup("");
00990     conn->ft_cancel_ok = 1;
00991     
00992     conn->root = ucftp_new_entry("/");
00993     ucftp_make_node(fs, conn->root, 0755 | AV_IFDIR);
00994     
00995     *cp = conn;
00996     
00997     return conn;
00998 }
00999 
01000 static int ucftp_get_conn(struct ucftpfs *fs, const char *userhost,
01001                           struct ucftpconn **resp)
01002 {
01003     int res;
01004     char *tmps;
01005     const char *host;
01006     const char *user;
01007     const char *password;
01008     struct ucftpconn *conn = NULL;
01009 
01010     tmps = av_strdup(userhost);
01011     res = ucftp_split_path(fs, tmps, &host, &user, &password);
01012     if(res == 0)
01013         conn = ucftp_lookup_conn(fs, host, user, password);
01014 
01015     av_free(tmps);
01016 
01017     if(res < 0)
01018         return res;
01019 
01020     *resp = conn;
01021 
01022     return 0;
01023 }
01024 
01025 /***************************************
01026  * ucftpfile constructor and destructor
01027  ***************************************/
01028 
01029 #define TRY_REUSE_CONN_AFTER_CLOSE
01030 
01031 static void ucftp_free_file(struct ucftpfile *f)
01032 {
01033 #ifndef TRY_REUSE_CONN_AFTER_CLOSE
01034     if(f->conn != NULL) {
01035         ucftp_close_conn(f->conn);
01036         ucftp_release_conn(f->conn);
01037     }
01038 #endif
01039     av_unref_obj(f->sockfb);
01040     
01041     if(f->sock >= 0)
01042         close(f->sock);
01043 
01044 #ifdef TRY_REUSE_CONN_AFTER_CLOSE
01045     /* if control connection is busy try to wait for reply, often
01046        closing the data socket will bring the control connection back
01047        to life */
01048     if ( f->conn != NULL ) {
01049         if ( f->conn->busy ) {
01050             if ( f->conn->ft_cancel_ok ) {
01051                 int res = ucftp_wait_reply_code(f->conn);
01052 
01053                 if(res >= 0 && ( res / 10 == 45 || res == 426 ) ) {
01054                     /* code 45x and 426 is acceptable here, server reported abort */
01055                 } else if(res >= 0 && res / 100 != 2)
01056                     res = -EIO;
01057                 
01058                 if(res < 0) {
01059                     av_log( AVLOG_WARNING, "UCFTP: canceling file transfer and reuse connection failed\n" );
01060 
01061                     f->conn->ft_cancel_ok = 0;
01062                     ucftp_close_conn(f->conn);
01063                 }
01064                 
01065                 ucftp_release_conn(f->conn);
01066             } else {
01067                 ucftp_close_conn(f->conn);
01068                 ucftp_release_conn(f->conn);
01069             }
01070         }
01071     }
01072 #endif
01073     
01074     f->sock = -1;
01075     f->sockfb = NULL;
01076     f->numbytes = 0;
01077     f->conn = NULL;
01078     f->writing = 0;
01079     f->eof = 0;
01080     
01081     av_unref_obj(f->ent);
01082     f->ent = NULL;
01083 }
01084 
01085 static struct ucftpfile *ucftp_new_file(struct ucftpentry *ent, int flags)
01086 {
01087     struct ucftpfile *f;
01088     
01089     AV_NEW_OBJ(f, ucftp_free_file);
01090     
01091     f->sock = -1;
01092     f->sockfb = NULL;
01093     f->numbytes = 0;
01094     f->conn = NULL;
01095     f->flags = flags;
01096     f->writing = 0;
01097     f->eof = 0;
01098 
01099     av_ref_obj(ent);
01100     f->ent = ent;
01101     
01102     return f;
01103 }
01104 
01105 static int ucftp_init_file(struct ucftpfile *lf, int sock)
01106 {
01107     lf->sock = sock;
01108     lf->sockfb = NULL;
01109     lf->numbytes = 0;
01110     lf->conn = NULL;
01111     lf->writing = 0;
01112     lf->eof = 0;
01113 
01114     lf->sockfb = av_filebuf_new(lf->sock, 0);
01115 
01116     return 0;
01117 }
01118 
01119 /***************************************
01120  * dir reading code
01121  ***************************************/
01122 
01123 static int ucftp_read_list(struct ucftpfs *fs, struct filebuf *fb, struct ucftpentry *ent,
01124                            struct lscache *lc)
01125 {
01126     int res;
01127     char *line;
01128     int eof = 0;
01129     avtime_t now = av_time();
01130     struct ucftpentry *oldlist, **entp;
01131 
01132     /* release current subdir and save it locally */
01133     oldlist = ent->node->subdir;
01134     if(oldlist != NULL) {
01135         oldlist->prevp = &oldlist;
01136         ent->node->subdir = NULL;
01137     }
01138     unref_list_parent(oldlist);
01139 
01140     entp = &ent->node->subdir;
01141 
01142     do {
01143         res = av_filebuf_getline(fb, &line, FTP_READ_TIMEOUT);
01144         if(res < 0) {
01145             free_entry_list(oldlist);
01146             return res;
01147         }
01148         
01149         if(res == 0) {
01150             free_entry_list(oldlist);
01151             av_log(AVLOG_ERROR, "FTP: read timeout");
01152             return -EIO;
01153         }
01154         if(line == NULL)
01155             eof = 1;
01156         else {
01157             struct avstat stbuf;
01158             char *filename;
01159             char *linkname;
01160             strip_crlf(line);
01161             
01162             av_log(AVLOG_DEBUG, "FTP: %s", line);
01163             res = av_parse_ls(lc, line, &stbuf, &filename, &linkname);
01164             av_free(line);
01165             if(res == 1) {
01166                 struct ucftpentry *subent = NULL;
01167                 int res2;
01168 
01169                 if(strcmp(filename, "..") != 0 &&
01170                    strcmp(filename, ".") != 0 ) {
01171 
01172                     subent = find_list_entry(oldlist, filename);
01173                     if(subent != NULL) {
01174                         list_remove_entry(subent);
01175                     } else {
01176                         subent = ucftp_new_entry(filename);
01177                     }
01178                     if(subent->node == NULL) {
01179                         res2 = ucftp_make_node(fs, subent, stbuf.mode);
01180                         av_unref_obj(subent);
01181                     }
01182                     ucftp_add_subentry(ent, subent, entp);
01183                     entp = &subent->next;
01184 
01185                     /* re-use some old values */
01186                     if(subent->node != NULL) {
01187                         stbuf.dev = subent->node->st.dev;
01188                         stbuf.ino = subent->node->st.ino;
01189                     }
01190 
01191                     subent->node->st = stbuf;
01192                     if(subent->node->linkname != NULL) {
01193                         av_free(subent->node->linkname);
01194                     }
01195                     if(linkname != NULL) {
01196                         subent->node->linkname = av_strdup(linkname);
01197                     } else {
01198                         subent->node->linkname = NULL;
01199                     }
01200                 }
01201                 
01202                 av_free(filename);
01203                 av_free(linkname);
01204             }
01205         }
01206     } while(!eof);
01207     
01208     ent->node->valid = now + UCFTP_DIR_VALID;
01209 
01210     free_entry_list(oldlist);
01211     
01212     return 0;
01213 }
01214 
01215 static int ucftp_do_list(struct ucftpfs *fs, struct ucftpconn *conn, const char *dir, 
01216                          struct ucftpentry *ent)
01217 {
01218     int res;
01219     char *cmd;
01220     int listsock;
01221     struct filebuf *fb;
01222     struct lscache *lc;
01223 
01224     res = ucftp_open_conn(conn);
01225     if(res < 0)
01226         return res;
01227 
01228     res = ucftp_set_ascii(conn);
01229     if(res < 0)
01230         return res;
01231     
01232     res = ucftp_set_cwd(conn, dir);
01233     if(res < 0)
01234         return res;
01235 
01236     res = ucftp_open_dataconn(conn);
01237     if(res < 0)
01238         return res;
01239         
01240     listsock = res;
01241     cmd = av_strdup("LIST -al");
01242     res = ucftp_command(conn, cmd);
01243     av_free(cmd);
01244     if(res >= 0 && res / 100 != 1)
01245         res = -EIO;
01246     
01247     if(res < 0) {
01248         close(listsock);
01249         return res;
01250     }
01251     
01252     fb = av_filebuf_new(listsock, 0);
01253     lc = av_new_lscache();
01254     res = ucftp_read_list(fs, fb, ent, lc);
01255     av_unref_obj(lc);
01256     av_unref_obj(fb);
01257 
01258     res = ucftp_wait_reply_code(conn);
01259     if(res >= 0 && res / 100 != 2)
01260         res = -EIO;
01261 
01262     if(res < 0) {
01263         ucftp_free_dirlist(ent);
01264         return res;
01265     }
01266 
01267     return 0;
01268 }
01269 
01270 static int ucftp_list(struct ucftpfs *fs, struct ucftpconn *conn, struct ucftpentry *ent)
01271 {
01272     int res;
01273     char *path;
01274 
01275     path = ucftp_create_path(ent);
01276     
01277     res = ucftp_do_list(fs, conn, path, ent);
01278 
01279     ucftp_release_conn(conn);
01280     av_free(path);
01281 
01282     return res;
01283 }
01284 
01285 /* called by ucftp_nth_entry */
01286 static struct ucftpnode *ucftp_special_entry(int n, struct ucftpnode *nod,
01287                                       const char **namep)
01288 {
01289     if(n == 0) {
01290         *namep = ".";
01291         return nod;
01292     }
01293     else {
01294         *namep = "..";
01295         return nod->parent->node;
01296     }
01297 }
01298 
01299 /* called by ucftp_readdir */
01300 static struct ucftpnode *ucftp_nth_entry(int n, struct ucftpnode *nod,
01301                                      const char **namep)
01302 {
01303     struct ucftpentry *ent;
01304     int i;
01305 
01306     if(nod->parent != NULL) {
01307         //TODO should I rather not handling these special entries here?
01308         if(n  < 2)
01309             return ucftp_special_entry(n, nod, namep);
01310 
01311         n -= 2;
01312     }
01313 
01314     ent = nod->subdir;
01315     for(i = 0; i < n && ent != NULL; i++)
01316         ent = ent->next;
01317     
01318     if(ent == NULL)
01319         return NULL;
01320 
01321     *namep = ent->name;
01322     return ent->node;
01323 }
01324 
01325 static int ucftp_readdir(vfile *vf, struct avdirent *buf)
01326 {
01327     struct ucftpfile *parentfile = ucftp_vfile_ucftpfile(vf);
01328     struct ucftpnode *parent = parentfile->ent->node;
01329     struct ucftpnode *nod;
01330     const char *name;
01331     
01332     if(!AV_ISDIR(parent->st.mode))
01333         return -ENOTDIR;
01334     
01335     if(!ucftp_is_valid_node(parent)) {
01336         //get dir list from ftp
01337         
01338         struct ucftpconn *conn;
01339         int res;
01340         
01341         conn = ucftp_find_conn(ucftp_vfile_fs(vf), parentfile->ent);
01342         res = ucftp_list(ucftp_vfile_fs(vf), conn, parentfile->ent);
01343         if(res < 0) {
01344             //TODO do something? aborting?
01345         }
01346     }
01347 
01348     nod = ucftp_nth_entry(vf->ptr, parent, &name);
01349     if(nod == NULL)
01350         return 0;
01351 
01352     buf->name = av_strdup(name);
01353     buf->ino = nod->st.ino;
01354     buf->type = AV_TYPE(nod->st.mode);
01355     
01356     vf->ptr ++;
01357     
01358     return 1;
01359 }
01360 
01361 /***************************************
01362  * entry look up code
01363  ***************************************/
01364 
01365 /* called by ucftp_do_lookup */
01366 static struct ucftpentry *ucftp_get_entry(struct ucftpentry *parent,
01367                                           const char *name)
01368 {
01369     struct ucftpentry **entp;
01370     struct ucftpentry *ent;
01371 
01372     if(strcmp(name, ".") == 0) {
01373         ent = parent;
01374        av_ref_obj(ent);
01375        return ent;
01376     }
01377     if(strcmp(name, "..") == 0) {
01378         ent = parent->parent;
01379        av_ref_obj(ent);
01380        return ent;
01381     }
01382     for(entp = &parent->node->subdir; *entp != NULL; entp = &(*entp)->next)
01383        if(strcmp(name, (*entp)->name) == 0) {
01384            ent = *entp;
01385            av_ref_obj(ent);
01386            return ent;
01387        }
01388 
01389     /* lookup failed, so create a new entry and add it to the
01390        directory list temporarily */
01391  
01392     ent = ucftp_new_entry(name);
01393     
01394     *entp = ent;
01395     ent->prevp = entp;
01396     ent->parent = parent;
01397     av_ref_obj(parent);
01398 
01399     return ent;
01400 }
01401 
01402 /* called by ucftp_lookup */
01403 static int ucftp_do_lookup(ventry *ve, const char *name,
01404                            struct ucftpentry **entp)
01405 {
01406     struct ucftpentry *parent = ucftp_ventry_ucftpentry(ve);
01407     
01408     if(parent->node == NULL)
01409         return -ENOENT;
01410 
01411     if(name == NULL) {
01412         *entp = parent->parent;
01413         av_ref_obj(*entp);
01414         return 0;
01415     }
01416 
01417     if(!AV_ISDIR(parent->node->st.mode))
01418         return -ENOTDIR;
01419 
01420     if(!ucftp_is_valid_node(parent->node)) {
01421         //get dir list from ftp
01422         
01423         struct ucftpconn *conn;
01424         struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
01425         int res;
01426 
01427         conn = ucftp_find_conn_ventry(ve);
01428         res = ucftp_list(fs, conn, parent);
01429         if(res < 0) {
01430             //TODO do something? aborting?
01431         }
01432     }
01433 
01434     *entp = ucftp_get_entry(parent, name);
01435     
01436     return 0;
01437 }
01438 
01439 /* called by ucftp_lookup */
01440 static struct ucftpentry *ucftp_get_root(struct ucftpconn *conn)
01441 {
01442     struct ucftpentry *root = conn->root;
01443 
01444     av_ref_obj(root);
01445 
01446     return root;
01447 }
01448 
01449 static int ucftp_lookup(ventry *ve, const char *name, void **newp)
01450 {
01451     int res = 0;
01452     struct ucftpentry *parent = ucftp_ventry_ucftpentry(ve);
01453     struct ucftpentry *ent;
01454 
01455     if(parent == NULL) {
01456         struct ucftpconn *conn;
01457 
01458         if(ve->mnt->opts[0] != '\0')
01459             return -ENOENT;
01460 
01461         res = ucftp_get_conn(ucftp_ventry_ucftpfs(ve), name, &conn);
01462         if ( res < 0 )
01463             return res;
01464 
01465         ent = ucftp_get_root(conn);
01466         ucftp_release_conn(conn);
01467     }
01468     else {
01469         res = ucftp_do_lookup(ve, name, &ent);
01470         if(res < 0)
01471             return res;
01472 
01473         // it's ref'd in previous lookup but not freed by using putent
01474         // so we need to do it here
01475         av_unref_obj(parent);
01476     }
01477 
01478     *newp = ent;
01479 
01480     if(ent != NULL && ent->node != NULL)
01481         return AV_TYPE(ent->node->st.mode);
01482     else
01483         return 0;
01484 }
01485 
01486 /***************************************
01487  * write code (put)
01488  ***************************************/
01489 
01490 static int ucftp_do_put(const char *dir, const char *file,
01491                         struct ucftpconn *conn, struct ucftpfile *uf)
01492 {
01493     int res;
01494     int putsock;
01495     char *cmd;
01496 
01497     res = ucftp_open_conn(conn);
01498     if(res < 0)
01499         return res;
01500 
01501     res = ucftp_set_binary(conn);
01502     if(res < 0)
01503         return res;
01504     
01505     res = ucftp_set_cwd(conn, dir);
01506     if(res < 0)
01507         return res;
01508 
01509     res = ucftp_open_dataconn(conn);
01510     if(res < 0)
01511         return res;
01512 
01513     putsock = res;
01514     cmd = av_stradd(NULL, "STOR ", file, NULL);
01515     res = ucftp_command(conn, cmd);
01516     av_free(cmd);
01517     if(res >= 0 && res / 100 != 1)
01518         res = -EIO;
01519     
01520     if(res < 0) {
01521         close(putsock);
01522         return res;
01523     }
01524 
01525     uf->sock = putsock;
01526     uf->conn = conn;
01527 
01528     uf->writing = 1;
01529     
01530     return 0;
01531 }
01532 
01533 static int ucftp_init_put(ventry *ve, int flags, struct ucftpfile **ufp)
01534 {
01535     int res;
01536     struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
01537     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
01538     struct ucftpconn *conn;
01539     char *dir;
01540     char *file;
01541 
01542     conn = ucftp_find_conn(fs, ent);
01543     if(!conn)
01544         return -EIO;   // no conn?
01545 
01546     if(conn->busy)
01547         return -EAGAIN;  // control connection busy
01548 
01549     conn->busy = 1;
01550 
01551     dir = ucftp_create_path(ent->parent);
01552     file = av_strdup(ent->name);
01553 
01554     *ufp = ucftp_new_file(ent, flags);
01555     res = ucftp_do_put(( dir[0] == '\0' ) ? "/" : dir, file, conn, *ufp);
01556     av_free(dir);
01557     av_free(file);
01558 
01559     if(res < 0) {
01560         av_unref_obj(*ufp);
01561         ucftp_release_conn(conn);
01562     } else {
01563         ucftp_truncate_node(ent->node, 0);
01564     }
01565 
01566     return res;
01567 }
01568 
01569 static avssize_t ucftp_write(vfile *vf, const char *buf, avsize_t nbyte)
01570 {
01571     struct ucftpfile *uf = ucftp_vfile_ucftpfile(vf);
01572     int res;
01573 
01574     if(!buf)
01575         return -EINVAL;
01576     if(nbyte < 1)
01577         return 0;
01578 
01579     if(AV_ISDIR(uf->ent->node->st.mode))
01580         return -EISDIR;
01581     
01582     if((uf->flags & AVO_ACCMODE) != AVO_WRONLY ||
01583        (uf->flags & AVO_TRUNC) == 0 ||
01584        (uf->flags & AVO_CREAT) == 0 ||
01585        (uf->flags & AVO_APPEND) != 0)
01586         return -EINVAL;
01587     
01588     if(uf->sock < 0) {
01589         return -EIO;
01590     }
01591 
01592     res = write(uf->sock, buf, nbyte);
01593     if(res < 0 || res != nbyte)
01594         return -EIO;
01595     
01596     ucftp_truncate_node(uf->ent->node, uf->ent->node->st.size + res);
01597     return res;
01598 }
01599 
01600 /***************************************
01601  * open code
01602  ***************************************/
01603 
01604 /* called by ucftp_open_check_type */
01605 static int ucftp_need_write(int flags)
01606 {
01607     if((flags & AVO_ACCMODE) == AVO_WRONLY ||
01608        (flags & AVO_ACCMODE) == AVO_RDWR ||
01609        (flags & AVO_TRUNC) != 0)
01610         return 1;
01611     
01612     return 0;
01613 }
01614 
01615 /* called by ucftp_open_check */
01616 static int ucftp_open_check_type(avmode_t mode, int flags)
01617 {
01618     if((flags & AVO_DIRECTORY) != 0 && !AV_ISDIR(mode))
01619         return -ENOTDIR;
01620     
01621     switch(mode & AV_IFMT) {
01622     case AV_IFREG:
01623         return 0;
01624         
01625     case AV_IFDIR:
01626         if(ucftp_need_write(flags))
01627             return -EISDIR;
01628         return 0;
01629 
01630     case AV_IFLNK:
01631         if((flags & AVO_ACCMODE) != AVO_NOPERM || !(flags & AVO_NOFOLLOW))
01632             return -ENOENT;
01633         return 0;
01634 
01635     default:
01636         /* FIFO, char/bockdev, socket */
01637         if((flags & AVO_ACCMODE) != AVO_NOPERM)
01638             return -ENXIO;
01639         return 0;
01640     }
01641 }
01642 
01643 /* called by ucftp_open */
01644 static int ucftp_open_check(struct ucftpnode *nod, int flags)
01645 {
01646     if(nod == NULL) {
01647         if(!(flags & AVO_CREAT))
01648             return -ENOENT;
01649         return 0;
01650     }
01651 
01652     if((flags & AVO_EXCL) != 0)
01653         return -EEXIST;
01654 
01655     return ucftp_open_check_type(nod->st.mode, flags);
01656 }
01657 
01658 static int ucftp_open(ventry *ve, int flags, avmode_t mode, void **resp)
01659 {
01660     int res;
01661     struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
01662     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
01663     struct ucftpfile *file = NULL;
01664     int new_node = 0;
01665 
01666     /* check permissions */
01667     res = ucftp_open_check(ent->node, flags);
01668     if(res < 0)
01669         return res;
01670 
01671     if(ent->node == NULL) {
01672         // node doesn't exists so there's no such file
01673         // create a node in case of write
01674         if((flags & AVO_ACCMODE) == AVO_WRONLY &&
01675            (flags & AVO_CREAT) &&
01676            (flags & AVO_TRUNC)) {
01677             res = ucftp_make_node(fs, ent, mode | AV_IFREG);
01678             if(res < 0)
01679                 return res;
01680             new_node = 1;
01681         } else {
01682             return -ENOENT;
01683         }
01684     }
01685 
01686     if((flags & AVO_ACCMODE) == AVO_NOPERM) {
01687         file = ucftp_new_file(ent, flags);
01688     } else {
01689         if((flags & AVO_DIRECTORY) != 0) {
01690             file = ucftp_new_file(ent, flags);
01691         } else {
01692             if(AV_ISREG(ent->node->st.mode)) {
01693                 if((flags & AVO_ACCMODE) == AVO_RDONLY) {
01694                     file = ucftp_new_file(ent, flags);
01695                 } else if((flags & AVO_ACCMODE) == AVO_WRONLY) {
01696                     if(ucftp_init_put(ve, flags, &file) < 0) {
01697                         if(new_node == 1)
01698                             ucftp_unlink_node(ent);
01699                         return -EIO;
01700                     }
01701                 }
01702             }
01703         }
01704     }
01705     
01706     if(file == NULL)
01707         return -EIO;
01708     
01709     *resp = file;
01710 
01711     return 0;
01712 }
01713 
01714 /***************************************
01715  * close code
01716  ***************************************/
01717 
01718 static int ucftp_close(vfile *vf)
01719 {
01720     struct ucftpfile *f = ucftp_vfile_ucftpfile(vf);
01721     int res = 0;
01722 
01723     if(f->writing == 1) {
01724         close(f->sock);
01725         
01726         res = ucftp_wait_reply_code(f->conn);
01727         if(res >= 0 && res / 100 != 2)
01728             res = -EIO;
01729 
01730         if ( res >= 0 ) {
01731             ucftp_release_conn(f->conn);
01732         }
01733     }
01734 
01735     av_unref_obj(f);
01736 
01737     return res;
01738 }
01739 
01740 /***************************************
01741  * read code (get)
01742  ***************************************/
01743 
01744 static int ucftp_do_get(const char *dir, const char *file,
01745                         struct ucftpconn *conn, struct ucftpfile *uf)
01746 {
01747     int res;
01748     int getsock;
01749     char *cmd;
01750 
01751     res = ucftp_open_conn(conn);
01752     if(res < 0)
01753         return res;
01754 
01755     res = ucftp_set_binary(conn);
01756     if(res < 0)
01757         return res;
01758     
01759     res = ucftp_set_cwd(conn, dir);
01760     if(res < 0)
01761         return res;
01762 
01763     res = ucftp_open_dataconn(conn);
01764     if(res < 0)
01765         return res;
01766 
01767     getsock = res;
01768     cmd = av_stradd(NULL, "RETR ", file, NULL);
01769     res = ucftp_command(conn, cmd);
01770     av_free(cmd);
01771     if(res >= 0 && res / 100 != 1)
01772         res = -EIO;
01773     
01774     if(res < 0) {
01775         close(getsock);
01776         return res;
01777     }
01778 
01779     res = ucftp_init_file(uf, getsock);
01780     if(res < 0) {
01781         return res;
01782     }
01783 
01784     uf->conn = conn;
01785     
01786     return 0;
01787 }
01788 
01789 static int ucftp_init_get(vfile *vf)
01790 {
01791     int res;
01792     struct ucftpfile *uf = ucftp_vfile_ucftpfile(vf);
01793     struct ucftpfs *fs = ucftp_vfile_fs(vf);
01794     struct ucftpentry *ent = uf->ent;
01795     struct ucftpconn *conn;
01796     char *dir;
01797     char *file;
01798 
01799     conn = ucftp_find_conn(fs, ent);
01800     if(!conn)
01801         return -EIO;   // no conn?
01802 
01803     if(conn->busy)
01804         return -EAGAIN;  // control connection busy
01805 
01806     conn->busy = 1;
01807 
01808     dir = ucftp_create_path(ent->parent);
01809     file = av_strdup(ent->name);
01810 
01811     res = ucftp_do_get(( dir[0] == '\0' ) ? "/" : dir, file, conn, uf);
01812     av_free(dir);
01813     av_free(file);
01814 
01815     if(res < 0) {
01816         ucftp_release_conn(conn);
01817     }
01818 
01819     return res;
01820 }
01821 
01822 /* TODO: perhaps try filling buf as it happens that bytes read from sock are lower than nbyte */
01823 static avssize_t ucftp_read(vfile *vf, char *buf, avsize_t nbyte)
01824 {
01825     avoff_t nact;
01826     struct ucftpfile *uf = ucftp_vfile_ucftpfile(vf);
01827     avsize_t nbytes;
01828     int res;
01829 
01830     if(AV_ISDIR(uf->ent->node->st.mode))
01831         return -EISDIR;
01832     
01833     if(uf->eof) {
01834         return 0;
01835     }
01836     
01837     if(!uf->sockfb) {
01838         if(!AV_ISREG(uf->ent->node->st.mode))
01839             return -EINVAL;
01840         if((uf->flags & AVO_ACCMODE) != AVO_RDONLY)
01841             return -EINVAL;
01842         if(ucftp_init_get(vf) < 0)
01843             return -EIO;
01844     }
01845 
01846     nact = nbyte;
01847     
01848     if ( uf->numbytes != vf->ptr ) {
01849         av_log(AVLOG_ERROR, "UCFTP: wrong file position\n");
01850         return -EIO;
01851     }
01852 
01853     for(;;) {
01854         nbytes = av_filebuf_read(uf->sockfb, buf, nact);
01855         if(nbytes != 0) {
01856             uf->numbytes += nbytes;
01857             vf->ptr += nbytes;
01858             break;
01859         } else {
01860             if(av_filebuf_eof(uf->sockfb)) {
01861                 av_unref_obj(uf->sockfb);
01862                 uf->sockfb = NULL;
01863                 close(uf->sock);
01864                 uf->sock = -1;
01865                 
01866                 uf->eof = 1;
01867  
01868                 res = ucftp_wait_reply_code(uf->conn);
01869                 if(res >= 0 && res / 100 != 2)
01870                     res = -EIO;
01871             
01872                 if(res < 0)
01873                     return res;
01874             
01875                 ucftp_release_conn(uf->conn);
01876                 uf->conn = NULL;
01877             
01878                 return 0;
01879             }
01880             else {
01881                 res = av_filebuf_check(&uf->sockfb, 1, FTP_READ_TIMEOUT);
01882                 if(res < 0)
01883                     return res;
01884             
01885                 if(res == 0) {
01886                     av_log(AVLOG_ERROR, "FTP: read timeout");
01887                     return -EIO;
01888                 }
01889             }
01890         }
01891     }
01892     
01893     return nbytes;
01894 }
01895 
01896 /***************************************
01897  * attribute code (stat)
01898  ***************************************/
01899 
01900 static int ucftp_getattr(vfile *vf, struct avstat *buf, int attrmask)
01901 {
01902     struct ucftpfile *file = ucftp_vfile_ucftpfile(vf);
01903 
01904     *buf = file->ent->node->st;
01905 
01906     return 0;
01907 }
01908 
01909 #if 0
01910 static void vol_set_attributes(struct avstat *dest, const struct avstat *src,
01911                                int attrmask)
01912 {
01913     if((attrmask & AVA_ATIME) != 0)
01914         dest->atime = src->atime;
01915     if((attrmask & AVA_MTIME) != 0)
01916         dest->mtime = src->mtime;
01917     if((attrmask & AVA_MODE) != 0)
01918         dest->mode = (dest->mode & AV_IFMT) | src->mode;
01919     if((attrmask & AVA_UID) != 0)
01920         dest->uid = src->uid;
01921     if((attrmask & AVA_GID) != 0)
01922         dest->gid = src->gid;
01923 }
01924 
01925 static int vol_setattr(vfile *vf, struct avstat *buf, int attrmask)
01926 {
01927     struct ucftpnode *nod = vol_vfile_ucftpnode(vf);
01928 
01929     vol_set_attributes(&nod->st, buf, attrmask);
01930     
01931     return 0;
01932 }
01933 #endif
01934 
01935 /***************************************
01936  * access and readlink functions
01937  ***************************************/
01938 
01939 static int ucftp_access(ventry *ve, int amode)
01940 {
01941     struct ucftpnode *nod = ucftp_ventry_ucftpentry(ve)->node;
01942 
01943     if(nod == NULL) 
01944         return -ENOENT;
01945     
01946     return 0;
01947 }
01948 
01949 static int ucftp_readlink(ventry *ve, char **bufp)
01950 {
01951     struct ucftpnode *nod = ucftp_ventry_ucftpentry(ve)->node;
01952 
01953     if(nod == NULL)
01954         return -ENOENT;
01955 
01956     if(!AV_ISLNK(nod->st.mode))
01957         return -EINVAL;
01958 
01959     if(!nod->linkname)
01960         return -EINVAL;
01961 
01962     *bufp = av_strdup(nod->linkname);
01963 
01964     return 0;
01965 }
01966 
01967 /***************************************
01968  * code for generic FTP commands
01969  ***************************************/
01970 
01971 static int ucftp_do_op(enum ucftp_op op, const char *dir, const char *file, struct ucftpconn *conn)
01972 {
01973     int res;
01974     char *cmd;
01975     
01976     res = ucftp_open_conn(conn);
01977     if(res < 0)
01978         return res;
01979 
01980     res = ucftp_set_cwd(conn, dir);
01981     if(res < 0)
01982         return res;
01983 
01984     res = ucftp_open_dataconn(conn);
01985     if(res < 0)
01986         return res;
01987 
01988     cmd = av_stradd(NULL, ucftp_op_cmd[op], file, NULL);
01989     res = ucftp_command(conn, cmd);
01990     av_free(cmd);
01991     if(res >= 0 && res / 100 != 2)
01992         res = -EIO;
01993 
01994     if(res < 0) {
01995         return res;
01996     }
01997 
01998     return 0;
01999 }
02000 
02001 
02002 static int ucftp_op(enum ucftp_op op, ventry *ve)
02003 {
02004     int res;
02005     struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
02006     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
02007     struct ucftpconn *conn;
02008     char *dir;
02009     char *file;
02010 
02011     conn = ucftp_find_conn(fs, ent);
02012     if(!conn)
02013         return -EIO;   // no conn?
02014 
02015     if(conn->busy)
02016         return -EAGAIN;  // control connection busy
02017 
02018     conn->busy = 1;
02019 
02020     dir = ucftp_create_path(ent->parent);
02021     file = av_strdup(ent->name);
02022 
02023     res = ucftp_do_op(op, dir[0] == '\0' ? "/" : dir, file, conn);
02024     av_free(dir);
02025     av_free(file);
02026 
02027     ucftp_release_conn(conn);
02028 
02029     return res;
02030 }
02031 
02032 /***************************************
02033  * deletion code
02034  ***************************************/
02035 
02036 static int ucftp_unlink(ventry *ve)
02037 {
02038     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
02039     int res;
02040 
02041     if(ent->node == NULL)
02042         return -ENOENT;
02043 
02044     if(AV_ISDIR(ent->node->st.mode))
02045         return -EISDIR;
02046     
02047     res = ucftp_op(OP_DELE, ve);
02048     if(res < 0)
02049         return res;
02050     
02051     ucftp_unlink_node(ent);
02052     
02053     return 0;
02054 }
02055 
02056 /* called by ucftp_rmdir */
02057 static int ucftp_check_rmdir(struct ucftpentry *ent)
02058 {
02059     struct ucftpnode *nod = ent->node;
02060 
02061     if(nod == NULL)
02062         return -ENOENT;
02063 
02064     if(!AV_ISDIR(nod->st.mode)) 
02065         return -ENOTDIR;
02066 
02067     if(nod->subdir != NULL)
02068         return -ENOTEMPTY;
02069 
02070     if(ent->parent == NULL)
02071         return -EBUSY;
02072 
02073     return 0;
02074 }
02075 
02076 static int ucftp_rmdir(ventry *ve)
02077 {
02078     int res;
02079     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
02080 
02081     res = ucftp_check_rmdir(ent);
02082     if(res < 0) 
02083         return res;
02084 
02085     res = ucftp_op(OP_RMD, ve);
02086     if(res < 0)
02087         return res;
02088     
02089     ucftp_unlink_node(ent);
02090     
02091     return 0;
02092 }
02093 
02094 /***************************************
02095  * make dir code
02096  ***************************************/
02097 
02098 static int ucftp_mkdir(ventry *ve, avmode_t mode)
02099 {
02100     int res;
02101     struct ucftpfs *fs = ucftp_ventry_ucftpfs(ve);
02102     struct ucftpentry *ent = ucftp_ventry_ucftpentry(ve);
02103     
02104     if(ent->node != NULL)
02105         return -EEXIST;
02106     
02107     res = ucftp_op(OP_MKD, ve);
02108     if(res < 0)
02109         return res;
02110 
02111     res = ucftp_make_node(fs, ent, mode | AV_IFDIR);
02112     if(res < 0)
02113         return res;
02114 
02115     if(ent->parent != NULL &&
02116        ent->parent->node != NULL)
02117         ent->parent->node->valid = 0;
02118     return 0;
02119 }
02120 
02121 /***************************************
02122  * seeking
02123  ***************************************/
02124 
02125 static avoff_t ucftp_lseek(vfile *vf, avoff_t offset, int whence)
02126 {
02127     return -ENOSYS;
02128     /* TODO implement some kind of seeking?
02129      * Since seeking is not supported by FTP and no data is cached
02130      * seeking would be terrible expensive.
02131      * on the other hand, forward seeking could be implemented
02132      * by repeatedly call read, seek to the end as well. Seek to
02133      * the start could be implemented by closing and re-open
02134      */
02135 }
02136 
02137 /***************************************
02138  * ucftp_ctl code
02139  ***************************************/
02140 
02141 static int ucftp_password_set(struct entry *ent, const char *param,
02142                               const char *val)
02143 {
02144     struct ucftpsession *fts;
02145     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
02146     struct ucftpfs *fs = (struct ucftpfs *) sf->data;
02147     unsigned int len;
02148 
02149     fts = ucftp_get_session(fs, param);
02150     av_free(fts->password);
02151     fts->password = av_strdup(val);
02152     len = strlen(fts->password);
02153     if(len > 0) {
02154         if(fts->password[len - 1] == '\n')
02155             fts->password[len - 1] = '\0';
02156     }
02157 
02158     return 0;
02159 }
02160 
02161 static int ucftp_loggedin_get(struct entry *ent, const char *param, char **resp)
02162 {
02163     struct ucftpsession *fts;
02164     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
02165     struct ucftpfs *fs = (struct ucftpfs *) sf->data;
02166 
02167     fts = ucftp_find_session(fs, param);
02168     if(fts == NULL)
02169         *resp = av_strdup("0\n");
02170     else
02171         *resp = av_strdup("1\n");
02172 
02173     return 0;
02174 }
02175 
02176 static int ucftp_loggedin_val(const char *val, int *resp)
02177 {
02178     char *end;
02179     int ival;
02180 
02181     ival = strtol(val, &end, 10);
02182     if(end == val)
02183         return -EINVAL;
02184 
02185     if(*end == '\n')
02186         end++;
02187     if(*end != '\0')
02188         return -EINVAL;
02189 
02190     if(ival < 0 || ival > 1)
02191         return -EINVAL;
02192     
02193     *resp = ival;
02194 
02195     return 0;
02196 }
02197 
02198 static int ucftp_loggedin_set(struct entry *ent, const char *param,
02199                               const char *val)
02200 {
02201     int res;
02202     struct ucftpsession *fts;
02203     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
02204     struct ucftpfs *fs = (struct ucftpfs *) sf->data;
02205 
02206     fts = ucftp_find_session(fs, param);
02207     if(fts == NULL)
02208         res = -EACCES;
02209     else {
02210         int ival;
02211 
02212         res = ucftp_loggedin_val(val, &ival);
02213         if(res == 0 && ival == 0) {
02214             /* FIXME: end connections using this session */
02215             ucftp_remove_session(fts);
02216         }
02217     }
02218 
02219     return res;
02220 }
02221 
02222 static int ucftp_init_ctl(struct vmodule *module, struct ucftpfs *fs)
02223 {
02224     int res;
02225     struct namespace *ns;
02226     struct statefile *stf;
02227     struct entry *ent;
02228     struct avfs *avfs;
02229     
02230     res = av_state_new(module, "ucftp_ctl", &ns, &avfs);
02231     if(res < 0)
02232         return res;
02233     
02234     ent = av_namespace_lookup(ns, NULL, "password");
02235     AV_NEW(stf);
02236     stf->data = fs;
02237     stf->get = NULL;
02238     stf->set = ucftp_password_set;
02239     av_namespace_set(ent, stf);
02240 
02241     ent = av_namespace_lookup(ns, NULL, "loggedin");
02242     AV_NEW(stf);
02243     stf->data = fs;
02244     stf->get = ucftp_loggedin_get;
02245     stf->set = ucftp_loggedin_set;
02246     av_namespace_set(ent, stf);
02247     
02248     av_unref_obj(ns);
02249 
02250     return 0;
02251 }
02252 
02253 /***************************************
02254  * ucftp constructor and destructor
02255  ***************************************/
02256 
02257 static void ucftp_destroy(struct avfs *avfs)
02258 {
02259     struct ucftpfs *fs = (struct ucftpfs *) avfs->data;
02260 
02261     struct ucftpconn *conn;
02262     struct ucftpconn *nextconn;
02263 
02264     for(conn = fs->conns; conn != NULL; conn = nextconn) {
02265         nextconn = conn->next;
02266 
02267         ucftp_free_tree(conn->root);
02268         av_unref_obj(conn->root);
02269         ucftp_close_conn(conn);
02270         av_free(conn->host);
02271         av_free(conn->user);
02272         av_free(conn->password);
02273         av_free(conn->cwd);
02274         av_free(conn);
02275     }
02276 
02277     while(fs->sessions.next != &fs->sessions)
02278         ucftp_remove_session(fs->sessions.next);
02279 
02280     av_free(fs);
02281 }
02282 
02283 extern int av_init_module_ucftp(struct vmodule *module);
02284 
02285 int av_init_module_ucftp(struct vmodule *module)
02286 {
02287     int res;
02288     struct avfs *avfs;
02289     struct ucftpfs *fs;
02290 
02291     res = av_new_avfs("ucftp", NULL, AV_VER, AVF_ONLYROOT, module, &avfs);
02292     if(res < 0)
02293         return res;
02294 
02295     avfs->destroy = ucftp_destroy;
02296 
02297     AV_NEW(fs);
02298 
02299     avfs->data = (void *) fs;
02300 
02301     fs->avfs = avfs;
02302     fs->conns = NULL;
02303     fs->sessions.next = &fs->sessions;
02304     fs->sessions.prev = &fs->sessions;
02305 
02306     avfs->lookup    = ucftp_lookup;
02307     avfs->putent    = ucftp_putent;
02308     avfs->copyent   = ucftp_copyent;
02309     avfs->getpath   = ucftp_getpath;
02310     
02311     avfs->open      = ucftp_open;
02312     avfs->close     = ucftp_close;
02313     avfs->read      = ucftp_read;
02314     avfs->readdir   = ucftp_readdir;
02315     avfs->getattr   = ucftp_getattr;
02316     avfs->write     = ucftp_write;
02317     avfs->access    = ucftp_access;
02318     avfs->readlink  = ucftp_readlink;
02319     avfs->mkdir     = ucftp_mkdir;
02320     avfs->unlink    = ucftp_unlink;
02321     avfs->rmdir     = ucftp_rmdir;
02322 
02323     avfs->lseek     = ucftp_lseek;
02324     
02325     //    avfs->rename    = vol_rename;
02326     //    avfs->setattr   = vol_setattr;
02327     //    avfs->truncate  = vol_truncate;
02328 
02329     av_add_avfs(avfs);
02330     res = ucftp_init_ctl(module, fs);
02331     if(res < 0)
02332         av_unref_obj(avfs);
02333     
02334     return res;
02335 }
02336 
02337 /* Local Variables: */
02338 /* c-basic-offset:4 */
02339 /* End: */