#!/usr/bin/gawk -f # # # ftpclient [] # # This is a sample command line FTP client script for gawk and # connect. Two command line options are available: # # -d sets FTP debug mode. # # -l : sets username and password # for the server login. # # See this source code for further details. # function noctrl(line) { sub(/[ \t\r\n]+$/, "", line); return (line); } function cfgets(ftpd, line) { if (ftpd |& getline line > 0) { line = noctrl(line); if (debug != 0) printf (">>> %s\n", line) >>STDERR; return (line); } return ("\001"); } function cfputs(ftpd, line) { line = noctrl(line); if (debug != 0) printf ("<<< %s\n", line) >>STDERR; printf ("%s\r\n", line) |& ftpd; fflush(); return (0); } function cfputc(ftpd, cmd, arg, result, k, rc, line, fatal) { if (cmd != "") { line = cmd; if (arg != "") line = cmd " " arg; cfputs(ftpd, line); } line = cfgets(ftpd); if ((k = index(line, " ")) == 0) rc = line; else rc = substr(line, 1, k-1); fatal = 1; if (result < 0) { result = -result; fatal = 0; } rc = toupper(rc); if (result != "" && rc != result) { printf ("%s: protocol error: %s\n", program, line) >>STDERR; if (fatal == 1) exit (1); } return (line); } function doport(ftpd, options, local, x, high, low) { # # Yes, this is difficult. We open a second connect, this time # in FTP-DATA mode. This returns information about the local # interface and port which is used to construct the FTP PORT # command. # portcmd = sprintf ("connect -a %s: %s", interface, options); portcmd |& getline local; split(local, x, ":"); # # Compute the PORT command's parameter ... # high = int(x[2] / 256); low = x[2] - high * 256; local = x[1] "," high "," low; gsub(/\./, ",", local); # # ... and send it to the server. # cfputc(ftpd, "PORT", local, 200); # # Return the portcmd for the data transmission. # return (portcmd); } function dolist(ftpd, cmd, dir) { cfputc(ftpd, "TYPE", "A", 200); # # Allocate a local port for the data transmission, send the # data to the server (done by doport()). # portcmd = doport(ftpd, ""); # # Send the LIST command, wait for the server to connect and # read the peer information from portcmd. # cfputc(ftpd, (cmd != "NLST")? "LIST": "NLST", dir, 150); portcmd |& getline line; # # Read directory listing till the end of file ... # while (portcmd |& getline line) { noctrl(line); printf ("%s\n", line); } # # ... close our data transmission channel and read the final # `transfer complete' message from the server. # close (portcmd); cfputc(ftpd, "", "", 226); return (0); } function doretr(ftpd, mode, localname, remotename) { if (remotename == "") remotename = localname; # # Retriving files from the server is basically the same as # we do in dolist(). We have however to deal with binary files. # cfputc(ftpd, "TYPE", mode, 200); portcmd = doport(ftpd, mode == "A"? "": "-w " localname); cfputc(ftpd, "RETR", remotename, 150); portcmd |& getline line; if (mode != "A") { # # If we are doing a binary transfer our data channel # connect does all the work. We read a line from connect # which will never appear. But if we receive the EOF # we know that connect has finished. # portcmd |& getline line; } else { # # ASCII transfers are read line by line and written # manually to the local file. But we could process text # files the same way as we handle binary files. # while (portcmd |& getline line) { noctrl(line); printf ("%s\n", line) >localfile; } close (localfile); } close (portcmd); cfputc(ftpd, "", "", 226); return (0); } function dostor(ftpd, mode, localname, remotename) { if (remotename == "") remotename = localname; # # We store files in a different way. We handle everything # a binary. # cfputc(ftpd, "TYPE", mode, 200); portcmd = doport(ftpd, "-r " localname); cfputc(ftpd, "STOR", remotename, 150); portcmd |& getline line; # # Wait for connect to finish. # portcmd |& getline line; close (portcmd); cfputc(ftpd, "", "", 226); return (0); } function dorename(ftpd, oldname, newname) { cfputc(ftpd, "RNFR", oldname, 350); cfputc(ftpd, "RNTO", newname, 250); return (0); } function dodele(ftpd, remotename) { cfputc(ftpd, "DELE", remotename, 250); return (0); } function domkd(ftpd, dir) { cfputc(ftpd, "MKD", dir, 257); return (0); } function dormd(ftpd, dir) { cfputc(ftpd, "RMD", dir, 250); return (0); } function docwd(ftpd, dir) { cfputc(ftpd, "CWD", dir, 250); return (0); } function docmd(ftpd, cmd) { $0 = cmd; cmd = toupper($1); if (cmd == "CWD") docwd(ftpd, $2); else if (cmd == "LIST" || cmd == "NLST") dolist(ftpd, cmd, $2); else if (cmd == "RETR") doretr(ftpd, "I", $2, $3); else if (cmd == "STOR") dostor(ftpd, "I", $2, $3); else if (cmd == "DELE") dodele(ftpd, $2); else if (cmd == "MKD") domkd(ftpd, $2); else if (cmd == "RMD") domkd(ftpd, $2); else { printf ("%s: unknown command: %s\n", program, cmd); exit (1); } return (0); } function nextarg(par, arg) { if (argi >= ARGC) { printf ("%s: missing argument: %s\n", program, par) >STDERR; exit (1); } arg = ARGV[argi]; ARGV[argi++] = ""; return (arg); } BEGIN { STDERR = "/dev/stderr"; program = "ftpclient"; debug = 0; username = "ftp"; password = "root@"; argi = 1; while (argi < ARGC && substr(ARGV[argi], 1, 1) == "-") { options = nextarg("option"); if (options == "--") break; for (i = 2; i<=length(options); i++) { c = substr(options, i, 1); if (c == "d") debug = 1; else if (c == "l") { split(nextarg("login"), x, ":"); username = x[1]; password = x[2]; } else { printf ("%s: unkown option: -%s\n", program, c) >STDERR; exit (1); } } } ftpserver = nextarg("ftp server"); if (index(ftpserver, ":") == 0) ftpserver = ftpserver ":21"; # # Connect to the FTP server, read the local interface information, # ignore peer information. # server = sprintf ("connect -x %s", ftpserver); interface = cfgets(server); sub(/:.*$/, "", interface); if (debug != 0) printf ("local interface is %s\n", interface); cfgets(server); # peer information. # # Read the server greeting ... # cfputc(server, "", "", 220); # # ... and login. # cfputc(server, "USER", username, 331); cfputc(server, "PASS", password, 230); if (argi < ARGC) { line = nextarg("FTP command"); while (argi < ARGC) line = line " " nextarg("FTP parameter"); docmd(server, line); } else { while (getline line > 0) docmd(server, line); } cfputc(server, "QUIT", "", 221); exit (0); }