This is FtpSubThread.m in view mode; [Download] [Up]
//#define TERM
#ifdef TERM
#import "termnet.h"
#endif // TERM
#import "FtpConnection.h"
#import "FtpSubThread.h"
#import "FtpDirectory.h"
#import "NXmystd.h"
#import <remote/NXProxy.h>
#import <netdb.h>
#import <stdarg.h>
#import <ctype.h>
#import <sys/timeb.h>
#define DEFAULTPORT 7358
#define MAXPORT 8000
NXLock *chdirLock = nil;
void ftime(struct timeb *tp); // This is defined nowhere in the headers !
int my_chdir(const char *path)
{
if (!chdirLock)
chdirLock = [[NXLock alloc] init];
[chdirLock lock];
return chdir (path);
}
void my_unchdir()
{
chdir("/");
[chdirLock unlock];
}
FILE *my_fopen(const char *path,const char *name,const char *mode)
{
FILE *fp;
my_chdir (path);
fp = fopen(name,mode);
my_unchdir();
return fp;
}
@implementation FtpSubThread
- (void oneway) setFtpConnection:obj;
{
//malloc_debug(7);
conn = obj;
[[conn connectionForProxy] registerForInvalidationNotification:self];
// init
recvpos = 0; recvfullpos = 0;
}
- senderIsInvalid:sender;
{
//LOGF("%p",sender);
return self;
}
- (void oneway) connectToHost:(const char *)name;
{
struct sockaddr_in addr;
struct hostent *ent;
char buf[1000];
int len;
[conn show:"creating socket"];
[self closeConnection];
sock = socket(PF_INET,SOCK_STREAM,0);
if(sock < 0)
{
[conn connectFailed:"couldnt create socket"];
return;
}
memset((char *)&addr, 0, sizeof(struct sockaddr));
addr . sin_family = AF_INET;
addr . sin_port = htons(21);
ent = gethostbyname((char *)name);
if (ent == NULL)
{
if( (addr . sin_addr.s_addr = inet_addr((char *)name)) == -1)
{
[conn connectFailed:"no such host !"];
return;
}
}
else // ToDo: should try EVERY address !!
{
//printf(ent -> h_name);
memcpy( (void *)&(addr . sin_addr), ent -> h_addr, ent -> h_length);
}
sprintf(buf,"trying %s...", inet_ntoa(addr.sin_addr));
[conn show:buf];
if( (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0))
{
[conn connectFailed:"connect failed !!!"];
perror("connectToHost: connect");
return;
}
len = sizeof (dataaddr);
if (getsockname(sock, (struct sockaddr *)&dataaddr, &len) < 0)
{
[conn connectFailed:"getsockname failed !!!"];
perror("connectToHost: getsockname");
return;
}
//printf("sock = %d,ent = %p,\n",sock,ent);
if ([self getResult] == 220)
[conn connected];
else
[conn connectFailed:"remote host refused connection"];
}
- (void oneway) closeConnection;
{
if (sock)
{
//[self putCommand:"QUIT"];
close(sock);
sock = -1;
}
}
- free;
{
[self closeConnection];
return [super free];
}
- (void oneway) loginAsUser:(const char *)user withPassword:(const char *)pass;
{
int res;
[self putCommand:"USER":user];
if ([self getResult] != 331)
{
sprintf(line,"user %s: access denied",user);
[conn loginFailed:line];
return;
}
[self putCommand:"PASS":pass];
if ([self getResult] != 230)
{
sprintf(line,"wrong password for user: %s",user);
[conn loginFailed:line];
return;
}
[self putCommand:"TYPE" :"A"];
res = [self getResult];
if (res != 200)
{
[conn connectionLost:"TYPE failed"];
return;
}
#ifdef DEBUG
[self putCommand:"SITE IDLE 30"];
res = [self getResult];
if (res != 200)
{
[conn connectionLost:"SITE IDLE failed"];
return;
}
#endif
transfermode = ASCII_MODE;
currentdir = NXUniqueString("nosuchdir");
[conn loggedIn];
}
- (void oneway) listDir:dir;
{
NXAtom path = [dir path];
int res;
int connectsock,datasock;
char *s;
//LOGF("path = %s",path);
if (transfermode != ASCII_MODE)
{
[self putCommand:"TYPE" :"A"];
res = [self getResult];
if (res != 200)
{
[conn connectionLost:"TYPE failed"];
return;
}
transfermode = ASCII_MODE;
}
if (currentdir != path)
{
[self putCommand:"CWD" :path];
res = [self getResult];
if (res != 250 && res != 200)
{
[conn transferError:dir:"CWD failed"];
return;
}
currentdir = path;
}
//if (![dir shouldTransferAlways]
//&& ![dir needsUpdate] )
if (0)
{
char *s1,*s2;
//LOGF("STATting %s\n",[dir path]);
[conn checkingDir:dir];
[self putCommand:"STAT" :"-dLl"];
res = [self getResult];
if (res != 211) // 502 STAT not implemented
{
[dir setShouldTransferAlways];
goto transfer; // break out of the if
//[conn transferError:dir:"STAT failed"];
//return;
}
s1 = strchr(line,'\n');
s2 = strrchr(line,'\n');
if (s2)
{
*s2 = '\0';
s2 = strrchr(line,'\n');
}
if (s2)
{
*s2 = '\0';
s2 = strrchr(line,' ');
}
if (s1 && s2)
{
*s2 = '\0';
s1++;
if (![dir compareStatusString:s1]) // it changed
{
if ([dir shouldTransferIfNeeded])
[dir setShouldTransferAlways];
[dir setStatusString:s1];
//LOGF("set statusline to:|%s|",s1);
}
}
else
{
ERRORF("illegal stat:|%s|",line);
if ([dir shouldTransferIfNeeded])
[dir setShouldTransferAlways];
}
}
transfer:
// oink oink
if ([dir shouldTransferIfNeeded])
[dir setShouldTransferAlways];
//LOGF("listingdir");
//LOGF("line = <%s>",line);
if ([dir shouldTransferAlways] || [dir needsUpdate])
{
//LOGF("RETRieving %s\n",[dir path]);
[conn listingDir:dir];
//LOGF("line = |%s|",line);
connectsock = [self createPort];
if (connectsock < 0)
{
[conn socketError:"couldn't create socket"];
[self closeConnection];
return;
}
//printf("createt socker:%d\n", connectsock);
[self putCommand:"NLST":"-lL"];
res = [self getResult];
if (res != 150)
{
close (connectsock);
connectsock = -1;
[conn transferError:dir:"NLST failed"];
return;
}
datasock = [self acceptPort:connectsock];
close(connectsock);
connectsock = -1;
if (datasock < 0)
{
[conn socketError:"couldn't create socket"];
[self closeConnection];
return;
}
//printf("accepted socker:%d\n", datasock);
[dir beginUpdate];
// all this is definitely stupid, but it seems to work, somehow...
while((s=[self getLineFrom:datasock]) != NULL)
{
int len = strlen(s)-1; // withut newline
int i;
id theNewFile;
char statline[1000];
enum {FTP_DIR,FTP_FILE,FTP_LINK,FTP_OTHER} filetype;
long size;
// I cant use flex for this, cause it isnt multithreaded and
// reentrant, BLAECH
s[len] = '\0'; // remove the newline
//LOGF("s=|%s|",s);
//LOGF("len = %d",len);
if (len < 35)
continue;
strcpy(statline,s); // save for later compareStatusString;
i = 0;
if (s[0] == 'd')
filetype = FTP_DIR;
else if (s[0] == 'l')
filetype = FTP_LINK;
else if (s[0] == '-')
filetype = FTP_FILE;
else
filetype = FTP_OTHER;
i = 10;
while(!isdigit(s[i]))
i++;
// linkcount
while(isdigit(s[i]))
i++;
while(isspace(s[i]))
i++;
// UID
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
// Now comes the tricky part...
// the next one is either the group, or the filesize
// but we cant tell yet
size = atol(s+i); // Just save it for later reference...
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
// Now we are either on the filesize, or already on the month
// and now we can tell :)
if (isdigit(s[i]))
{
// Its the size
size = atol(s+i);
// and skip it
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
}
// Month
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
// day
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
// time or year
while(!isspace(s[i]))
i++;
while(isspace(s[i]))
i++;
// filename !!
//LOGF("size = %d",size);
if (filetype == FTP_LINK) // hackhack...
{
char *tmp = strstr(s+i," -> ");
if (tmp)
*tmp = '\0';
filetype = FTP_DIR;
}
//LOGF("update:|%s|\n",s);
theNewFile = [dir update:s+i withSize:size isFile:filetype == FTP_FILE];
//LOGF("theNewFile = %p,isDir = %s",theNewFile,[theNewFile isDir]?"DIR":"FILE");
if (theNewFile)
{
char *s = strrchr(statline,' ');
if (s)
*s = '\0';
if ([theNewFile isDir] && ![theNewFile compareStatusString:statline])
{
//puts("mist");
[theNewFile setNeedsUpdate];
[theNewFile setStatusString:statline];
LOGF("dir setStatusString:|%s|",statline);
}
else if ([theNewFile isDir])
{
[theNewFile setNeedsNoUpdate];
}
}
else
{
ERRORF("theNewFile == NULL");
}
//LOGF("elihw");
#if 0 // This is the old routine
len--;
if (s[len] == '@')
{
s[len--] = '\0';
}
if (s[len] == '/') // NO else !!
{
isFile = NO;
s[len--] = '\0';
}
else if (s[len] == '*') // else !!
{
s[len--] = '\0';
}
#endif
}
close(datasock);
datasock=-1;
[dir endUpdate];
if ([self getResult] != 226)
{
[conn transferError:dir:"directory listing failed"];
return;
}
}
else
[dir endUpdate];
//LOGF("dirlisted");
[conn dirListed:dir];
}
- (void oneway) getFile:file;
{
NXAtom path;
int res,len;
long siz;
int connectsock,datasock;
unsigned char buf[16384];
FILE *localfp=NULL;
BOOL aborted = NO;
long nowtime;
struct timeb tp;
lastshouldstoptime = 0;
//printf("subthread: localPath = %s\n",[file localPath]);
//LOGF("getfilename = |%s|,localName = %s",[file name],[file localName]);
//LOGF("unique getfilename = |%s|",NXUniqueString([file name]));
path = [[file parentDir] path];
siz = 0;
if ([file shouldReget])
{
//LOGF("regetting file=%s",[file name]);
localfp = my_fopen([file localPath],[file localName],"r");
if (localfp == NULL)
{
sprintf(buf,"couldnt find %s/%s, creating",[file localPath],[file localName]);
[conn show:buf];
}
else
{
fseek(localfp,0,SEEK_END);
siz = ftell(localfp);
fclose(localfp);
localfp = my_fopen([file localPath],[file localName],"a");
if (localfp == NULL)
{
sprintf(buf,"couldnt create %s/%s",[file localPath],[file localName]);
[conn transferError:file:buf];
return;
}
}
}
if (!localfp)
{
//LOGF("replacing file=%s",[file name]);
localfp = my_fopen([file localPath],[file localName],"w");
if (localfp == NULL)
{
sprintf(buf,"couldnt create %s/%s",[file localPath],[file localName]);
[conn transferError:file:buf];
return;
}
}
//LOGF("siz = %d",siz);
if (transfermode != BINARY_MODE)
{
[self putCommand:"TYPE" :"I"];
res = [self getResult];
if (res != 200)
{
[conn connectionLost:"TYPE failed"];
goto getfile_error;
}
transfermode = BINARY_MODE;
}
if (currentdir != path)
{
[self putCommand:"CWD" :path];
res = [self getResult];
if (res != 250 && res != 200)
{
[conn transferError:file:"CWD failed"];
goto getfile_error;
}
currentdir = path;
}
[self putCommand:"SIZE" :[file name]];
res = [self getResult];
if (res != 213) // 500 SIZE not implemented
{
//[file setFileSize:-1];
//[conn transferError:file:"SIZE failed"];
//goto getfile_error;
}
else
{
if (strlen(line) >=4)
{
long newsize = atol(line+4);
if (newsize != [file fileSize])
{
[file setFileSize:newsize];
[[file parentDir] setNeedsUpdate];
}
}
//else
// [file setFileSize:-1];
}
[conn retrievingFile:file];
connectsock = [self createPort];
if (connectsock < 0)
{
[conn socketError:"couldn't create socket"];
goto getfile_error;
}
//printf("createt socker:%d\n", connectsock);
if (siz)
{
sprintf(buf,"%ld",siz);
[self putCommand:"REST":buf];
res = [self getResult];
if (res != 350)
{
close (connectsock);
connectsock = -1;
[conn transferError:file:"RESTart failed"];
goto getfile_error;
}
}
[self putCommand:"RETR":[file name]];
res = [self getResult];
if (res != 150)
{
close (connectsock);
connectsock = -1;
[conn transferError:file:"RETRieve failed"];
goto getfile_error;
}
datasock = [self acceptPort:connectsock];
close(connectsock);
connectsock = -1;
if (datasock < 0)
{
[conn socketError:"accept socket failed"];
goto getfile_error;
}
//printf("accepted socker:%d\n", datasock);
//printf("sizeof buf:%ld\n",sizeof(buf));
[file setShouldReget:YES];
[conn shouldStopAtPosition:siz]; // just to set the progress views
while(TRUE)
{
LOGF("datasock = %d",datasock);
len = recv(datasock,buf,sizeof(buf),0);
LOGF("len = %d",len);
if (len == -1)
{
perror("getFile: recv failed");
close(datasock);
datasock = -1;
[conn connectionLost:"recv from socket failed!!!!"];
fclose(localfp);
[file setShouldReget:YES];
return;
}
if (len == 0)
{
//puts("len = 0: file transfer finished");
break;
}
res = fwrite(buf,sizeof(unsigned char),len,localfp);
//printf("len:%d,res:%d\n",len,res);
if (res != len)
{
[conn transferError:file:"write to local file failed"];
close(datasock);
datasock = -1;
fclose(localfp);
return;
}
siz+=len;
//printf("siz = %d,len = %ld\n",siz,len);
//[conn shouldStopAtPosition:siz];
ftime(&tp);
nowtime = tp.time*1000 + tp.millitm;
if (nowtime > lastshouldstoptime + 100)
{
[[conn connectionForProxy] setInTimeout:500];
[[conn connectionForProxy] setOutTimeout:0];
NX_DURING
if ([conn shouldStopAtPosition:siz] && !aborted)
{
[conn show:"aborting..."];
sprintf(buf,"%c%c%c%c",(u_char)0xff,(u_char)0xf4,(u_char)0xff,(u_char)0xf2);
send(sock,"\0xff\0xf4",2, MSG_OOB);
send(sock,"\0xff",1,MSG_OOB);
send(sock,"\0xf2",1, MSG_OOB);
[conn show:"aborting2..."];
[self putCommand:"ABOR"];
[conn show:"trying to interrupt transfer..."];
aborted = YES;
//shutdown(datasock,2);
}
NX_HANDLER
//LOGF("message did not go through !");
NX_ENDHANDLER
[[conn connectionForProxy] setInTimeout:-1];
[[conn connectionForProxy] setOutTimeout:-1];
}
lastshouldstoptime = nowtime;
}
[conn shouldStopAtPosition:siz]; // just to set the progress views
if (aborted)
[conn show:"aborting4..."];
fclose(localfp);
close(datasock);
datasock = -1;
if (!aborted)
{
[file setShouldReget:NO];
[file setLocalName:NULL];
}
res = [self getResult];
if (res == 226)
{
[conn fileRetrieved:file];
}
else if (!aborted || res != 426)
{
[conn transferError:file:"RETR failed2"];
}
if (aborted)
{
res = [self getResult];
if (res == 225 || res == 500)
{
[conn show:"aborted !"];
[conn fileInterrupted:file];
}
else
{
[conn transferError:file:"ABORt failed"];
}
}
return;
getfile_error:
fclose(localfp);
sprintf(buf,"%s/%s",[file localPath],[file localName]);
unlink(buf);
[file setShouldReget:NO];
[file setLocalName:NULL];
return;
}
- (int) createPort;
{
int datasock;
char buf[1000];
char *a = (char *) &dataaddr.sin_addr;
char *p = (char *) &dataaddr.sin_port;
int len = sizeof(dataaddr);
datasock = socket(AF_INET,SOCK_STREAM,0);
if(datasock < 0)
{
return datasock;
}
#define UC(b) (((int)b)&0xff)
//printf("%d,%d,%d,%d:%d,%d\n",UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]),UC(p[0]),UC(p[1]));
dataaddr . sin_port = 0; //htons(listenport);
if( (bind(datasock, (struct sockaddr *)&dataaddr, sizeof(dataaddr)) < 0))
{
ERRORF("sock = %d,datasock = %d\n",sock,datasock);
perror("createPort: bind");
return -1;
}
if (getsockname(datasock, (struct sockaddr *)&dataaddr, &len) < 0) {
ERRORF("sock = %d,datasock = %d\n",sock,datasock);
perror("createPort: getsockname");
return -1;
}
listen(datasock, 0);
if (listen(datasock, 0) < 0) {
ERRORF("sock = %d,datasock = %d\n",sock,datasock);
perror("createPort: listen");
return -1;
}
//printf("%d,%d,%d,%d:%d,%d\n", UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]),UC(p[0]),UC(p[1]));
sprintf(buf,"PORT %d,%d,%d,%d,%d,%d", UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]),UC(p[0]),UC(p[1]));
[self putCommand:buf];
[self getResult];
return datasock;
}
- (int) acceptPort:(int) datasock
{
struct sockaddr_in addr;
int len = sizeof(addr);
int newsock;
memset((char *)&addr, 0, sizeof(struct sockaddr));
newsock = accept(datasock, (struct sockaddr *) &addr, &len);
//printf("acceptsocket: oldsock = %d,newsock = %d\n",datasock,newsock);
return newsock;
}
// get and put Methods...=
- (int) getResult;
{
char *s,*l;
int result;
s = [self getLineFrom:sock];
if (!s)
{
[conn console:"421 remote closed...\n"];
[conn connectionLost:"remote server closed connection"];
puts("421 remote closed...");
return 421;
}
strncpy(line,s,sizeof(line)-1);
line[sizeof(line)-1]='\0';
l = line + strlen(s);
if (strlen(s) < 4)
{
//printf("len = %ld, s = |%s|\n",strlen(s),s);
[conn connectionLost:"illegal response"];
return -1;
}
result = ((s[0]-'0')*10+s[1]-'0')*10+s[2]-'0';
//LOGF("sock=%d,result=%d,line=|%s|",sock,result,line);
if (s[3] == '-')
{
while (1)
{
s=[self getLineFrom:sock];
if (!s)
{
[conn console:"421 remote closed...\n"];
[conn connectionLost:"remote server closed connection"];
puts("421 remote closed...");
return 421;
}
LOGF("sock=%d,line=|%s|",sock,s);
if (l < line+sizeof(line))
{
strncpy(l,s,sizeof(line)-(l-line)-1);
line[sizeof(line)-1]='\0';
l = l + strlen(s);
}
if (s[3] == ' '
&& isdigit(s[0])
&& isdigit(s[1])
&& isdigit(s[2])
&& ((s[0]-'0')*10+s[1]-'0')*10+s[2]-'0' == result
)
{
break;
}
}
}
if (421 == result)
{
[conn connectionLost:line];
close(sock);
sock = -1;
}
return result;
}
#if 1
// get data from the given socket until the next '\n'
- (char *) getLineFrom:(int) mysock;
{
int i;
int len;
char c=0;
i=0;
do
{
// /*if (mysock <0)*/ LOGF("mysock = %d",mysock);
len = recv(mysock, &c,1,0);
// /*if (len <1)*/ LOGF("len = %d,c=%c (%d)",len,c,(int)c);
if (len == 0)
{
//printf("got 0 len from socket %d\n",mysock);
return NULL;
}
else if (len != 1)
{
perror("recv failed");
close(mysock);
mysock = -1;
[conn connectionLost:"recv failed"];
return NULL;
}
if (c == '\r')
continue;
//printf("got [%d]\n",(int) c);
if (c == '\n')
{
c = '\n';
break;
}
resbuf[i++] = c;
} while(1);
resbuf[i] = '\n';
resbuf[i+1] = '\0';
//printf("<%s>\n",resbuf);
if (mysock == sock)
{
[conn console:resbuf];
}
return resbuf;
}
#else
- (char *) getLineFrom:(int) mysock;
{
int resbufi;
char c;
LOGF("pos=%d,fullpos=%d",recvpos,recvfullpos);
LOGF("socket:%d,recvbuf:\n|%.*s|",mysock,recvfullpos-recvpos,recvbuf+recvpos);
for(resbufi=0; resbufi<sizeof(resbuf)-1;recvpos++)
{
if (recvpos >= recvfullpos)
{
int len;
LOGF("trying to receive");
len = recv(mysock, recvbuf,sizeof(recvbuf),0);
recvpos=0;
recvfullpos = len;
if (len == 0)
{
LOGF("got 0 len from socket %d\n",mysock);
return NULL;
}
else if (len < 0)
{
perror("recv failed");
close(mysock);
mysock = -1;
[conn connectionLost:"recv failed"];
return NULL;
}
LOGF("received %d new bytes. buf:\n|%.*s|",len,len,recvbuf);
}
c = recvbuf[recvpos];
//LOGF("pos=%d,fullpos=%d,char=|%c|",recvpos,recvfullpos,c);
if (c == '\r')
continue;
//printf("got [%d]\n",(int) c);
if (c == '\n')
break;
resbuf[resbufi++] = c;
}
recvpos++;
resbuf[resbufi++] = '\n';
resbuf[resbufi] = '\0';
LOGF("returning:\n|%s|",resbuf);
if (mysock == sock)
{
[conn console:resbuf];
}
return resbuf;
}
#endif
- (int) putCommand:(const char *)s;
{
int len;
char buf[1000];
strcpy(buf,s);
if ( buf[strlen(buf)-1] != '\n') strcat(buf,"\n");
LOGF("len=%d,buf=|%s|",strlen(buf),buf);
LOGF("sock = %d",sock);
len = send(sock, buf, strlen(buf), 0);
LOGF("len = %d",len);
if (len < 0)
{
[conn connectionLost:"send failed"];
close (sock);
sock = -1;
return -1;
}
//[conn console:">>> "];
//[conn console:buf];
LOGF(">>> %s",buf);
return 0;
}
- (int) putCommand:(const char *)s1:(const char *)s2;
{
char buf[1000];
sprintf(buf,"%s %s\n",s1,s2);
return [self putCommand:buf];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.