home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Magazine
/
wwwoffle-2.1.tar.gz
/
wwwoffle-2.1
/
ftp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-02-11
|
20KB
|
835 lines
/***************************************
$Header: /home/amb/wwwoffle/RCS/ftp.c 1.19 1998/02/11 19:25:04 amb Exp $
WWWOFFLE - World Wide Web Offline Explorer - Version 2.1.
Functions for getting URLs using FTP.
******************/ /******************
Written by Andrew M. Bishop
This file Copyright 1997,98 Andrew M. Bishop
It may be distributed under the GNU Public License, version 2, or
any higher version. See section COPYING of the GNU Public license
for conditions under which this file may be redistributed.
***************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include "wwwoffle.h"
#include "misc.h"
#include "config.h"
#include "errors.h"
#include "sockets.h"
#include "proto.h"
/*+ Set this to 1 to see the full dialog with the FTP server. +*/
#define DEBUG_FTP 0
/*+ Set to the name of the proxy if there is one. +*/
static char *proxy=NULL;
/*+ The file descriptor of the socket +*/
static int server_ctrl=-1, /*+ for the control connection to the server. +*/
server_data=-1; /*+ for the data connection to the server. +*/
/*+ A buffer to contain the reply +*/
static char *bufferhead=NULL, /*+ head. +*/
*buffer=NULL, /*+ body. +*/
*buffertail=NULL; /*+ tail. +*/
/*+ The number of characters in the buffer +*/
static int nbufferhead=0, /*+ in total for the head . +*/
nreadhead=0, /*+ that have been read from the head. +*/
nbuffer=0, /*+ in total for the body part. +*/
nread=0, /*+ that have been read for the body. +*/
nbuffertail=0, /*+ in total for the tail . +*/
nreadtail=0; /*+ that have been read from the tail. +*/
static char *htmlise_dir_entry(char *line);
/*++++++++++++++++++++++++++++++++++++++
Open a connection to get a URL using FTP.
char *FTP_Open Returns NULL on success, a useful message on error.
URL *Url The URL to open.
++++++++++++++++++++++++++++++++++++++*/
char *FTP_Open(URL *Url)
{
char *msg=NULL;
char *colon;
char *server_host=NULL;
int server_port=Protocols[Protocol_FTP].defport;
/* Sort out the host. */
proxy=WhichProxy(Url->proto,Url->host);
if(IsLocalNetHost(Url->host))
proxy=NULL;
if(proxy)
{
server_host=(char*)malloc(strlen(proxy)+1);
strcpy(server_host,proxy);
}
else
{
server_host=(char*)malloc(strlen(Url->host)+1);
strcpy(server_host,Url->host);
}
if((colon=strchr(server_host,':')))
{
*colon++=0;
if(*colon)
server_port=atoi(colon);
}
/* Open the connection. */
server_ctrl=OpenClientSocket(server_host,server_port);
init_buffer(server_ctrl);
if(server_ctrl==-1)
msg=PrintMessage(Warning,"Cannot open the FTP control connection to %s port %d; [%!s].",server_host,server_port);
free(server_host);
return(msg);
}
/*++++++++++++++++++++++++++++++++++++++
Write to the server to request the URL.
char *FTP_Request Returns NULL on success, a useful message on error.
URL *Url The URL to get.
char *request_head The head of the HTTP request for the URL.
char *request_body The body of the HTTP request for the URL.
++++++++++++++++++++++++++++++++++++++*/
char *FTP_Request(URL *Url,char *request_head,char *request_body)
{
char *msg=NULL,*str=NULL;
char *path,*file=NULL;
char *host,*mimetype;
char *msg_reply=NULL;
int i,l,port_l,port_h;
char *user,*pass;
/* Take a simple route if it is proxied. */
if(proxy)
{
char *new_request_head=MakeRequestAuthorised(proxy,request_head);
if(write_string(server_ctrl,new_request_head)==-1)
msg=PrintMessage(Warning,"Failed to write to remote FTP proxy [%!s].");
if(request_body && write_string(server_ctrl,request_body)==-1)
msg=PrintMessage(Warning,"Failed to write to remote FTP proxy [%!s].");
if(request_head!=new_request_head)
free(new_request_head);
return(msg);
}
/* Else Sort out the path. */
path=UrlDecode(Url->path,0);
if(path[strlen(path)-1]=='/')
{
path[strlen(path)-1]=0;
file=NULL;
}
else
for(i=strlen(path)-1;i>=0;i--)
if(path[i]=='/')
{
path[i]=0;
file=&path[i+1];
break;
}
WhatFTPUserPass(Url->host,&user,&pass);
/* send all the RFC959 commands. */
server_data=-1;
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: connected; got: %s",str);
#endif
if(!file && !*path && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
{
if(msg_reply)
{
msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
strcat(msg_reply,str+4);
}
else
{
msg_reply=(char*)malloc(strlen(str));
strcpy(msg_reply,str+4);
}
}
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || atoi(str)!=220)
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message when connected to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server when connected; timed out?");
return(msg);
}
/* Login */
if(write_formatted(server_ctrl,"USER %s\r\n",user)==-1)
{
msg=PrintMessage(Warning,"Failed to write 'USER' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'USER %s'; got: %s",user,str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || (atoi(str)!=230 && atoi(str)!=331))
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'USER' command to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'USER' command; timed out?");
return(msg);
}
if(atoi(str)==331)
{
if(write_formatted(server_ctrl,"PASS %s\r\n",pass)==-1)
{
msg=PrintMessage(Warning,"Failed to write 'PASS' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'PASS %s'; got: %s",pass,str);
#endif
if(!file && !*path && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
{
if(msg_reply)
{
msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
strcat(msg_reply,str+4);
}
else
{
msg_reply=(char*)malloc(strlen(str));
strcpy(msg_reply,str+4);
}
}
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || (atoi(str)!=202 && atoi(str)!=230))
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'PASS' command to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'PASS' command; timed out?");
return(msg);
}
}
/* Change directory */
if(write_formatted(server_ctrl,"CWD %s\r\n",*path?path:"/")==-1)
{
msg=PrintMessage(Warning,"Failed to write 'CWD' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'CWD %s' got: %s",*path?path:"/",str);
#endif
if(!file && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
{
if(msg_reply)
{
msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
strcat(msg_reply,str+4);
}
else
{
msg_reply=(char*)malloc(strlen(str));
strcpy(msg_reply,str+4);
}
}
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || atoi(str)!=250)
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'CWD' command to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'CWD' command; timed out?");
return(msg);
}
/* Change directory again to see if file is a dir. */
if(file)
{
if(write_formatted(server_ctrl,"CWD %s\r\n",file)==-1)
{
msg=PrintMessage(Warning,"Failed to write 'CWD' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'CWD %s' got: %s",file,str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || (atoi(str)!=250 && atoi(str)!=550))
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'CWD' command to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'CWD' command; timed out?");
return(msg);
}
if(atoi(str)==250)
{
char *msghead=
"HTTP/1.0 302 Is a directory\r\n"
"Content-Type: text/html\r\n"
"Location: %s/\r\n"
"\r\n";
char *msg=
"<HTML>\n"
"<HEAD>\n"
"<TITLE>FTP Directory requires trailing '/'</TITLE>\n"
"</HEAD>\n"
"<BODY>\n"
"<h1>Index of %s</h1>\n"
"You need to specify a URL with a '/' at the end for a directory\n"
"<br>\n"
"<a href=\"%s/\">%s/</a>\n";
char *msgtail=
"</BODY>\n"
"</HTML>\n";
bufferhead=(char*)malloc(strlen(Url->link)+strlen(msghead)+1);
sprintf(bufferhead,msghead,Url->link);
buffer=(char*)malloc(2*strlen(Url->name)+strlen(Url->link)+strlen(msg)+1);
sprintf(buffer,msg,Url->name,Url->link,Url->name);
buffertail=msgtail;
goto near_end;
}
}
/* Set mode to binary or ASCII */
if(write_formatted(server_ctrl,"TYPE %c\r\n",file?'I':'A')==-1)
{
msg=PrintMessage(Warning,"Failed to write 'TYPE' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'TYPE %c'; got: %s",file?'I':'A',str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || atoi(str)!=200)
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'TYPE' command to FTP server.",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'TYPE' command; timed out?");
return(msg);
}
/* Create the data connection. */
if(write_string(server_ctrl,"PASV\r\n")==-1)
{
msg=PrintMessage(Warning,"Failed to write 'PASV' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'PASV'; got: %s",str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
if(!str || atoi(str)!=227)
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'PASV' command",str);
}
else
msg=PrintMessage(Warning,"No reply from FTP server to 'PASV' command; timed out?");
return(msg);
}
if((host=strchr(str,',')))
{
while(isdigit(*--host));
host++;
}
if(!host || sscanf(host,"%*d,%*d,%*d,%*d%n,%d,%d",&l,&port_h,&port_l)!=2)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending 'PASV' command, cannot parse.",str);
return(msg);
}
host[l]=0;
for(;l>0;l--)
if(host[l]==',')
host[l]='.';
server_data=OpenClientSocket(host,port_l+256*port_h);
init_buffer(server_data);
if(server_data==-1)
{
msg=PrintMessage(Warning,"Cannot open the FTP data connection [%!s].");
return(msg);
}
/* Make the request */
if(file)
{
if(write_formatted(server_ctrl,"RETR %s\r\n",file)==-1)
{
msg=PrintMessage(Warning,"Failed to write 'RETR' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'RETR %s'; got: %s",file,str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
mimetype=WhatMIMEType(file);
}
else
{
if(write_string(server_ctrl,"LIST\r\n")==-1)
{
msg=PrintMessage(Warning,"Failed to write 'LIST' command to remote FTP host [%!s].");
return(msg);
}
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'LIST'; got: %s",str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
mimetype="text/html";
}
if(str && (atoi(str)==150 || atoi(str)==125))
*str='2',*(str+1)='0',*(str+2)='0';
else
{
if(str)
{
char *p=str+strlen(str)-1;
while(*p=='\n' || *p=='\r') *p--=0;
msg=PrintMessage(Warning,"Got '%s' message after sending '%s' command to FTP server.",str,file?"RETR":"LIST");
}
else
msg=PrintMessage(Warning,"No reply from FTP server to '%s' command; timed out?",file?"RETR":"LIST");
return(msg);
}
/* Prepare the HTTP header. */
bufferhead=(char*)malloc(strlen(str)+strlen(mimetype)+32);
strcpy(bufferhead,"HTTP/1.0 ");
strcat(bufferhead,str);
strcat(bufferhead,"Content-Type: ");
strcat(bufferhead,mimetype);
strcat(bufferhead,"\r\n");
strcat(bufferhead,"\r\n");
buffer=NULL;
buffertail=NULL;
if(!file)
{
char *msg=
"<HTML>\n"
"<HEAD>\n"
"<TITLE>Index of %s</TITLE>\n"
"<BASE HREF=\"%s\">\n"
"</HEAD>\n"
"<BODY>\n"
"<h1>Index of %s</h1>\n"
"<pre>\n";
char *msgtail=
"</pre>\n"
"</BODY>\n"
"</HTML>\n";
char *path=UrlDecode(Url->name,0);
if(msg_reply)
{
char *old_msg_reply=msg_reply;
msg_reply=HTMLString(msg_reply);
free(old_msg_reply);
}
buffer=(char*)malloc((msg_reply?strlen(msg_reply)+8:0)+strlen(msg)+3*strlen(path)+8);
sprintf(buffer,msg,path,path,path);
if(msg_reply)
{
strcat(buffer,msg_reply);
strcat(buffer,"</pre>\n"
"<pre>\n");
}
buffertail=msgtail;
free(path);
}
near_end:
if(msg_reply)
free(msg_reply);
nbufferhead=strlen(bufferhead); nreadhead=0;
nbuffer=buffer?strlen(buffer):0; nread=0;
nbuffertail=buffertail?strlen(buffertail):0; nreadtail=0;
free(str);
free(path);
return(msg);
}
/*++++++++++++++++++++++++++++++++++++++
Read a line from the header of the reply for the URL.
char *FTP_ReadHead Returns the next line of data, NULL on EOF.
char *line The previous line read.
++++++++++++++++++++++++++++++++++++++*/
char *FTP_ReadHead(char *line)
{
char *l=line;
int m;
/* Take a simple route if it is proxied. */
if(proxy)
return(read_line_or_timeout(server_ctrl,line));
/* Else send the header. */
if(nreadhead==nbufferhead)
return(NULL);
for(m=nreadhead;m<nbufferhead;m++)
if(bufferhead[m]=='\n')
break;
m++;
l=(char*)realloc((void*)l,m-nreadhead+1);
strncpy(l,&bufferhead[nreadhead],m-nreadhead);
l[m-nreadhead]=0;
nreadhead=m;
return(l);
}
/*++++++++++++++++++++++++++++++++++++++
Read bytes from the body of the reply for the URL.
int FTP_ReadBody Returns the number of bytes read on success, -1 on error.
char *s A string to fill in with the information.
int n The number of bytes to read.
++++++++++++++++++++++++++++++++++++++*/
int FTP_ReadBody(char *s,int n)
{
int m=0;
/* Take a simple route if it is proxied. */
if(proxy)
return(read_data_or_timeout(server_ctrl,s,n));
/* Else send the data then the tail. */
if(server_data==-1) /* Redirection */
{
for(;nread<nbuffer && m<n;nread++,m++)
s[m]=buffer[nread];
for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
s[m]=buffertail[nreadtail];
}
else if(!buffer && !buffertail) /* File not dir entry. */
m=read_data_or_timeout(server_data,s,n);
else if(buffer && buffertail) /* Middle of dir entry */
{
for(;nread<nbuffer && m<n;nread++,m++)
s[m]=buffer[nread];
if(nread==nbuffer)
{
buffer=htmlise_dir_entry(buffer);
if(buffer)
nbuffer=strlen(buffer),nread=0;
}
if(!buffer)
for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
s[m]=buffertail[nreadtail];
}
else if(!buffer && buffertail) /* End of dir entry. */
{
for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
s[m]=buffertail[nreadtail];
}
/* Clear up at the very end. */
if(m==0)
if(buffer)
free(buffer);
return(m);
}
/*++++++++++++++++++++++++++++++++++++++
Close a connection opened using FTP.
int FTP_Close Return 0 on success, -1 on error.
++++++++++++++++++++++++++++++++++++++*/
int FTP_Close(void)
{
int err=0;
char *str=NULL;
/* Take a simple route if it is proxied. */
if(proxy)
return(CloseSocket(server_ctrl));
/* Else say goodbye and close all of the sockets, */
write_string(server_ctrl,"QUIT\r\n");
do
{
str=read_line_or_timeout(server_ctrl,str);
#if DEBUG_FTP
PrintMessage(Debug,"FTP: sent 'QUIT'; got: %s",str);
#endif
}
while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
/* Close the sockets */
err=CloseSocket(server_ctrl);
if(server_data!=-1)
CloseSocket(server_data);
if(bufferhead)
free(bufferhead);
return(err);
}
/*++++++++++++++++++++++++++++++++++++++
Convert a line from the ftp server dir listing into a pretty listing.
char *htmlise_dir_entry Returns the next line.
char *line The previous line.
++++++++++++++++++++++++++++++++++++++*/
static char *htmlise_dir_entry(char *line)
{
int i,isdir=0;
char *q,*p[16],*l,*file=NULL,*link=NULL;
l=read_line_or_timeout(server_data,line);
if(!l)
return(l);
for(q=l,i=0;*q && i<16;i++)
{
while(*q==' ' || *q=='\t')
q++;
if(*q=='\n' || *q=='\r')
break;
if(*q)
p[i]=q;
while(*q && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
q++;
}
if((*p[0]=='-' || *p[0]=='d' || *p[0]=='l') && i>=8) /* A UNIX 'ls -l' listing. */
{
if(i==8)
{file=p[7]; link=file;}
else if(i==10 && (*p[8]=='-' && *(p[8]+1)=='>'))
{file=p[7]; link=p[9];}
else if(i==9)
{file=p[8]; link=file;}
else if(i==11 && (*p[9]=='-' && *(p[9]+1)=='>'))
{file=p[8]; link=p[10];}
if(*p[0]=='d')
isdir=1;
}
if(file)
{
char *endf,endfc,*endl,endlc,*ll,*urlenc;
endf=file;
while(*endf && *endf!=' ' && *endf!='\t' && *endf!='\r' && *endf!='\n')
endf++;
endfc=*endf;
endl=link;
while(*endl && *endl!=' ' && *endl!='\t' && *endl!='\r' && *endl!='\n')
endl++;
endlc=*endl;
*endl=0;
urlenc=UrlEncode(link);
*endl=endlc;
ll=(char*)malloc(strlen(l)+strlen(urlenc)+24);
strncpy(ll,l,file-l);
strcpy(ll+(file-l),"<a href=\"");
strcat(ll,urlenc);
if(isdir && *(endl-1)!='/')
strcat(ll,"/");
strcat(ll,"\">");
*endf=0;
strcat(ll,file);
*endf=endfc;
strcat(ll,"</a>");
strcat(ll,endf);
free(urlenc);
if(l)
free(l);
l=ll;
}
return(l);
}