static int seek(stream_t *s,off_t newpos) { struct stream_priv_s* p = s->priv; int resp; char rsp_txt[256]; if(s->pos > s->end_pos) { s->eof=1; return 0; } // Check to see if the server did not already terminate the transfer if(fd_can_read(p->handle, 0)) { if(readresp(p,rsp_txt) != 2) mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt); closesocket(s->fd); s->fd = -1; } // Close current download if(s->fd >= 0) { static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH}; //int fl; // First close the fd closesocket(s->fd); s->fd = 0; // Send send the telnet sequence needed to make the server react // Dunno if this is really needed, lftp have it. I let // it here in case it turn out to be needed on some other OS //fl=fcntl(p->handle,F_GETFL); //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK); // send only first byte as OOB due to OOB braindamage in many unices send(p->handle,pre_cmd,1,MSG_OOB); send(p->handle,pre_cmd+1,sizeof(pre_cmd)-1,0); //fcntl(p->handle,F_SETFL,fl); // Get the 426 Transfer aborted // Or the 226 Transfer complete resp = readresp(p,rsp_txt); if(resp != 4 && resp != 2) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Server didn't abort correctly: %s\n",rsp_txt); s->eof = 1; return 0; } // Send the ABOR command // Ignore the return code as sometimes it fail with "nothing to abort" FtpSendCmd("ABOR",p,rsp_txt); } return FtpOpenData(s,newpos); }
/* * FtpClose - close a data connection */ GLOBALDEF int FtpClose(netbuf *nData) { netbuf *ctrl; switch (nData->dir) { case FTPLIB_WRITE: /* potential problem - if buffer flush fails, how to notify user? */ if (nData->buf != NULL) writeline(NULL, 0, nData); case FTPLIB_READ: if (nData->buf) free(nData->buf); shutdown(nData->handle,2); net_close(nData->handle); ctrl = nData->ctrl; free(nData); if (ctrl) { ctrl->data = NULL; return(readresp('2', ctrl)); } return 1; case FTPLIB_CONTROL: if (nData->data) { nData->ctrl = NULL; FtpClose(nData); } net_close(nData->handle); free(nData); return 0; } return 1; }
/* * FtpAcceptConnection - accept connection from server * * return 1 if successful, 0 otherwise */ static int FtpAcceptConnection (netbuf *nData, netbuf *nControl) { int sData; struct sockaddr addr; unsigned int l; int i; struct timeval tv; fd_set mask; int rv; FD_ZERO(&mask); FD_SET(nControl->handle, &mask); FD_SET(nData->handle, &mask); tv.tv_usec = 0; tv.tv_sec = ACCEPT_TIMEOUT; i = nControl->handle; if (i < nData->handle) i = nData->handle; i = select(i + 1, &mask, NULL, NULL, &tv); if (i == -1) { strncpy(nControl->response, strerror(errno), sizeof (nControl->response)); net_close(nData->handle); nData->handle = 0; rv = 0; } else if (i == 0) { strcpy(nControl->response, "timed out waiting for connection"); net_close(nData->handle); nData->handle = 0; rv = 0; } else { if (FD_ISSET(nData->handle, &mask)) { l = sizeof (addr); sData = accept(nData->handle, &addr, &l); i = errno; net_close(nData->handle); if (sData > 0) { rv = 1; nData->handle = sData; } else { strncpy(nControl->response, strerror(i), sizeof (nControl->response)); nData->handle = 0; rv = 0; } } else if (FD_ISSET(nControl->handle, &mask)) { net_close(nData->handle); nData->handle = 0; readresp('2', nControl); rv = 0; } } return rv; }
/* * Ftp_Send - send a command and wait for expected response * * return 1 if proper response received, 0 otherwise */ int Ftp_Send(const char *cmd, char expresp, netbuf *nControl) { char buf[256]; if (nControl->dir != FTP_CONTROL) return 0; if (debug_lib > 2) fprintf(stderr,"%s\n",cmd); sprintf(buf,"%s\r\n",cmd); if (netwrite(nControl->handle,buf,strlen(buf), 0) <= 0) return 0; return readresp(expresp, nControl); }
/* * FtpSendCmd - send a command and wait for expected response * * return 1 if proper response received, 0 otherwise */ static int FtpSendCmd (const char *cmd, char expresp, netbuf *nControl) { char buf[256]; if (nControl->dir != FTPLIB_CONTROL) return 0; if (ftplib_debug > 2) fprintf(stderr, "%s\n", cmd); if ((strlen(cmd) + 3) > sizeof (buf)) return 0; sprintf(buf, "%s\r\n", cmd); if (net_write(nControl->handle, buf, strlen(buf)) <= 0) { perror("write"); return 0; } return readresp(expresp, nControl); }
/* * FtpClose - close a data connection */ GLOBALDEF int FtpClose(netbuf *nData) { netbuf *ctrl; if (nData->dir == FTPLIB_WRITE) { if (nData->buf != NULL) writeline(NULL, 0, nData); } else if (nData->dir != FTPLIB_READ) return 0; if (nData->buf) free(nData->buf); shutdown(nData->handle,2); net_close(nData->handle); ctrl = nData->ctrl; free(nData); if (ctrl) return(readresp('2', ctrl)); return 1; }
/* * Ftp_Connect - connect to remote server * * return 1 if connected, 0 if not */ int Ftp_Connect(const char *host, int port, netbuf **nControl) { int socket_d = 0; netbuf *ctrl; socket_d = Socket_Connect(host, port); if (socket_d > 0) { // allocate and fill in netbuf structure ctrl = calloc(1,sizeof(netbuf)); if (ctrl == NULL) { Socket_close(socket_d); return 0; } ctrl->buf = malloc(FTP_BUFSIZ); if (ctrl->buf == NULL) { Socket_close(socket_d); free(ctrl); return 0; } ctrl->handle = socket_d; ctrl->dir = FTP_CONTROL; if (readresp('2', ctrl) == 0) { Socket_close(socket_d); free(ctrl->buf); free(ctrl); return 0; } *nControl = ctrl; return 1; } else return 0; }
static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp) { int l = strlen(cmd); int hascrlf = cmd[l - 2] == '\r' && cmd[l - 1] == '\n'; if(hascrlf && l == 2) mp_msg(MSGT_STREAM,MSGL_V, "\n"); else mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd); while(l > 0) { int s = send(nControl->handle,cmd,l,0); if(s <= 0) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno)); return 0; } cmd += s; l -= s; } if (hascrlf) return readresp(nControl,rsp); else return FtpSendCmd("\r\n", nControl, rsp); }
/* * FtpConnect - connect to remote server * * return 1 if connected, 0 if not */ GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) { int sControl; struct sockaddr_in sin; struct hostent *phe; struct servent *pse; int on=1; netbuf *ctrl; char *lhost; char *pnum; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; lhost = strdup(host); pnum = strchr(lhost,':'); if (pnum == NULL) { #if defined(VMS) sin.sin_port = htons(21); #else if ((pse = getservbyname("ftp","tcp")) == NULL) { perror("getservbyname"); return 0; } sin.sin_port = pse->s_port; #endif } else { *pnum++ = '\0'; if (isdigit(*pnum)) sin.sin_port = htons(atoi(pnum)); else { pse = getservbyname(pnum,"tcp"); if(pse == NULL) { perror("getservbyname"); return 0; } sin.sin_port = pse->s_port; } } if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) { if ((phe = gethostbyname(lhost)) == NULL) { perror("gethostbyname"); return 0; } memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); } free(lhost); sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sControl == -1) { perror("socket"); return 0; } if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) { perror("setsockopt"); net_close(sControl); return 0; } if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("connect"); net_close(sControl); return 0; } ctrl = calloc(1,sizeof(netbuf)); if (ctrl == NULL) { perror("calloc"); net_close(sControl); return 0; } ctrl->buf = malloc(FTPLIB_BUFSIZ); if (ctrl->buf == NULL) { perror("calloc"); net_close(sControl); free(ctrl); return 0; } ctrl->handle = sControl; ctrl->dir = FTPLIB_CONTROL; ctrl->ctrl = NULL; ctrl->cmode = FTPLIB_DEFMODE; ctrl->idlecb = NULL; ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; ctrl->idlearg = NULL; ctrl->xfered = 0; ctrl->xfered1 = 0; ctrl->cbbytes = 0; if (readresp('2', ctrl) == 0) { net_close(sControl); free(ctrl->buf); free(ctrl); return 0; } *nControl = ctrl; return 1; }
/* * Ftp_Xfer - issue a command and transfer data * * return SRL_SUCCESS if successful, otherwise */ static short Ftp_Xfer(const char *path, Bytes_struct *inBuf, netbuf *nControl, int mode) { int len = 0, totlen = 0, status = 0; char dbuf[FTP_BUFSIZ]; struct ftpbuf *tail = NULL, *head = NULL; struct ftpbuf *curr; netbuf *nData; inBuf->num = 0; if (!Ftp_Access(path, mode, nControl, &nData)) return SRL_TCP_CONNECTION_FAILED; while ((len = Ftp_Read(dbuf, FTP_BUFSIZ, nData)) > 0) { if (totlen == 0) { status = addFtpBuf(&head, dbuf, len); if (status == 0) return SRL_MEMORY_ERROR; tail = head; totlen = len; } else { status = addFtpBuf(&(tail->next), dbuf, len); if (status == 0) return SRL_MEMORY_ERROR; tail = tail->next; totlen += len; } } // Copy ftpData to inbuf->buf inBuf->data = (unsigned char *) calloc (1, totlen); if (inBuf->data == NULL) { // Could not allocate memory // Free fp & return 0 freeFtpBuf(head); return SRL_SUCCESS; } inBuf->num = 0; len = 0; curr = head; while(curr != NULL) { memcpy(inBuf->data+len, curr->buf, curr->len); inBuf->num += curr->len; len = curr->len; curr = curr->next; } freeFtpBuf(head); Ftp_Socket_close(nData); if (readresp('2', nControl)) return SRL_SUCCESS; else return SRL_FTP_ERROR; }
/* * FtpConnect - connect to remote server * * return 1 if connected, 0 if not */ GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) { int sControl, stat, flags, oldflags; struct sockaddr_in sin; struct hostent *phe; struct servent *pse; int on=1; netbuf *ctrl; char *lhost; char *pnum; struct timeval tv; fd_set wr; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; lhost = strdup(host); pnum = strchr(lhost,':'); if (pnum == NULL) { #if defined(VMS) || defined(ANDROID) sin.sin_port = htons(21); #else if ((pse = getservbyname("ftp","tcp")) == NULL) { perror("getservbyname"); return 0; } sin.sin_port = pse->s_port; #endif } else { *pnum++ = '\0'; if (isdigit(*pnum)) sin.sin_port = htons(atoi(pnum)); else { pse = getservbyname(pnum,"tcp"); sin.sin_port = pse->s_port; } } if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) { if ((phe = gethostbyname(lhost)) == NULL) { perror("gethostbyname"); return 0; } memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); } free(lhost); sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sControl == -1) { perror("socket"); return 0; } if ( setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) { perror("setsockopt"); net_close(sControl); return 0; } #if defined(_WIN32) if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("connect"); net_close(sControl); return 0; } #else //set nonblocking for connection timeout flags = fcntl( sControl, F_GETFL,0); oldflags=flags; fcntl( sControl, F_SETFL, O_NONBLOCK|flags); stat=connect( sControl, (struct sockaddr *)&sin, sizeof(sin)); if (stat < 0) { if (errno != EWOULDBLOCK && errno != EINPROGRESS) { perror("connect"); net_close(sControl); return 0; } } FD_ZERO(&wr); FD_SET( sControl, &wr); tv.tv_sec = ACCEPT_TIMEOUT; tv.tv_usec = 0; stat = select(sControl+1, 0, &wr, 0, &tv); if (stat < 1) { // time out has expired, // or an error has ocurred perror("timeout"); net_close(sControl); return 0; } if (ftplib_debug > 1) printf("connected\n"); //set original flags fcntl( sControl, F_SETFL, oldflags); #endif ctrl = calloc(1,sizeof(netbuf)); if (ctrl == NULL) { perror("calloc"); net_close(sControl); return 0; } ctrl->buf = malloc(FTPLIB_BUFSIZ); if (ctrl->buf == NULL) { perror("calloc"); net_close(sControl); free(ctrl); return 0; } ctrl->handle = sControl; ctrl->dir = FTPLIB_CONTROL; ctrl->ctrl = NULL; ctrl->cmode = FTPLIB_DEFMODE; ctrl->idlecb = NULL; ctrl->writercb = NULL; ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; ctrl->idlearg = NULL; ctrl->writerarg = NULL; ctrl->xfered = 0; ctrl->xfered1 = 0; ctrl->cbbytes = 0; if (readresp('2', ctrl) == 0) { net_close(sControl); free(ctrl->buf); free(ctrl); return 0; } *nControl = ctrl; return 1; }
static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { int len = 0,resp; struct stream_priv_s* p = (struct stream_priv_s*)opts; char str[256],rsp_txt[256]; if(mode != STREAM_READ) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Unknown open mode %d\n",mode); m_struct_free(&stream_opts,opts); return STREAM_UNSUPPORTED; } if(!p->filename || !p->host) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Bad url\n"); m_struct_free(&stream_opts,opts); return STREAM_ERROR; } // Open the control connection p->handle = connect2Server(p->host,p->port,1); if(p->handle < 0) { m_struct_free(&stream_opts,opts); return STREAM_ERROR; } // We got a connection, let's start serious things stream->fd = -1; stream->priv = p; p->buf = malloc(BUFSIZE); if (readresp(p, NULL) == 0) { close_f(stream); m_struct_free(&stream_opts,opts); return STREAM_ERROR; } // Login snprintf(str,255,"USER %s",p->user); resp = FtpSendCmd(str,p,rsp_txt); // password needed if(resp == 3) { snprintf(str,255,"PASS %s",p->pass); resp = FtpSendCmd(str,p,rsp_txt); if(resp != 2) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); close_f(stream); return STREAM_ERROR; } } else if(resp != 2) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); close_f(stream); return STREAM_ERROR; } // Set the transfer type resp = FtpSendCmd("TYPE I",p,rsp_txt); if(resp != 2) { mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt); close_f(stream); return STREAM_ERROR; } // Get the filesize snprintf(str,255,"SIZE %s",p->filename); resp = FtpSendCmd(str,p,rsp_txt); if(resp != 2) { mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); } else { int dummy; sscanf(rsp_txt,"%d %d",&dummy,&len); } if(len > 0) { stream->seek = seek; stream->end_pos = len; } // The data connection is really opened only at the first // read/seek. This must be done when the cache is used // because the connection would stay open in the main process, // preventing correct abort with many servers. stream->fd = -1; stream->priv = p; stream->fill_buffer = fill_buffer; stream->close = close_f; return STREAM_OK; }