ftp.nice.ch/pub/next/connectivity/filetransfer/Yftp.0.564.NIHS.bs.tar.gz#/Yftp/Yftp.0.564/FtpSubThread.m

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.