ftp.tcl

Go to the documentation of this file.
00001 /*  ftp.tcl --*/
00002 /* */
00003 /*  FTP library package for Tcl 8.2+.  Originally written by Steffen*/
00004 /*  Traeger (Steffen.Traeger@t-online.de); modified by Peter MacDonald*/
00005 /*  (peter@pdqi.com) to support multiple simultaneous FTP sessions;*/
00006 /*  Modified by Steve Ball (Steve.Ball@zveno.com) to support*/
00007 /*  asynchronous operation.*/
00008 /* */
00009 /*  Copyright (c) 1996-1999 by Steffen Traeger <Steffen.Traeger@t-online.de>*/
00010 /*  Copyright (c) 2000 by Ajuba Solutions*/
00011 /*  Copyright (c) 2000 by Zveno Pty Ltd*/
00012 /* */
00013 /*  See the file "license.terms" for information on usage and redistribution*/
00014 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00015 /*  */
00016 /*  RCS: @(#) $Id: ftp.tcl,v 1.46 2007/05/07 21:15:19 andreas_kupries Exp $*/
00017 /* */
00018 /*    core ftp support:     ftp::Open <server> <user> <passwd> <?options?>*/
00019 /*          ftp::Close <s>*/
00020 /*          ftp::Cd <s> <directory>*/
00021 /*          ftp::Pwd <s>*/
00022 /*          ftp::Type <s> <?ascii|binary|tenex?>    */
00023 /*          ftp::List <s> <?directory?>*/
00024 /*          ftp::NList <s> <?directory?>*/
00025 /*          ftp::FileSize <s> <file>*/
00026 /*          ftp::ModTime <s> <file> <?newtime?>*/
00027 /*          ftp::Delete <s> <file>*/
00028 /*          ftp::Rename <s> <from> <to>*/
00029 /*          ftp::Put <s> <(local | -data "data" -channel chan)> <?remote?>*/
00030 /*          ftp::Append <s> <(local | -data "data" | -channel chan)> <?remote?>*/
00031 /*          ftp::Get <s> <remote> <?(local | -variable varname | -channel chan)?>*/
00032 /*          ftp::Reget <s> <remote> <?local?>*/
00033 /*          ftp::Newer <s> <remote> <?local?>*/
00034 /*          ftp::MkDir <s> <directory>*/
00035 /*          ftp::RmDir <s> <directory>*/
00036 /*          ftp::Quote <s> <arg1> <arg2> ...*/
00037 /* */
00038 /*  Internal documentation. Contents of a session state array.*/
00039 /* */
00040 /*  ---------------------------------------------*/
00041 /*  key             value*/
00042 /*  ---------------------------------------------*/
00043 /*  State           Current state of the session and the currently executing command.*/
00044 /*  RemoteFileName  Name of the remote file, for put/get*/
00045 /*  LocalFileName   Name of local file, for put/get*/
00046 /*  inline          1 - Put/Get is inline (from data, to variable)*/
00047 /*  filebuffer  */
00048 /*  PutData         Data to move when inline*/
00049 /*  SourceCI        Channel to read from, "Put"*/
00050 /*  ---------------------------------------------*/
00051 /* */
00052 
00053 package require Tcl 8.2
00054 package require log     ; /*  tcllib/log, general logging facility.*/
00055 
00056 namespace ::ftp {
00057     namespace export DisplayMsg Open Close Cd Pwd Type List NList \
00058         FileSize ModTime Delete Rename Put Append Get Reget \
00059         Newer Quote MkDir RmDir
00060 
00061      serial =  0
00062      VERBOSE =  0
00063      DEBUG =  0
00064 }
00065 
00066 /* */
00067 /* */
00068 /*  DisplayMsg --*/
00069 /* */
00070 /*  This is a simple procedure to display any messages on screen.*/
00071 /*  Can be intercepted by the -output option to Open*/
00072 /* */
00073 /*  namespace ftp {*/
00074 /*      proc DisplayMsg {msg} {*/
00075 /*          ......*/
00076 /*      }*/
00077 /*  }*/
00078 /* */
00079 /*  Arguments:*/
00080 /*  msg -       message string*/
00081 /*  state -     different states {normal, data, control, error}*/
00082 /* */
00083 ret  ::ftp::DisplayMsg (type s , type msg , optional state ="") {
00084 
00085     upvar ::ftp::ftp$s ftp
00086 
00087     if { ([info exists ftp(Output)]) && ($ftp(Output) != "") } {
00088         eval [concat $ftp(Output) {$s $msg $state}]
00089         return
00090     }
00091 
00092     # FIX #476729. Instead of changing the documentation this
00093     #              procedure is changed to enforce the documented
00094     #              behaviour. IOW, this procedure will not throw
00095     #              errors anymore. At the same time printing to stdout
00096     #              is exchanged against calls into the 'log' module
00097     #              tcllib, which is much easier to customize for the
00098     #              needs of any application using the ftp module. The
00099     #              variable VERBOSE is still relevant as it controls
00100     #              whether this procedure is called or not.
00101 
00102     global errorInfo
00103     switch -exact -- $state {
00104         data    {log::log debug "$state | $msg"}
00105         control {log::log debug "$state | $msg"}
00106         error   {log::log error "$state | E: $msg:\n$errorInfo"}
00107         default {log::log debug "$state | $msg"}
00108     }
00109     return
00110 }
00111 
00112 /* */
00113 /* */
00114 /*  Timeout --*/
00115 /* */
00116 /*  Handle timeouts*/
00117 /*  */
00118 /*  Arguments:*/
00119 /*   -*/
00120 /* */
00121 ret  ::ftp::Timeout (type s) {
00122     upvar ::ftp::ftp$s ftp
00123 
00124     after cancel $ftp(Wait)
00125     set ftp(state.control) 1
00126 
00127     DisplayMsg "" "Timeout of control connection after $ftp(Timeout) sec.!" error
00128     Command $ftp(Command) timeout
00129     return
00130 }
00131 
00132 /* */
00133 /* */
00134 /*  WaitOrTimeout --*/
00135 /* */
00136 /*  Blocks the running procedure and waits for a variable of the transaction */
00137 /*  to complete. It continues processing procedure until a procedure or */
00138 /*  StateHandler sets the value of variable "finished". */
00139 /*  If a connection hangs the variable is setting instead of by this procedure after */
00140 /*  specified seconds in $ftp(Timeout).*/
00141 /*   */
00142 /*  */
00143 /*  Arguments:*/
00144 /*   -      */
00145 /* */
00146 
00147 ret  ::ftp::WaitOrTimeout (type s) {
00148     upvar ::ftp::ftp$s ftp
00149 
00150     set retvar 1
00151 
00152     if { ![string length $ftp(Command)] && [info exists ftp(state.control)] } {
00153 
00154         set ftp(Wait) [after [expr {$ftp(Timeout) * 1000}] [list [namespace current]::Timeout $s]]
00155 
00156         vwait ::ftp::ftp${s}(state.control)
00157         set retvar $ftp(state.control)
00158     }
00159 
00160     if {$ftp(Error) != ""} {
00161         set errmsg $ftp(Error)
00162         set ftp(Error) ""
00163         DisplayMsg $s $errmsg error
00164     }
00165 
00166     return $retvar
00167 }
00168 
00169 /* */
00170 /* */
00171 /*  WaitComplete --*/
00172 /* */
00173 /*  Transaction completed.*/
00174 /*  Cancel execution of the delayed command declared in procedure WaitOrTimeout.*/
00175 /*  */
00176 /*  Arguments:*/
00177 /*  value - result of the transaction*/
00178 /*          0 ... Error*/
00179 /*          1 ... OK*/
00180 /* */
00181 
00182 ret  ::ftp::WaitComplete (type s , type value) {
00183     upvar ::ftp::ftp$s ftp
00184 
00185     if {![info exists ftp(Command)]} {
00186     set ftp(state.control) $value
00187     return $value
00188     }
00189     if { ![string length $ftp(Command)] && [info exists ftp(state.data)] } {
00190         vwait ::ftp::ftp${s}(state.data)
00191     }
00192 
00193     catch {after cancel $ftp(Wait)}
00194     set ftp(state.control) $value
00195     return $ftp(state.control)
00196 }
00197 
00198 /* */
00199 /* */
00200 /*  PutsCtrlSocket --*/
00201 /* */
00202 /*  Puts then specified command to control socket,*/
00203 /*  if DEBUG is set than it logs via DisplayMsg*/
00204 /*  */
00205 /*  Arguments:*/
00206 /*  command -       ftp command*/
00207 /* */
00208 
00209 ret  ::ftp::PutsCtrlSock (type s , optional command ="") {
00210     upvar ::ftp::ftp$s ftp
00211     variable DEBUG
00212     
00213     if { $DEBUG } {
00214         DisplayMsg $s "---> $command"
00215     }
00216 
00217     puts $ftp(CtrlSock) $command
00218     flush $ftp(CtrlSock)
00219     return
00220 }
00221 
00222 /* */
00223 /* */
00224 /*  StateHandler --*/
00225 /* */
00226 /*  Implements a finite state handler and a fileevent handler*/
00227 /*  for the control channel*/
00228 /*  */
00229 /*  Arguments:*/
00230 /*  sock -      socket name*/
00231 /*          If called from a procedure than this argument is empty.*/
00232 /*              If called from a fileevent than this argument contains*/
00233 /*          the socket channel identifier.*/
00234 
00235 ret  ::ftp::StateHandler (type s , optional sock ="") {
00236     upvar ::ftp::ftp$s ftp
00237     variable DEBUG 
00238     variable VERBOSE
00239 
00240     # disable fileevent on control socket, enable it at the and of the state machine
00241     # fileevent $ftp(CtrlSock) readable {}
00242         
00243     # there is no socket (and no channel to get) if called from a procedure
00244 
00245     set rc "   "
00246     set msgtext {}
00247 
00248     if { $sock != "" } {
00249 
00250         set number 0                            ;# Error condition
00251         catch {set number [gets $sock bufline]}
00252 
00253         if { $number > 0 } {
00254 
00255             # get return code, check for multi-line text
00256             
00257             if {![regexp -- "^-?(\[0-9\]+)( |-)?(.*)$" $bufline all rc multi_line msgtext]} {
00258         set errmsg "C: Internal Error @ line 255.\
00259             Regex pattern not matching the input \"$bufline\""
00260         if {$VERBOSE} {
00261             DisplayMsg $s $errmsg control
00262         }
00263         } else {
00264         # multi-line format detected ("-"), get all the lines
00265         # until the real return code
00266 
00267         set buffer $bufline
00268             
00269         while { [string equal $multi_line "-"] } {
00270             set number [gets $sock bufline] 
00271             if { $number > 0 } {
00272             append buffer \n "$bufline"
00273             regexp -- "(^\[0-9\]+)( |-)?(.*)$" $bufline all rc multi_line
00274             # multi_line is not set if the bufline does not match the regexp,
00275             # I.e. this keeps the '-' which started this around until the
00276             # closing line does match and sets it to space.
00277             }
00278         }
00279 
00280         # Export the accumulated response. [Bug 1191607].
00281         set msgtext $buffer
00282         }
00283         } elseif { [eof $ftp(CtrlSock)] } {
00284             # remote server has closed control connection. kill
00285             # control socket, unset State to disable all following
00286             # commands. Killing the socket is done before
00287             # 'WaitComplete' to prevent it from recursively entering
00288             # this code, overflowing the stack (socket still existing,
00289             # still readable, still eof). [SF Tcllib Bug 15822535].
00290             
00291             set rc 421
00292             catch {close $ftp(CtrlSock)}
00293             catch {unset  ftp(CtrlSock)}
00294             catch {unset  ftp(state.data)}
00295             if { $VERBOSE } {
00296                 DisplayMsg $s "C: 421 Service not available, closing control connection." control
00297             }
00298             if {![string equal $ftp(State) "quit_sent"]} {
00299         set ftp(Error) "Service not available!"
00300         }
00301             CloseDataConn $s
00302             WaitComplete $s 0
00303         Command $ftp(Command) terminated
00304             catch {unset ftp(State)}
00305             return
00306         } else {
00307         # Fix SF bug #466746: Incomplete line, do nothing.
00308         return     
00309     }
00310     } 
00311     
00312     if { $DEBUG } {
00313         DisplayMsg $s "-> rc=\"$rc\"\n-> msgtext=\"$msgtext\"\n-> state=\"$ftp(State)\""
00314     }
00315 
00316     # In asynchronous mode, should we move on to the next state?
00317     set nextState 0
00318     
00319     # system status replay
00320     if { [string equal $rc "211"] } {
00321         return
00322     }
00323 
00324     # use only the first digit 
00325     regexp -- "^\[0-9\]?" $rc rc
00326     
00327     switch -exact -- $ftp(State) {
00328         user { 
00329             switch -exact -- $rc {
00330                 2 {
00331                     PutsCtrlSock $s "USER $ftp(User)"
00332                     set ftp(State) passwd
00333             Command $ftp(Command) user
00334                 }
00335                 default {
00336                     set errmsg "Error connecting! $msgtext"
00337                     set complete_with 0
00338             Command $ftp(Command) error $errmsg
00339                 }
00340             }
00341         }
00342         passwd {
00343             switch -exact -- $rc {
00344                 2 {
00345                     set complete_with 1
00346             Command $ftp(Command) password
00347                 }
00348                 3 {
00349                     PutsCtrlSock $s "PASS $ftp(Passwd)"
00350                     set ftp(State) connect
00351             Command $ftp(Command) password
00352                 }
00353                 default {
00354                     set errmsg "Error connecting! $msgtext"
00355                     set complete_with 0
00356             Command $ftp(Command) error $msgtext
00357                 }
00358             }
00359         }
00360         connect {
00361             switch -exact -- $rc {
00362                 2 {
00363             # The type is set after this, and we want to report
00364             # that the connection is complete once the type is done
00365             set nextState 1
00366             if {[info exists ftp(NextState)] && ![llength $ftp(NextState)]} {
00367             Command $ftp(Command) connect $s
00368             } else {
00369             set complete_with 1
00370             }
00371                 }
00372                 default {
00373                     set errmsg "Error connecting! $msgtext"
00374                     set complete_with 0
00375             Command $ftp(Command) error $msgtext
00376                 }
00377             }
00378         }   
00379     connect_last {
00380         Command $ftp(Command) connect $s
00381         set complete_with 1
00382     }
00383         quit {
00384             PutsCtrlSock $s "QUIT"
00385             set ftp(State) quit_sent
00386         }
00387         quit_sent {
00388             switch -exact -- $rc {
00389                 2 {
00390                     set complete_with 1
00391             set nextState 1
00392             Command $ftp(Command) quit
00393                 }
00394                 default {
00395                     set errmsg "Error disconnecting! $msgtext"
00396                     set complete_with 0
00397             Command $ftp(Command) error $msgtext
00398                 }
00399             }
00400         }
00401         quote {
00402             PutsCtrlSock $s $ftp(Cmd)
00403             set ftp(State) quote_sent
00404         }
00405         quote_sent {
00406             set complete_with 1
00407             set ftp(Quote) $buffer
00408         set nextState 1
00409         Command $ftp(Command) quote $buffer
00410         }
00411         type {
00412             if { [string equal $ftp(Type) "ascii"] } {
00413                 PutsCtrlSock $s "TYPE A"
00414             } elseif { [string equal $ftp(Type) "binary"] } {
00415                 PutsCtrlSock $s "TYPE I"
00416             } else {
00417                 PutsCtrlSock $s "TYPE L"
00418             }
00419             set ftp(State) type_sent
00420         }
00421         type_sent {
00422             switch -exact -- $rc {
00423                 2 {
00424                     set complete_with 1
00425             set nextState 1
00426             Command $ftp(Command) type $ftp(Type)
00427                 }
00428                 default {
00429                     set errmsg "Error setting type \"$ftp(Type)\"!"
00430                     set complete_with 0
00431             Command $ftp(Command) error "error setting type \"$ftp(Type)\""
00432                 }
00433             }
00434         }
00435     type_change {
00436         set ftp(Type) $ftp(type:changeto)
00437         set ftp(State) type
00438         StateHandler $s
00439     }
00440         nlist_active {
00441             if { [OpenActiveConn $s] } {
00442                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00443                 set ftp(State) nlist_open
00444             } else {
00445                 set errmsg "Error setting port!"
00446             }
00447         }
00448         nlist_passive {
00449             PutsCtrlSock $s "PASV"
00450             set ftp(State) nlist_open
00451         }
00452         nlist_open {
00453             switch -exact -- $rc {
00454                 1 {}
00455         2 {
00456                     if { [string equal $ftp(Mode) "passive"] } {
00457                         if { ![OpenPassiveConn $s $buffer] } {
00458                             set errmsg "Error setting PASSIVE mode!"
00459                             set complete_with 0
00460                 Command $ftp(Command) error "error setting passive mode"
00461                         }
00462                     }   
00463                     PutsCtrlSock $s "NLST$ftp(Dir)"
00464                     set ftp(State) list_sent
00465                 }
00466                 default {
00467                     if { [string equal $ftp(Mode) "passive"] } {
00468                         set errmsg "Error setting PASSIVE mode!"
00469                     } else {
00470                         set errmsg "Error setting port!"
00471                     }  
00472                     set complete_with 0
00473             Command $ftp(Command) error $errmsg
00474                 }
00475             }
00476         }
00477         list_active {
00478             if { [OpenActiveConn $s] } {
00479                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00480                 set ftp(State) list_open
00481             } else {
00482                 set errmsg "Error setting port!"
00483         Command $ftp(Command) error $errmsg
00484             }
00485         }
00486         list_passive {
00487             PutsCtrlSock $s "PASV"
00488             set ftp(State) list_open
00489         }
00490         list_open {
00491             switch -exact -- $rc {
00492                 1 {}
00493         2 {
00494                     if { [string equal $ftp(Mode) "passive"] } {
00495                         if { ![OpenPassiveConn $s $buffer] } {
00496                             set errmsg "Error setting PASSIVE mode!"
00497                             set complete_with 0
00498                 Command $ftp(Command) error $errmsg
00499                         }
00500                     }   
00501                     PutsCtrlSock $s "LIST$ftp(Dir)"
00502                     set ftp(State) list_sent
00503                 }
00504                 default {
00505                     if { [string equal $ftp(Mode) "passive"] } {
00506                         set errmsg "Error setting PASSIVE mode!"
00507                     } else {
00508                         set errmsg "Error setting port!"
00509                     }  
00510                     set complete_with 0
00511             Command $ftp(Command) error $errmsg
00512                 }
00513             }
00514         }
00515         list_sent {
00516             switch -exact -- $rc {
00517                 1 -
00518         2 {
00519                     set ftp(State) list_close
00520                 }
00521                 default {  
00522                     if { [string equal $ftp(Mode) "passive"] } {
00523                         catch {unset ftp(state.data)}
00524                     }    
00525                     set errmsg "Error getting directory listing!"
00526                     set complete_with 0
00527             Command $ftp(Command) error $errmsg
00528                 }
00529             }
00530         }
00531         list_close {
00532             switch -exact -- $rc {
00533                 1 {}
00534         2 {
00535             set nextState 1
00536             if {[info exists ftp(NextState)] && ![llength $ftp(NextState)]} {
00537             Command $ftp(Command) list [ListPostProcess $ftp(List)]
00538             } else {
00539             set complete_with 1
00540             }
00541                 }
00542                 default {
00543                     set errmsg "Error receiving list!"
00544                     set complete_with 0
00545             Command $ftp(Command) error $errmsg
00546                 }
00547             }
00548         }
00549     list_last {
00550         Command $ftp(Command) list [ListPostProcess $ftp(List)]
00551         set complete_with 1
00552     }
00553         size {
00554             PutsCtrlSock $s "SIZE $ftp(File)"
00555             set ftp(State) size_sent
00556         }
00557         size_sent {
00558             switch -exact -- $rc {
00559                 2 {
00560                     regexp -- "^\[0-9\]+ (.*)$" $buffer all ftp(FileSize)
00561                     set complete_with 1
00562             set nextState 1
00563             Command $ftp(Command) size $ftp(File) $ftp(FileSize)
00564                 }
00565                 default {
00566                     set errmsg "Error getting file size!"
00567                     set complete_with 0
00568             Command $ftp(Command) error $errmsg
00569                 }
00570             }
00571         } 
00572         modtime {
00573             if {$ftp(DateTime) != ""} {
00574               PutsCtrlSock $s "MDTM $ftp(DateTime) $ftp(File)"
00575             } else { ;# No DateTime Specified
00576               PutsCtrlSock $s "MDTM $ftp(File)"
00577             }
00578             set ftp(State) modtime_sent
00579         }  
00580         modtime_sent {
00581             switch -exact -- $rc {
00582                 2 {
00583                     regexp -- "^\[0-9\]+ (.*)$" $buffer all ftp(DateTime)
00584                     set complete_with 1
00585             set nextState 1
00586             Command $ftp(Command) modtime $ftp(File) [ModTimePostProcess $ftp(DateTime)]
00587                 }
00588                 default {
00589                     if {$ftp(DateTime) != ""} {
00590                       set errmsg "Error setting modification time! No server MDTM support?"
00591                     } else {
00592                       set errmsg "Error getting modification time!"
00593                     }
00594                     set complete_with 0
00595             Command $ftp(Command) error $errmsg
00596                 }
00597             }
00598         } 
00599         pwd {
00600             PutsCtrlSock $s "PWD"
00601             set ftp(State) pwd_sent
00602         }
00603         pwd_sent {
00604             switch -exact -- $rc {
00605                 2 {
00606                     regexp -- "^.*\"(.*)\"" $buffer temp ftp(Dir)
00607                     set complete_with 1
00608             set nextState 1
00609             Command $ftp(Command) pwd $ftp(Dir)
00610                 }
00611                 default {
00612                     set errmsg "Error getting working dir!"
00613                     set complete_with 0
00614             Command $ftp(Command) error $errmsg
00615                 }
00616             }
00617         }
00618         cd {
00619             PutsCtrlSock $s "CWD$ftp(Dir)"
00620             set ftp(State) cd_sent
00621         }
00622         cd_sent {
00623             switch -exact -- $rc {
00624                 1 {}
00625         2 {
00626                     set complete_with 1
00627             set nextState 1
00628             Command $ftp(Command) cd $ftp(Dir)
00629                 }
00630                 default {
00631                     set errmsg "Error changing directory to \"$ftp(Dir)\""
00632                     set complete_with 0
00633             Command $ftp(Command) error $errmsg
00634                 }
00635             }
00636         }
00637         mkdir {
00638             PutsCtrlSock $s "MKD $ftp(Dir)"
00639             set ftp(State) mkdir_sent
00640         }
00641         mkdir_sent {
00642             switch -exact -- $rc {
00643                 2 {
00644                     set complete_with 1
00645             set nextState 1
00646             Command $ftp(Command) mkdir $ftp(Dir)
00647                 }
00648                 default {
00649                     set errmsg "Error making dir \"$ftp(Dir)\"!"
00650                     set complete_with 0
00651             Command $ftp(Command) error $errmsg
00652                 }
00653             }
00654         }
00655         rmdir {
00656             PutsCtrlSock $s "RMD $ftp(Dir)"
00657             set ftp(State) rmdir_sent
00658         }
00659         rmdir_sent {
00660             switch -exact -- $rc {
00661                 2 {
00662                     set complete_with 1
00663             set nextState 1
00664             Command $ftp(Command) rmdir $ftp(Dir)
00665                 }
00666                 default {
00667                     set errmsg "Error removing directory!"
00668                     set complete_with 0
00669             Command $ftp(Command) error $errmsg
00670                 }
00671             }
00672         }
00673         delete {
00674             PutsCtrlSock $s "DELE $ftp(File)"
00675             set ftp(State) delete_sent
00676         }
00677         delete_sent {
00678             switch -exact -- $rc {
00679                 2 {
00680                     set complete_with 1
00681             set nextState 1
00682             Command $ftp(Command) delete $ftp(File)
00683                 }
00684                 default {
00685                     set errmsg "Error deleting file \"$ftp(File)\"!"
00686                     set complete_with 0
00687             Command $ftp(Command) error $errmsg
00688                 }
00689             }
00690         }
00691         rename {
00692             PutsCtrlSock $s "RNFR $ftp(RenameFrom)"
00693             set ftp(State) rename_to
00694         }
00695         rename_to {
00696             switch -exact -- $rc {
00697                 3 {
00698                     PutsCtrlSock $s "RNTO $ftp(RenameTo)"
00699                     set ftp(State) rename_sent
00700                 }
00701                 default {
00702                     set errmsg "Error renaming file \"$ftp(RenameFrom)\"!"
00703                     set complete_with 0
00704             Command $ftp(Command) error $errmsg
00705                 }
00706             }
00707         }
00708         rename_sent {
00709             switch -exact -- $rc {
00710                 2 {
00711                     set complete_with 1
00712             set nextState 1
00713             Command $ftp(Command) rename $ftp(RenameFrom) $ftp(RenameTo)
00714                 }
00715                 default {
00716                     set errmsg "Error renaming file \"$ftp(RenameFrom)\"!"
00717                     set complete_with 0
00718             Command $ftp(Command) error $errmsg
00719                 }
00720             }
00721         }
00722         put_active {
00723             if { [OpenActiveConn $s] } {
00724                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00725                 set ftp(State) put_open
00726             } else {
00727                 set errmsg "Error setting port!"
00728         Command $ftp(Command) error $errmsg
00729             }
00730         }
00731         put_passive {
00732             PutsCtrlSock $s "PASV"
00733             set ftp(State) put_open
00734         }
00735         put_open {
00736             switch -exact -- $rc {
00737                 1 -
00738         2 {
00739                     if { [string equal $ftp(Mode) "passive"] } {
00740                         if { ![OpenPassiveConn $s $buffer] } {
00741                             set errmsg "Error setting PASSIVE mode!"
00742                             set complete_with 0
00743                 Command $ftp(Command) error $errmsg
00744                         }
00745                     } 
00746                     PutsCtrlSock $s "STOR $ftp(RemoteFilename)"
00747                     set ftp(State) put_sent
00748                 }
00749                 default {
00750                     if { [string equal $ftp(Mode) "passive"] } {
00751                         set errmsg "Error setting PASSIVE mode!"
00752                     } else {
00753                         set errmsg "Error setting port!"
00754                     }  
00755                     set complete_with 0
00756             Command $ftp(Command) error $errmsg
00757                 }
00758             }
00759         }
00760         put_sent {
00761             switch -exact -- $rc {
00762                 1 -
00763         2 {
00764                     set ftp(State) put_close
00765                 }
00766                 default {
00767                     if { [string equal $ftp(Mode) "passive"] } {
00768                         # close already opened DataConnection
00769                         catch {unset ftp(state.data)}
00770                     }  
00771                     set errmsg "Error opening connection!"
00772                     set complete_with 0
00773             Command $ftp(Command) error $errmsg
00774                 }
00775             }
00776         }
00777         put_close {
00778             switch -exact -- $rc {
00779         1 {
00780             # Keep going
00781             return
00782         }
00783                 2 {
00784                     set complete_with 1
00785             set nextState 1
00786             Command $ftp(Command) put $ftp(RemoteFilename)
00787                 }
00788                 default {
00789             DisplayMsg $s "rc = $rc msgtext = \"$msgtext\""
00790                     set errmsg "Error storing file \"$ftp(RemoteFilename)\" due to \"$msgtext\""
00791                     set complete_with 0
00792             Command $ftp(Command) error $errmsg
00793                 }
00794             }
00795         }
00796         append_active {
00797             if { [OpenActiveConn $s] } {
00798                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00799                 set ftp(State) append_open
00800             } else {
00801                 set errmsg "Error setting port!"
00802         Command $ftp(Command) error $errmsg
00803             }
00804         }
00805         append_passive {
00806             PutsCtrlSock $s "PASV"
00807             set ftp(State) append_open
00808         }
00809         append_open {
00810             switch -exact -- $rc {
00811         1 -
00812                 2 {
00813                     if { [string equal $ftp(Mode) "passive"] } {
00814                         if { ![OpenPassiveConn $s $buffer] } {
00815                             set errmsg "Error setting PASSIVE mode!"
00816                             set complete_with 0
00817                 Command $ftp(Command) error $errmsg
00818                         }
00819                     }   
00820                     PutsCtrlSock $s "APPE $ftp(RemoteFilename)"
00821                     set ftp(State) append_sent
00822                 }
00823                 default {
00824                     if { [string equal $ftp(Mode) "passive"] } {
00825                         set errmsg "Error setting PASSIVE mode!"
00826                     } else {
00827                         set errmsg "Error setting port!"
00828                     }  
00829                     set complete_with 0
00830             Command $ftp(Command) error $errmsg
00831                 }
00832             }
00833         }
00834         append_sent {
00835             switch -exact -- $rc {
00836                 1 {
00837                     set ftp(State) append_close
00838                 }
00839                 default {
00840                     if { [string equal $ftp(Mode) "passive"] } {
00841                         # close already opened DataConnection
00842                         catch {unset ftp(state.data)}
00843                     }  
00844                     set errmsg "Error opening connection!"
00845                     set complete_with 0
00846             Command $ftp(Command) error $errmsg
00847                 }
00848             }
00849         }
00850         append_close {
00851             switch -exact -- $rc {
00852                 2 {
00853                     set complete_with 1
00854             set nextState 1
00855             Command $ftp(Command) append $ftp(RemoteFilename)
00856                 }
00857                 default {
00858                     set errmsg "Error storing file \"$ftp(RemoteFilename)\"!"
00859                     set complete_with 0
00860             Command $ftp(Command) error $errmsg
00861                 }
00862             }
00863         }
00864         reget_active {
00865             if { [OpenActiveConn $s] } {
00866                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00867                 set ftp(State) reget_restart
00868             } else {
00869                 set errmsg "Error setting port!"
00870         Command $ftp(Command) error $errmsg
00871             }
00872         }
00873         reget_passive {
00874             PutsCtrlSock $s "PASV"
00875             set ftp(State) reget_restart
00876         }
00877         reget_restart {
00878             switch -exact -- $rc {
00879                 2 { 
00880                     if { [string equal $ftp(Mode) "passive"] } {
00881                         if { ![OpenPassiveConn $s $buffer] } {
00882                             set errmsg "Error setting PASSIVE mode!"
00883                             set complete_with 0
00884                 Command $ftp(Command) error $errmsg
00885                         }
00886                     }   
00887                     if { $ftp(FileSize) != 0 } {
00888                         PutsCtrlSock $s "REST $ftp(FileSize)"
00889                         set ftp(State) reget_open
00890                     } else {
00891                         PutsCtrlSock $s "RETR $ftp(RemoteFilename)"
00892                         set ftp(State) reget_sent
00893                     } 
00894                 }
00895                 default {
00896                     set errmsg "Error restarting filetransfer of \"$ftp(RemoteFilename)\"!"
00897                     set complete_with 0
00898             Command $ftp(Command) error $errmsg
00899                 }
00900             }
00901         }
00902         reget_open {
00903             switch -exact -- $rc {
00904                 2 -
00905                 3 {
00906                     PutsCtrlSock $s "RETR $ftp(RemoteFilename)"
00907                     set ftp(State) reget_sent
00908                 }
00909                 default {
00910                     if { [string equal $ftp(Mode) "passive"] } {
00911                         set errmsg "Error setting PASSIVE mode!"
00912                     } else {
00913                         set errmsg "Error setting port!"
00914                     }  
00915                     set complete_with 0
00916             Command $ftp(Command) error $errmsg
00917                 }
00918             }
00919         }
00920         reget_sent {
00921             switch -exact -- $rc {
00922                 1 {
00923                     set ftp(State) reget_close
00924                 }
00925                 default {
00926                     if { [string equal $ftp(Mode) "passive"] } {
00927                         # close already opened DataConnection
00928                         catch {unset ftp(state.data)}
00929                     }  
00930                     set errmsg "Error retrieving file \"$ftp(RemoteFilename)\"!"
00931                     set complete_with 0
00932             Command $ftp(Command) error $errmsg
00933                 }
00934             }
00935         }
00936         reget_close {
00937             switch -exact -- $rc {
00938                 2 {
00939                     set complete_with 1
00940             set nextState 1
00941             Command $ftp(Command) get $ftp(RemoteFilename):$ftp(From):$ftp(To)
00942             unset ftp(From) ftp(To)
00943                 }
00944                 default {
00945                     set errmsg "Error retrieving file \"$ftp(RemoteFilename)\"!"
00946                     set complete_with 0
00947             Command $ftp(Command) error $errmsg
00948                 }
00949             }
00950         }
00951         get_active {
00952             if { [OpenActiveConn $s] } {
00953                 PutsCtrlSock $s "PORT $ftp(LocalAddr),$ftp(DataPort)"
00954                 set ftp(State) get_open
00955             } else {
00956                 set errmsg "Error setting port!"
00957         Command $ftp(Command) error $errmsg
00958             }
00959         } 
00960         get_passive {
00961             PutsCtrlSock $s "PASV"
00962             set ftp(State) get_open
00963         }
00964         get_open {
00965             switch -exact -- $rc {
00966                 1 -
00967         2 -
00968                 3 {
00969                     if { [string equal $ftp(Mode) "passive"] } {
00970                         if { ![OpenPassiveConn $s $buffer] } {
00971                             set errmsg "Error setting PASSIVE mode!"
00972                             set complete_with 0
00973                 Command $ftp(Command) error $errmsg
00974                         }
00975                     }   
00976                     PutsCtrlSock $s "RETR $ftp(RemoteFilename)"
00977                     set ftp(State) get_sent
00978                 }
00979                 default {
00980                     if { [string equal $ftp(Mode) "passive"] } {
00981                         set errmsg "Error setting PASSIVE mode!"
00982                     } else {
00983                         set errmsg "Error setting port!"
00984                     }  
00985                     set complete_with 0
00986             Command $ftp(Command) error $errmsg
00987                 }
00988             }
00989         }
00990         get_sent {
00991             switch -exact -- $rc {
00992                 1 {
00993                     set ftp(State) get_close
00994                 }
00995                 default {
00996                     if { [string equal $ftp(Mode) "passive"] } {
00997                         # close already opened DataConnection
00998                         catch {unset ftp(state.data)}
00999                     }  
01000                     set errmsg "Error retrieving file \"$ftp(RemoteFilename)\"!"
01001                     set complete_with 0
01002             Command $ftp(Command) error $errmsg
01003                 }
01004             }
01005         }
01006         get_close {
01007             switch -exact -- $rc {
01008                 2 {
01009                     set complete_with 1
01010             set nextState 1
01011             if {$ftp(inline)} {
01012             upvar #0 $ftp(get:varname) returnData
01013             set returnData $ftp(GetData)
01014             Command $ftp(Command) get $ftp(GetData)
01015             } else {
01016             Command $ftp(Command) get $ftp(RemoteFilename)
01017             }
01018                 }
01019                 default {
01020                     set errmsg "Error retrieving file \"$ftp(RemoteFilename)\"!"
01021                     set complete_with 0
01022             Command $ftp(Command) error $errmsg
01023                 }
01024             }
01025         }
01026     default {
01027         error "Unknown state \"$ftp(State)\""
01028     }
01029     }
01030 
01031     # finish waiting 
01032     if { [info exists complete_with] } {
01033         WaitComplete $s $complete_with
01034     }
01035 
01036     # display control channel message
01037     if { [info exists buffer] } {
01038         if { $VERBOSE } {
01039             foreach line [split $buffer \n] {
01040                 DisplayMsg $s "C: $line" control
01041             }
01042         }
01043     }
01044     
01045     # Rather than throwing an error in the event loop, set the ftp(Error)
01046     # variable to hold the message so that it can later be thrown after the
01047     # the StateHandler has completed.
01048 
01049     if { [info exists errmsg] } {
01050         set ftp(Error) $errmsg
01051     }
01052 
01053     # If operating asynchronously, commence next state
01054     if {$nextState && [info exists ftp(NextState)] && [llength $ftp(NextState)]} {
01055     # Pop the head of the NextState queue
01056     set ftp(State) [lindex $ftp(NextState) 0]
01057     set ftp(NextState) [lreplace $ftp(NextState) 0 0]
01058     StateHandler $s
01059     }
01060 
01061     # enable fileevent on control socket again
01062     #fileevent $ftp(CtrlSock) readable [list ::ftp::StateHandler $ftp(CtrlSock)]
01063 
01064 }
01065 
01066 /* */
01067 /* */
01068 /*  Type --*/
01069 /* */
01070 /*  REPRESENTATION TYPE - Sets the file transfer type to ascii or binary.*/
01071 /*  (exported)*/
01072 /* */
01073 /*  Arguments:*/
01074 /*  type -      specifies the representation type (ascii|binary)*/
01075 /*  */
01076 /*  Returns:*/
01077 /*  type    -       returns the current type or {} if an error occurs*/
01078 
01079 ret  ::ftp::Type (type s , optional type ="") {
01080     upvar ::ftp::ftp$s ftp
01081 
01082     if { ![info exists ftp(State)] } {
01083         if { ![string is digit -strict $s] } {
01084             DisplayMsg $s "Bad connection name \"$s\"" error
01085         } else {
01086             DisplayMsg $s "Not connected!" error
01087         }
01088         return {}
01089     }
01090 
01091     # return current type
01092     if { $type == "" } {
01093         return $ftp(Type)
01094     }
01095 
01096     # save current type
01097     set old_type $ftp(Type) 
01098     
01099     set ftp(Type) $type
01100     set ftp(State) type
01101     StateHandler $s
01102     
01103     # wait for synchronization
01104     set rc [WaitOrTimeout $s]
01105     if { $rc } {
01106         return $ftp(Type)
01107     } else {
01108         # restore old type
01109         set ftp(Type) $old_type
01110         return {}
01111     }
01112 }
01113 
01114 /* */
01115 /* */
01116 /*  NList --*/
01117 /* */
01118 /*  NAME LIST - This command causes a directory listing to be sent from*/
01119 /*  server to user site.*/
01120 /*  (exported)*/
01121 /*  */
01122 /*  Arguments:*/
01123 /*  dir -       The $dir should specify a directory or other system */
01124 /*          specific file group descriptor; a null argument */
01125 /*          implies the current directory. */
01126 /* */
01127 /*  Arguments:*/
01128 /*  dir -       directory to list */
01129 /*  */
01130 /*  Returns:*/
01131 /*  sorted list of files or {} if listing fails*/
01132 
01133 ret  ::ftp::NList (type s , optional dir ="") {
01134     upvar ::ftp::ftp$s ftp
01135 
01136     if { ![info exists ftp(State)] } {
01137         if { ![string is digit -strict $s] } {
01138             DisplayMsg $s "Bad connection name \"$s\"" error
01139         } else {
01140             DisplayMsg $s "Not connected!" error
01141         }
01142         return {}
01143     }
01144 
01145     set ftp(List) {}
01146     if { $dir == "" } {
01147         set ftp(Dir) ""
01148     } else {
01149         set ftp(Dir) " $dir"
01150     }
01151 
01152     # save current type and force ascii mode
01153     set old_type $ftp(Type)
01154     if { $ftp(Type) != "ascii" } {
01155     if {[string length $ftp(Command)]} {
01156         set ftp(NextState) [list nlist_$ftp(Mode) type_change list_last]
01157         set ftp(type:changeto) $old_type
01158         Type $s ascii
01159         return {}
01160     }
01161         Type $s ascii
01162     }
01163 
01164     set ftp(State) nlist_$ftp(Mode)
01165     StateHandler $s
01166 
01167     # wait for synchronization
01168     set rc [WaitOrTimeout $s]
01169 
01170     # restore old type
01171     if { [Type $s] != $old_type } {
01172         Type $s $old_type
01173     }
01174 
01175     unset ftp(Dir)
01176     if { $rc } {
01177     return [lsort [split [string trim $ftp(List) \n] \n]]
01178     } else {
01179         CloseDataConn $s
01180         return {}
01181     }
01182 }
01183 
01184 /* */
01185 /* */
01186 /*  List --*/
01187 /* */
01188 /*  LIST - This command causes a list to be sent from the server*/
01189 /*  to user site.*/
01190 /*  (exported)*/
01191 /*  */
01192 /*  Arguments:*/
01193 /*  dir -       If the $dir specifies a directory or other group of */
01194 /*          files, the server should transfer a list of files in */
01195 /*          the specified directory. If the $dir specifies a file*/
01196 /*          then the server should send current information on the*/
01197 /*              file.  A null argument implies the user's current */
01198 /*          working or default directory.  */
01199 /*  */
01200 /*  Returns:*/
01201 /*  list of files or {} if listing fails*/
01202 
01203 ret  ::ftp::List (type s , optional dir ="") {
01204 
01205     upvar ::ftp::ftp$s ftp
01206 
01207     if { ![info exists ftp(State)] } {
01208         if { ![string is digit -strict $s] } {
01209             DisplayMsg $s "Bad connection name \"$s\"" error
01210         } else {
01211             DisplayMsg $s "Not connected!" error
01212         }
01213         return {}
01214     }
01215 
01216     set ftp(List) {}
01217     if { $dir == "" } {
01218         set ftp(Dir) ""
01219     } else {
01220         set ftp(Dir) " $dir"
01221     }
01222 
01223     # save current type and force ascii mode
01224 
01225     set old_type $ftp(Type)
01226     if { ![string equal "$ftp(Type)" "ascii"] } {
01227     if {[string length $ftp(Command)]} {
01228         set ftp(NextState) [list list_$ftp(Mode) type_change list_last]
01229         set ftp(type:changeto) $old_type
01230         Type $s ascii
01231         return {}
01232     }
01233         Type $s ascii
01234     }
01235 
01236     set ftp(State) list_$ftp(Mode)
01237     StateHandler $s
01238 
01239     # wait for synchronization
01240 
01241     set rc [WaitOrTimeout $s]
01242 
01243     # restore old type
01244 
01245     if { ![string equal "[Type $s]" "$old_type"] } {
01246         Type $s $old_type
01247     }
01248 
01249     unset ftp(Dir)
01250     if { $rc } { 
01251     return [ListPostProcess $ftp(List)]
01252     } else {
01253         CloseDataConn $s
01254         return {}
01255     }
01256 }
01257 
01258 ret  ::ftp::ListPostProcess l (
01259 
01260     # type clear ", type total"-, type line
01261 
01262     , type set , type l [, type split $, type l "\, type n"]
01263     , type set , type index [, type lsearch -, type regexp $, type l "^, type total"]
01264     , type if , optional $index =!= "-1"  , optional 
01265     set =l [lreplace =$l $index =$index]
01266     
01267 
01268     # , type clear , type blank , type line
01269 
01270     , type set , type index [, type lsearch -, type regexp $, type l "^$"]
01271     , type if , optional $index =!= "-1"  , optional 
01272     set =l [lreplace =$l $index =$index]
01273     
01274 
01275     , type return $, type l
01276 )
01277 
01278 #############################################################################
01279 #
01280 # FileSize --
01281 #
01282 # REMOTE FILE SIZE - This command gets the file size of the
01283 # file on the remote machine. 
01284 # ATTENTION! Doesn't work properly in ascii mode!
01285 # (exported)
01286 # 
01287 # Arguments:
01288 # filename -        specifies the remote file name
01289 # 
01290 # Returns:
01291 # size -        files size in bytes or {} in error cases
01292 
01293 ret  ::ftp::FileSize (type s , optional filename ="") {
01294     upvar ::ftp::ftp$s ftp
01295 
01296     if { ![info exists ftp(State)] } {
01297         if { ![string is digit -strict $s] } {
01298             DisplayMsg $s "Bad connection name \"$s\"" error
01299         } else {
01300             DisplayMsg $s "Not connected!" error
01301         }
01302         return {}
01303     }
01304     
01305     if { $filename == "" } {
01306         return {}
01307     } 
01308 
01309     set ftp(File) $filename
01310     set ftp(FileSize) 0
01311     
01312     set ftp(State) size
01313     StateHandler $s
01314 
01315     # wait for synchronization
01316     set rc [WaitOrTimeout $s]
01317     
01318     if {![string length $ftp(Command)]} {
01319     unset ftp(File)
01320     }
01321         
01322     if { $rc } {
01323         return $ftp(FileSize)
01324     } else {
01325         return {}
01326     }
01327 }
01328 
01329 
01330 /* */
01331 /* */
01332 /*  ModTime --*/
01333 /* */
01334 /*  MODIFICATION TIME - This command gets the last modification time of the*/
01335 /*  file on the remote machine.*/
01336 /*  (exported)*/
01337 /*  */
01338 /*  Arguments:*/
01339 /*  filename -      specifies the remote file name*/
01340 /*  datetime -            optional new timestamp for file*/
01341 /*  */
01342 /*  Returns:*/
01343 /*  clock -     files date and time as a system-depentend integer*/
01344 /*          value in seconds (see tcls clock command) or {} in */
01345 /*          error cases*/
01346 /*  if MDTM not supported on server, returns original timestamp*/
01347 
01348 ret  ::ftp::ModTime (type s , optional filename ="" , optional datetime ="") {
01349     upvar ::ftp::ftp$s ftp
01350 
01351     if { ![info exists ftp(State)] } {
01352         if { ![string is digit -strict $s] } {
01353             DisplayMsg $s "Bad connection name \"$s\"" error
01354         } else {
01355             DisplayMsg $s "Not connected!" error
01356         } 
01357         return {}
01358     }
01359     
01360     if { $filename == "" } {
01361         return {}
01362     } 
01363 
01364     set ftp(File) $filename
01365 
01366     if {$datetime != ""} {
01367       set datetime [clock format $datetime -format "%Y%m%d%H%M%S"]
01368     }
01369     set ftp(DateTime) $datetime
01370     
01371     set ftp(State) modtime
01372     StateHandler $s
01373 
01374     # wait for synchronization
01375     set rc [WaitOrTimeout $s]
01376     
01377     if {![string length $ftp(Command)]} {
01378     unset ftp(File)
01379     }
01380     if { ![string length $ftp(Command)] && $rc } {
01381         return [ModTimePostProcess $ftp(DateTime)]
01382     } else {
01383         return {}
01384     }
01385 }
01386 
01387 ret  ::ftp::ModTimePostProcess (type clock) {
01388     foreach {year month day hour min sec} {1 1 1 1 1 1} break
01389 
01390     # Bug #478478. Special code to detect ftp servers with a Y2K patch
01391     # gone bad and delivering, hmmm, non-standard date information.
01392 
01393     if {[string length $clock] == 15} {
01394         scan $clock "%2s%3s%2s%2s%2s%2s%2s" cent year month day hour min sec
01395         set year [expr {($cent * 100) + $year}]
01396     log::log warning "data | W: server with non-standard time, bad Y2K patch."
01397     } else {
01398         scan $clock "%4s%2s%2s%2s%2s%2s" year month day hour min sec
01399     }
01400 
01401     set clock [clock scan "$month/$day/$year $hour:$min:$sec" -gmt 1]
01402     return $clock
01403 }
01404 
01405 /* */
01406 /* */
01407 /*  Pwd --*/
01408 /* */
01409 /*  PRINT WORKING DIRECTORY - Causes the name of the current working directory.*/
01410 /*  (exported)*/
01411 /*  */
01412 /*  Arguments:*/
01413 /*  None.*/
01414 /*  */
01415 /*  Returns:*/
01416 /*  current directory name*/
01417 
01418 ret  ::ftp::Pwd (type s ) {
01419     upvar ::ftp::ftp$s ftp
01420 
01421     if { ![info exists ftp(State)] } {
01422         if { ![string is digit -strict $s] } {
01423             DisplayMsg $s "Bad connection name \"$s\"" error
01424         } else {
01425             DisplayMsg $s "Not connected!" error
01426         }
01427         return {}
01428     }
01429 
01430     set ftp(Dir) {}
01431 
01432     set ftp(State) pwd
01433     StateHandler $s
01434 
01435     # wait for synchronization
01436     set rc [WaitOrTimeout $s]
01437     
01438     if { $rc } {
01439         return $ftp(Dir)
01440     } else {
01441         return {}
01442     }
01443 }
01444 
01445 /* */
01446 /* */
01447 /*  Cd --*/
01448 /*    */
01449 /*  CHANGE DIRECTORY - Sets the working directory on the server host.*/
01450 /*  (exported)*/
01451 /*  */
01452 /*  Arguments:*/
01453 /*  dir -           pathname specifying a directory*/
01454 /* */
01455 /*  Returns:*/
01456 /*  0 -         ERROR*/
01457 /*  1 -             OK*/
01458 
01459 ret  ::ftp::Cd (type s , optional dir ="") {
01460     upvar ::ftp::ftp$s ftp
01461 
01462     if { ![info exists ftp(State)] } {
01463         if { ![string is digit -strict $s] } {
01464             DisplayMsg $s "Bad connection name \"$s\"" error
01465         } else {
01466             DisplayMsg $s "Not connected!" error
01467         }
01468         return 0
01469     }
01470 
01471     if { $dir == "" } {
01472         set ftp(Dir) ""
01473     } else {
01474         set ftp(Dir) " $dir"
01475     }
01476 
01477     set ftp(State) cd
01478     StateHandler $s
01479 
01480     # wait for synchronization
01481     set rc [WaitOrTimeout $s] 
01482 
01483     if {![string length $ftp(Command)]} {
01484     unset ftp(Dir)
01485     }
01486     
01487     if { $rc } {
01488         return 1
01489     } else {
01490         return 0
01491     }
01492 }
01493 
01494 /* */
01495 /* */
01496 /*  MkDir --*/
01497 /* */
01498 /*  MAKE DIRECTORY - This command causes the directory specified in the $dir*/
01499 /*  to be created as a directory (if the $dir is absolute) or as a subdirectory*/
01500 /*  of the current working directory (if the $dir is relative).*/
01501 /*  (exported)*/
01502 /*  */
01503 /*  Arguments:*/
01504 /*  dir -           new directory name*/
01505 /* */
01506 /*  Returns:*/
01507 /*  0 -         ERROR*/
01508 /*  1 -             OK*/
01509 
01510 ret  ::ftp::MkDir (type s , type dir) {
01511     upvar ::ftp::ftp$s ftp
01512 
01513     if { ![info exists ftp(State)] } {
01514         DisplayMsg $s "Not connected!" error
01515         return 0
01516     }
01517 
01518     set ftp(Dir) $dir
01519 
01520     set ftp(State) mkdir
01521     StateHandler $s
01522 
01523     # wait for synchronization
01524     set rc [WaitOrTimeout $s] 
01525 
01526     if {![string length $ftp(Command)]} {
01527     unset ftp(Dir)
01528     }
01529     
01530     if { $rc } {
01531         return 1
01532     } else {
01533         return 0
01534     }
01535 }
01536 
01537 /* */
01538 /* */
01539 /*  RmDir --*/
01540 /* */
01541 /*  REMOVE DIRECTORY - This command causes the directory specified in $dir to */
01542 /*  be removed as a directory (if the $dir is absolute) or as a */
01543 /*  subdirectory of the current working directory (if the $dir is relative).*/
01544 /*  (exported)*/
01545 /* */
01546 /*  Arguments:*/
01547 /*  dir -           directory name*/
01548 /* */
01549 /*  Returns:*/
01550 /*  0 -         ERROR*/
01551 /*  1 -             OK*/
01552 
01553 ret  ::ftp::RmDir (type s , type dir) {
01554     upvar ::ftp::ftp$s ftp
01555 
01556     if { ![info exists ftp(State)] } {
01557         DisplayMsg $s "Not connected!" error
01558         return 0
01559     }
01560 
01561     set ftp(Dir) $dir
01562 
01563     set ftp(State) rmdir
01564     StateHandler $s
01565 
01566     # wait for synchronization
01567     set rc [WaitOrTimeout $s] 
01568 
01569     if {![string length $ftp(Command)]} {
01570     unset ftp(Dir)
01571     }
01572     
01573     if { $rc } {
01574         return 1
01575     } else {
01576         return 0
01577     }
01578 }
01579 
01580 /* */
01581 /* */
01582 /*  Delete --*/
01583 /* */
01584 /*  DELETE - This command causes the file specified in $file to be deleted at */
01585 /*  the server site.*/
01586 /*  (exported)*/
01587 /*  */
01588 /*  Arguments:*/
01589 /*  file -          file name*/
01590 /* */
01591 /*  Returns:*/
01592 /*  0 -         ERROR*/
01593 /*  1 -             OK*/
01594 
01595 ret  ::ftp::Delete (type s , type file) {
01596     upvar ::ftp::ftp$s ftp
01597 
01598     if { ![info exists ftp(State)] } {
01599         DisplayMsg $s "Not connected!" error
01600         return 0
01601     }
01602 
01603     set ftp(File) $file
01604 
01605     set ftp(State) delete
01606     StateHandler $s
01607 
01608     # wait for synchronization
01609     set rc [WaitOrTimeout $s] 
01610 
01611     if {![string length $ftp(Command)]} {
01612     unset ftp(File)
01613     }
01614     
01615     if { $rc } {
01616         return 1
01617     } else {
01618         return 0
01619     }
01620 }
01621 
01622 /* */
01623 /* */
01624 /*  Rename --*/
01625 /* */
01626 /*  RENAME FROM TO - This command causes the file specified in $from to be */
01627 /*  renamed at the server site.*/
01628 /*  (exported)*/
01629 /*  */
01630 /*  Arguments:*/
01631 /*  from -          specifies the old file name of the file which */
01632 /*              is to be renamed*/
01633 /*  to -                specifies the new file name of the file */
01634 /*              specified in the $from agument*/
01635 /*  Returns:*/
01636 /*  0 -         ERROR*/
01637 /*  1 -             OK*/
01638 
01639 ret  ::ftp::Rename (type s , type from , type to) {
01640     upvar ::ftp::ftp$s ftp
01641 
01642     if { ![info exists ftp(State)] } {
01643         DisplayMsg $s "Not connected!" error
01644         return 0
01645     }
01646 
01647     set ftp(RenameFrom) $from
01648     set ftp(RenameTo) $to
01649 
01650     set ftp(State) rename
01651 
01652     StateHandler $s
01653 
01654     # wait for synchronization
01655     set rc [WaitOrTimeout $s] 
01656 
01657     if {![string length $ftp(Command)]} {
01658     unset ftp(RenameFrom)
01659     unset ftp(RenameTo)
01660     }
01661     
01662     if { $rc } {
01663         return 1
01664     } else {
01665         return 0
01666     }
01667 }
01668 
01669 /* */
01670 /* */
01671 /*  ElapsedTime --*/
01672 /* */
01673 /*  Gets the elapsed time for file transfer*/
01674 /*  */
01675 /*  Arguments:*/
01676 /*  stop_time -         ending time*/
01677 
01678 ret  ::ftp::ElapsedTime (type s , type stop_, type time) {
01679     variable VERBOSE
01680     upvar ::ftp::ftp$s ftp
01681 
01682     set elapsed [expr {$stop_time - $ftp(Start_Time)}]
01683     if { $elapsed == 0 } {
01684         set elapsed 1
01685     }
01686     set persec [expr {$ftp(Total) / $elapsed}]
01687     if { $VERBOSE } {
01688         DisplayMsg $s "$ftp(Total) bytes sent in $elapsed seconds ($persec Bytes/s)"
01689     }
01690     return
01691 }
01692 
01693 /* */
01694 /* */
01695 /*  PUT --*/
01696 /* */
01697 /*  STORE DATA - Causes the server to accept the data transferred via the data */
01698 /*  connection and to store the data as a file at the server site.  If the file*/
01699 /*  exists at the server site, then its contents shall be replaced by the data*/
01700 /*  being transferred.  A new file is created at the server site if the file*/
01701 /*  does not already exist.*/
01702 /*  (exported)*/
01703 /* */
01704 /*  Arguments:*/
01705 /*  source -            local file name*/
01706 /*  dest -          remote file name, if unspecified, ftp assigns*/
01707 /*              the local file name.*/
01708 /*  Returns:*/
01709 /*  0 -         file not stored*/
01710 /*  1 -             OK*/
01711 
01712 ret  ::ftp::Put (type s , type args) {
01713     upvar ::ftp::ftp$s ftp
01714 
01715     if { ![info exists ftp(State)] } {
01716         DisplayMsg $s "Not connected!" error
01717         return 0
01718     }
01719     if {([llength $args] < 1) || ([llength $args] > 4)} {
01720         DisplayMsg $s \
01721         "wrong # args: should be \"ftp::Put handle (-data \"data\" | -channel chan | localFilename) remoteFilename\"" error
01722     return 0    
01723     }
01724 
01725     set ftp(inline) 0
01726     set flags 1
01727     set source ""
01728     set dest ""
01729     foreach arg $args {
01730         if {[string equal $arg "--"]} {
01731             set flags 0
01732         } elseif {($flags) && ([string equal $arg "-data"])} {
01733             set ftp(inline) 1
01734             set ftp(filebuffer) ""
01735         } elseif {($flags) && ([string equal $arg "-channel"])} {
01736             set ftp(inline) 2
01737     } elseif {$source == ""} {
01738             set source $arg
01739     } elseif {$dest == ""} {
01740             set dest $arg
01741     } else {
01742             DisplayMsg $s "wrong # args: should be \"ftp::Put handle (-data \"data\" | -channel chan | localFilename) remoteFilename\"" error
01743         return 0
01744         }
01745     }
01746 
01747     if {($source == "")} {
01748         DisplayMsg $s "Must specify a valid data source to Put" error
01749         return 0
01750     }        
01751 
01752     set ftp(RemoteFilename) $dest
01753 
01754     if {$ftp(inline) == 1} {
01755         set ftp(PutData) $source
01756         if { $dest == "" } {
01757             set dest ftp.tmp
01758         }
01759         set ftp(RemoteFilename) $dest
01760     } else {
01761     if {$ftp(inline) == 0} {
01762         # File transfer
01763 
01764         set ftp(PutData) ""
01765         if { ![file exists $source] } {
01766         DisplayMsg $s "File \"$source\" not exist" error
01767         return 0
01768         }
01769         if { $dest == "" } {
01770         set dest [file tail $source]
01771         }
01772         set ftp(LocalFilename) $source
01773         set ftp(SourceCI) [open $ftp(LocalFilename) r]
01774     } else {
01775         # Channel transfer. We fake the rest of the system into
01776         # believing that a file transfer is happening. This makes
01777         # the handling easier.
01778 
01779         set ftp(SourceCI) $source
01780         set ftp(inline) 0
01781     }
01782         set ftp(RemoteFilename) $dest
01783 
01784     # TODO: read from source file asynchronously
01785         if { [string equal $ftp(Type) "ascii"] } {
01786             fconfigure $ftp(SourceCI) -buffering line -blocking 1
01787         } else {
01788             fconfigure $ftp(SourceCI) -buffering line -translation binary -blocking 1
01789         }
01790     }
01791 
01792     set ftp(State) put_$ftp(Mode)
01793     StateHandler $s
01794 
01795     # wait for synchronization
01796     set rc [WaitOrTimeout $s]
01797     if { $rc } {
01798     if {![string length $ftp(Command)]} {
01799         ElapsedTime $s [clock seconds]
01800     }
01801         return 1
01802     } else {
01803         CloseDataConn $s
01804         return 0
01805     }
01806 }
01807 
01808 /* */
01809 /* */
01810 /*  APPEND --*/
01811 /* */
01812 /*  APPEND DATA - Causes the server to accept the data transferred via the data */
01813 /*  connection and to store the data as a file at the server site.  If the file*/
01814 /*  exists at the server site, then the data shall be appended to that file; */
01815 /*  otherwise the file specified in the pathname shall be created at the*/
01816 /*  server site.*/
01817 /*  (exported)*/
01818 /* */
01819 /*  Arguments:*/
01820 /*  source -            local file name*/
01821 /*  dest -          remote file name, if unspecified, ftp assigns*/
01822 /*              the local file name.*/
01823 /*  Returns:*/
01824 /*  0 -         file not stored*/
01825 /*  1 -             OK*/
01826 
01827 ret  ::ftp::Append (type s , type args) {
01828     upvar ::ftp::ftp$s ftp
01829 
01830     if { ![info exists ftp(State)] } {
01831         DisplayMsg $s "Not connected!" error
01832         return 0
01833     }
01834 
01835     if {([llength $args] < 1) || ([llength $args] > 4)} {
01836         DisplayMsg $s "wrong # args: should be \"ftp::Append handle (-data \"data\" | -channel chan | localFilename) remoteFilename\"" error
01837         return 0
01838     }
01839 
01840     set ftp(inline) 0
01841     set flags 1
01842     set source ""
01843     set dest ""
01844     foreach arg $args {
01845         if {[string equal $arg "--"]} {
01846             set flags 0
01847         } elseif {($flags) && ([string equal $arg "-data"])} {
01848             set ftp(inline) 1
01849             set ftp(filebuffer) ""
01850         } elseif {($flags) && ([string equal $arg "-channel"])} {
01851             set ftp(inline) 2
01852         } elseif {$source == ""} {
01853             set source $arg
01854         } elseif {$dest == ""} {
01855             set dest $arg
01856         } else {
01857             DisplayMsg $s "wrong # args: should be \"ftp::Append handle (-data \"data\" | -channel chan | localFilename) remoteFilename\"" error
01858             return 0
01859         }
01860     }
01861 
01862     if {($source == "")} {
01863         DisplayMsg $s "Must specify a valid data source to Append" error
01864         return 0
01865     }   
01866 
01867     set ftp(RemoteFilename) $dest
01868 
01869     if {$ftp(inline) == 1} {
01870         set ftp(PutData) $source
01871         if { $dest == "" } {
01872             set dest ftp.tmp
01873         }
01874         set ftp(RemoteFilename) $dest
01875     } else {
01876     if {$ftp(inline) == 0} {
01877         # File transfer
01878 
01879         set ftp(PutData) ""
01880         if { ![file exists $source] } {
01881         DisplayMsg $s "File \"$source\" not exist" error
01882         return 0
01883         }
01884             
01885         if { $dest == "" } {
01886         set dest [file tail $source]
01887         }
01888 
01889         set ftp(LocalFilename) $source
01890         set ftp(SourceCI) [open $ftp(LocalFilename) r]
01891     } else {
01892         # Channel transfer. We fake the rest of the system into
01893         # believing that a file transfer is happening. This makes
01894         # the handling easier.
01895 
01896         set ftp(SourceCI) $source
01897         set ftp(inline) 0
01898     }
01899         set ftp(RemoteFilename) $dest
01900 
01901         if { [string equal $ftp(Type) "ascii"] } {
01902             fconfigure $ftp(SourceCI) -buffering line -blocking 1
01903         } else {
01904             fconfigure $ftp(SourceCI) -buffering line -translation binary \
01905                     -blocking 1
01906         }
01907     }
01908 
01909     set ftp(State) append_$ftp(Mode)
01910     StateHandler $s
01911 
01912     # wait for synchronization
01913     set rc [WaitOrTimeout $s]
01914     if { $rc } {
01915     if {![string length $ftp(Command)]} {
01916         ElapsedTime $s [clock seconds]
01917     }
01918         return 1
01919     } else {
01920         CloseDataConn $s
01921         return 0
01922     }
01923 }
01924 
01925 
01926 /* */
01927 /* */
01928 /*  Get --*/
01929 /* */
01930 /*  RETRIEVE DATA - Causes the server to transfer a copy of the specified file*/
01931 /*  to the local site at the other end of the data connection.*/
01932 /*  (exported)*/
01933 /* */
01934 /*  Arguments:*/
01935 /*  source -            remote file name*/
01936 /*  dest -          local file name, if unspecified, ftp assigns*/
01937 /*              the remote file name.*/
01938 /*  Returns:*/
01939 /*  0 -         file not retrieved*/
01940 /*  1 -             OK*/
01941 
01942 ret  ::ftp::Get (type s , type args) {
01943     upvar ::ftp::ftp$s ftp
01944 
01945     if { ![info exists ftp(State)] } {
01946         DisplayMsg $s "Not connected!" error
01947         return 0
01948     }
01949 
01950     if {([llength $args] < 1) || ([llength $args] > 4)} {
01951         DisplayMsg $s "wrong # args: should be \"ftp::Get handle remoteFile ?(-variable varName | -channel chan | localFilename)?\"" error
01952     return 0    
01953     }
01954 
01955     set ftp(inline) 0
01956     set flags 1
01957     set source ""
01958     set dest ""
01959     set varname "**NONE**"
01960     foreach arg $args {
01961         if {[string equal $arg "--"]} {
01962             set flags 0
01963         } elseif {($flags) && ([string equal $arg "-variable"])} {
01964             set ftp(inline) 1
01965             set ftp(filebuffer) ""
01966         } elseif {($flags) && ([string equal $arg "-channel"])} {
01967             set ftp(inline) 2
01968     } elseif {($ftp(inline) == 1) && ([string equal $varname "**NONE**"])} {
01969             set varname $arg
01970         set ftp(get:varname) $varname
01971     } elseif {($ftp(inline) == 2) && ([string equal $varname "**NONE**"])} {
01972         set ftp(get:channel) $arg
01973     } elseif {$source == ""} {
01974             set source $arg
01975     } elseif {$dest == ""} {
01976             set dest $arg
01977     } else {
01978             DisplayMsg $s "wrong # args: should be \"ftp::Get handle remoteFile
01979 ?(-variable varName | -channel chan | localFilename)?\"" error
01980         return 0
01981         }
01982     }
01983 
01984     if {($ftp(inline) != 0) && ($dest != "")} {
01985         DisplayMsg $s "Cannot return data in a variable or channel, and place it in destination file." error
01986         return 0
01987     }
01988 
01989     if {$source == ""} {
01990         DisplayMsg $s "Must specify a valid data source to Get" error
01991         return 0
01992     }
01993 
01994     if {$ftp(inline) == 0} {
01995     if { $dest == "" } {
01996         set dest $source
01997     } else {
01998         if {[file isdirectory $dest]} {
01999         set dest [file join $dest [file tail $source]]
02000         }
02001     }
02002     if {![file exists [file dirname $dest]]} {
02003         return -code error "ftp::Get, directory \"[file dirname $dest]\" for destination \"$dest\" does not exist"
02004     }
02005     set ftp(LocalFilename) $dest
02006     }
02007 
02008     set ftp(RemoteFilename) $source
02009 
02010     if {$ftp(inline) == 2} {
02011     set ftp(inline) 0
02012     }
02013     set ftp(State) get_$ftp(Mode)
02014     StateHandler $s
02015 
02016     # wait for synchronization
02017     set rc [WaitOrTimeout $s]
02018 
02019     # It is important to unset 'get:channel' in all cases or it will
02020     # interfere with any following ftp command (as its existence
02021     # suppresses the closing of the destination channel identifier
02022     # (DestCI). We cannot do it earlier than just before the 'return'
02023     # or code depending on it for the current command may not execute
02024     # correctly.
02025 
02026     if { $rc } {
02027     if {![string length $ftp(Command)]} {
02028         ElapsedTime $s [clock seconds]
02029         if {$ftp(inline)} {
02030         catch {unset ftp(get:channel)}
02031         upvar $varname returnData
02032         set returnData $ftp(GetData)
02033         }
02034     }
02035     # catch {unset ftp(get:channel)}
02036     # SF Bug 1708350. DISABLED. In async mode (Open -command) the
02037     # unset here causes HandleData to blow up, see marker <@>. In
02038     # essence in async mode HandleData can be entered multiple
02039     # times, and unsetting get:channel here causes it to think
02040     # that the data goes into a local file, not a channel, but the
02041     # state does not contain local file information, so an error
02042     # is thrown. Removing the catch here seems to fix it without
02043     # adverse effects elsewhere. Maybe. We hope.
02044         return 1
02045     } else {
02046         if {$ftp(inline)} {
02047         catch {unset ftp(get:channel)}
02048             return ""
02049     }
02050         CloseDataConn $s
02051     catch {unset ftp(get:channel)}
02052         return 0
02053     }
02054 }
02055 
02056 /* */
02057 /* */
02058 /*  Reget --*/
02059 /* */
02060 /*  RESTART RETRIEVING DATA - Causes the server to transfer a copy of the specified file*/
02061 /*  to the local site at the other end of the data connection like get but skips over */
02062 /*  the file to the specified data checkpoint. */
02063 /*  (exported)*/
02064 /* */
02065 /*  Arguments:*/
02066 /*  source -            remote file name*/
02067 /*  dest -          local file name, if unspecified, ftp assigns*/
02068 /*              the remote file name.*/
02069 /*  Returns:*/
02070 /*  0 -         file not retrieved*/
02071 /*  1 -             OK*/
02072 
02073 ret  ::ftp::Reget (type s , type source , optional dest ="" , optional from_bytes =0 , optional till_bytes =-1) {
02074     upvar ::ftp::ftp$s ftp
02075     
02076     if { ![info exists ftp(State)] } {
02077         DisplayMsg $s "Not connected!" error
02078         return 0
02079     }
02080 
02081     if { $dest == "" } {
02082         set dest $source
02083     }
02084     if {![file exists [file dirname $dest]]} {
02085     return -code error \
02086     "ftp::Reget, directory \"[file dirname $dest]\" for destination \"$dest\" does not exist"
02087     }
02088 
02089     set ftp(RemoteFilename) $source
02090     set ftp(LocalFilename) $dest
02091     set ftp(From) $from_bytes
02092 
02093 
02094     # Assumes that the local file has a starting offset of $from_bytes
02095     # The following calculation ensures that the download starts from the
02096     # correct offset
02097 
02098     if { [file exists $ftp(LocalFilename)] } {
02099     set ftp(FileSize) [ expr {[file size $ftp(LocalFilename)] + $from_bytes }]
02100         
02101     if { $till_bytes != -1 } {
02102         set ftp(To)   $till_bytes   
02103         set ftp(Bytes_to_go) [ expr {$till_bytes - $ftp(FileSize)} ]
02104     
02105         if { $ftp(Bytes_to_go) <= 0 } {return 0}
02106 
02107     } else {
02108         # till_bytes not set
02109         set ftp(To)   end
02110     }
02111 
02112     } else {
02113     # local file does not exist
02114         set ftp(FileSize) $from_bytes
02115           
02116     if { $till_bytes != -1 } {
02117         set ftp(Bytes_to_go) [ expr {$till_bytes - $from_bytes }]
02118         set ftp(To) $till_bytes
02119     } else {
02120         #till_bytes not set
02121         set ftp(To)   end
02122     }
02123     }
02124     
02125     set ftp(State) reget_$ftp(Mode)
02126     StateHandler $s
02127 
02128     # wait for synchronization
02129     set rc [WaitOrTimeout $s]
02130     if { $rc } {
02131     if {![string length $ftp(Command)]} {
02132         ElapsedTime $s [clock seconds]
02133     }
02134         return 1
02135     } else {
02136         CloseDataConn $s
02137         return 0
02138     }
02139 }
02140 
02141 /* */
02142 /* */
02143 /*  Newer --*/
02144 /* */
02145 /*  GET NEWER DATA - Get the file only if the modification time of the remote */
02146 /*  file is more recent that the file on the current system. If the file does*/
02147 /*  not exist on the current system, the remote file is considered newer.*/
02148 /*  Otherwise, this command is identical to get. */
02149 /*  (exported)*/
02150 /* */
02151 /*  Arguments:*/
02152 /*  source -            remote file name*/
02153 /*  dest -          local file name, if unspecified, ftp assigns*/
02154 /*              the remote file name.*/
02155 /* */
02156 /*  Returns:*/
02157 /*  0 -         file not retrieved*/
02158 /*  1 -             OK*/
02159 
02160 ret  ::ftp::Newer (type s , type source , optional dest ="") {
02161     upvar ::ftp::ftp$s ftp
02162 
02163     if { ![info exists ftp(State)] } {
02164         DisplayMsg $s "Not connected!" error
02165         return 0
02166     }
02167 
02168     if {[string length $ftp(Command)]} {
02169     return -code error "unable to retrieve file asynchronously (not implemented yet)"
02170     }
02171 
02172     if { $dest == "" } {
02173         set dest $source
02174     }
02175     if {![file exists [file dirname $dest]]} {
02176     return -code error "ftp::Newer, directory \"[file dirname $dest]\" for destination \"$dest\" does not exist"
02177     }
02178 
02179     set ftp(RemoteFilename) $source
02180     set ftp(LocalFilename) $dest
02181 
02182     # get remote modification time
02183     set rmt [ModTime $s $ftp(RemoteFilename)]
02184     if { $rmt == "-1" } {
02185         return 0
02186     }
02187 
02188     # get local modification time
02189     if { [file exists $ftp(LocalFilename)] } {
02190         set lmt [file mtime $ftp(LocalFilename)]
02191     } else {
02192         set lmt 0
02193     }
02194     
02195     # remote file is older than local file
02196     if { $rmt < $lmt } {
02197         return 0
02198     }
02199 
02200     # remote file is newer than local file or local file doesn't exist
02201     # get it
02202     set rc [Get $s $ftp(RemoteFilename) $ftp(LocalFilename)]
02203     return $rc
02204         
02205 }
02206 
02207 /* */
02208 /* */
02209 /*  Quote -- */
02210 /* */
02211 /*  The arguments specified are sent, verbatim, to the remote ftp server.     */
02212 /* */
02213 /*  Arguments:*/
02214 /*      arg1 arg2 ...*/
02215 /* */
02216 /*  Returns:*/
02217 /*   string sent back by the remote ftp server or null string if any error*/
02218 /* */
02219 
02220 ret  ::ftp::Quote (type s , type args) {
02221     upvar ::ftp::ftp$s ftp
02222 
02223     if { ![info exists ftp(State)] } {
02224         DisplayMsg $s "Not connected!" error
02225         return 0
02226     }
02227 
02228     set ftp(Cmd) $args
02229     set ftp(Quote) {}
02230 
02231     set ftp(State) quote
02232     StateHandler $s
02233 
02234     # wait for synchronization
02235     set rc [WaitOrTimeout $s] 
02236 
02237     unset ftp(Cmd)
02238 
02239     if { $rc } {
02240         return $ftp(Quote)
02241     } else {
02242         return {}
02243     }
02244 }
02245 
02246 
02247 /* */
02248 /* */
02249 /*  Abort -- */
02250 /* */
02251 /*  ABORT - Tells the server to abort the previous ftp service command and */
02252 /*  any associated transfer of data. The control connection is not to be */
02253 /*  closed by the server, but the data connection must be closed.*/
02254 /*  */
02255 /*  NOTE: This procedure doesn't work properly. Thus the ftp::Abort command*/
02256 /*  is no longer available!*/
02257 /* */
02258 /*  Arguments:*/
02259 /*  None.*/
02260 /* */
02261 /*  Returns:*/
02262 /*  0 -         ERROR*/
02263 /*  1 -             OK*/
02264 /* */
02265 /*  proc Abort {} {*/
02266 /* */
02267 /*  }*/
02268 
02269 /* */
02270 /* */
02271 /*  Close -- */
02272 /* */
02273 /*  Terminates a ftp session and if file transfer is not in progress, the server*/
02274 /*  closes the control connection.  If file transfer is in progress, the */
02275 /*  connection will remain open for result response and the server will then*/
02276 /*  close it. */
02277 /*  (exported)*/
02278 /*  */
02279 /*  Arguments:*/
02280 /*  None.*/
02281 /* */
02282 /*  Returns:*/
02283 /*  0 -         ERROR*/
02284 /*  1 -             OK*/
02285 
02286 ret  ::ftp::Close (type s ) {
02287     variable connections
02288     upvar ::ftp::ftp$s ftp
02289 
02290     if { ![info exists ftp(State)] } {
02291         DisplayMsg $s "Not connected!" error
02292         return 0
02293     }
02294 
02295     if {[info exists \
02296             connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost),afterid)]} {
02297         unset connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost),afterid)
02298         unset connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost))
02299     }
02300 
02301     set ftp(State) quit
02302     StateHandler $s
02303 
02304     # wait for synchronization
02305     WaitOrTimeout $s
02306 
02307     catch {close $ftp(CtrlSock)}
02308     catch {unset ftp}
02309     return 1
02310 }
02311 
02312 ret  ::ftp::LazyClose (type s ) {
02313     variable connections
02314     upvar ::ftp::ftp$s ftp
02315 
02316     if { ![info exists ftp(State)] } {
02317         DisplayMsg $s "Not connected!" error
02318         return 0
02319     }
02320 
02321     if {[info exists connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost))]} {
02322         set connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost),afterid) \
02323                 [after 5000 [list ftp::Close $s]]
02324     }
02325     return 1
02326 }
02327 
02328 /* */
02329 /* */
02330 /*  Open --*/
02331 /* */
02332 /*  Starts the ftp session and sets up a ftp control connection.*/
02333 /*  (exported)*/
02334 /*  */
02335 /*  Arguments:*/
02336 /*  server -        The ftp server hostname.*/
02337 /*  user -      A string identifying the user. The user identification */
02338 /*          is that which is required by the server for access to */
02339 /*          its file system.  */
02340 /*  passwd -        A string specifying the user's password.*/
02341 /*  options -       -blocksize size     writes "size" bytes at once*/
02342 /*                      (default 4096)*/
02343 /*          -timeout seconds    if non-zero, sets up timeout to*/
02344 /*                      occur after specified number of*/
02345 /*                      seconds (default 120)*/
02346 /*          -progress proc      procedure name that handles callbacks*/
02347 /*                      (no default)  */
02348 /*          -output proc        procedure name that handles output*/
02349 /*                      (no default)  */
02350 /*          -mode mode      switch active or passive file transfer*/
02351 /*                      (default active)*/
02352 /*          -port number        alternative port (default 21)*/
02353 /*          -command proc       callback for completion notification*/
02354 /*                      (no default)*/
02355 /*  */
02356 /*  Returns:*/
02357 /*  0 -         Not logged in*/
02358 /*  1 -             User logged in*/
02359 
02360 ret  ::ftp::Open (type server , type user , type passwd , type args) {
02361     variable DEBUG 
02362     variable VERBOSE
02363     variable serial
02364     variable connections
02365 
02366     set s $serial
02367     incr serial
02368     upvar ::ftp::ftp$s ftp
02369 #    if { [info exists ftp(State)] } {
02370 #        DisplayMsg $s "Mmh, another attempt to open a new connection? There is already a hot wire!" error
02371 #        return 0
02372 #    }
02373 
02374     # default NO DEBUG
02375     if { ![info exists DEBUG] } {
02376         set DEBUG 0
02377     }
02378 
02379     # default NO VERBOSE
02380     if { ![info exists VERBOSE] } {
02381         set VERBOSE 0
02382     }
02383     
02384     if { $DEBUG } {
02385         DisplayMsg $s "Starting new connection with: "
02386     }
02387 
02388     set ftp(inline)     0
02389     set ftp(User)       $user
02390     set ftp(Passwd)     $passwd
02391     set ftp(RemoteHost) $server
02392     set ftp(LocalHost)  [info hostname]
02393     set ftp(DataPort)   0
02394     set ftp(Type)   {}
02395     set ftp(Error)  ""
02396     set ftp(Progress)   {}
02397     set ftp(Command)    {}
02398     set ftp(Output)     {}
02399     set ftp(Blocksize)  4096    
02400     set ftp(Timeout)    600 
02401     set ftp(Mode)   active  
02402     set ftp(Port)   21  
02403 
02404     set ftp(State)  user
02405     
02406     # set state var
02407     set ftp(state.control) ""
02408     
02409     # Get and set possible options
02410     set options {-blocksize -timeout -mode -port -progress -output -command}
02411     foreach {option value} $args {
02412         if { [lsearch -exact $options $option] != "-1" } {
02413             if { $DEBUG } {
02414                 DisplayMsg $s "  $option = $value"
02415             }
02416             regexp -- {^-(.?)(.*)$} $option all first rest
02417             set option "[string toupper $first]$rest"
02418             set ftp($option) $value
02419         } 
02420     }
02421     if { $DEBUG && ([llength $args] == 0) } {
02422         DisplayMsg $s "  no option"
02423     }
02424 
02425     if {[info exists \
02426             connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost),afterid)]} {
02427         after cancel $connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost),afterid)
02428     Command $ftp(Command) connect $connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost))
02429         return $connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost))
02430     }
02431 
02432 
02433     # No call of StateHandler is required at this time.
02434     # StateHandler at first time is called automatically
02435     # by a fileevent for the control channel.
02436 
02437     # Try to open a control connection
02438     if { ![OpenControlConn $s [expr {[string length $ftp(Command)] > 0}]] } {
02439         return -1
02440     }
02441 
02442     # waits for synchronization
02443     #   0 ... Not logged in
02444     #   1 ... User logged in
02445     if {[string length $ftp(Command)]} {
02446     # Don't wait - asynchronous operation
02447     set ftp(NextState) {type connect_last}
02448         set connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost)) $s
02449     return $s
02450     } elseif { [WaitOrTimeout $s] } {
02451         # default type is binary
02452         Type $s binary
02453         set connections($ftp(User),$ftp(Passwd),$ftp(RemoteHost)) $s
02454     Command $ftp(Command) connect $s
02455         return $s
02456     } else {
02457         # close connection if not logged in
02458         Close $s
02459         return -1
02460     }
02461 }
02462 
02463 /* */
02464 /* */
02465 /*  CopyNext --*/
02466 /* */
02467 /*  recursive background copy procedure for ascii/binary file I/O*/
02468 /*  */
02469 /*  Arguments:*/
02470 /*  bytes -         indicates how many bytes were written on $ftp(DestCI)*/
02471 
02472 ret  ::ftp::CopyNext (type s , type bytes , optional error ={)} {
02473     upvar ::ftp::ftp$s ftp
02474     variable DEBUG
02475     variable VERBOSE
02476 
02477     # summary bytes
02478 
02479     incr ftp(Total) $bytes
02480 
02481     # update bytes_to_go and blocksize
02482 
02483     if { [info exists ftp(Bytes_to_go)] } {
02484      ftp = (Bytes_to_go) [expr {$ftp(Bytes_to_go) - $bytes}]
02485      
02486     if { $ftp(Blocksize) <= $ftp(Bytes_to_go) } {
02487          blocksize =  $ftp(Blocksize)
02488     } else {
02489          blocksize =  $ftp(Bytes_to_go)
02490     }
02491     } else {
02492      blocksize =  $ftp(Blocksize)
02493     } 
02494     
02495     /*  callback for progress bar procedure*/
02496     
02497     if { ([info exists ftp(Progress)]) && \
02498         [string length $ftp(Progress)] && \
02499         ([info commands [lindex $ftp(Progress) 0]] != "") } { 
02500         eval $ftp(Progress) $ftp(Total)
02501     }
02502 
02503     /*  setup new timeout handler*/
02504 
02505     catch {after cancel $ftp(Wait)}
02506      ftp = (Wait) [after [expr {$ftp(Timeout) * 1000}] [namespace current]::Timeout $s]
02507 
02508     if { $DEBUG } {
02509         DisplayMsg $s "-> $ftp(Total) bytes $ftp(SourceCI) -> $ftp(DestCI)" 
02510     }
02511 
02512     if { $error != "" } {
02513     /*  Protect the destination channel from destruction if it came*/
02514     /*  from the caller. Closing it is not our responsibility in that case.*/
02515 
02516     if {![info exists ftp(get:channel)]} {
02517         catch {close $ftp(DestCI)}
02518     }
02519         catch {close $ftp(SourceCI)}
02520         catch {un ftp = (state.data)}
02521         DisplayMsg $s $error error
02522 
02523     } elseif { ([eof $ftp(SourceCI)] || ($blocksize <= 0)) } {
02524     /*  Protect the destination channel from destruction if it came*/
02525     /*  from the caller. Closing it is not our responsibility in that case.*/
02526 
02527     if {![info exists ftp(get:channel)]} {
02528         close $ftp(DestCI)
02529     }
02530         close $ftp(SourceCI)
02531         catch {un ftp = (state.data)}
02532         if { $VERBOSE } {
02533             DisplayMsg $s "D: Port closed" data
02534         }
02535 
02536     } else {
02537     fcopy $ftp(SourceCI) $ftp(DestCI) \
02538         -command [list [namespace current]::CopyNext $s] \
02539         -size $blocksize
02540     }
02541     return
02542 }
02543 
02544 /* */
02545 /* */
02546 /*  HandleData --*/
02547 /* */
02548 /*  Handles ascii/binary data transfer for Put and Get */
02549 /*  */
02550 /*  Arguments:*/
02551 /*  sock -      socket name (data channel)*/
02552 
02553 ret  ::ftp::HandleData (type s , type sock) {
02554     upvar ::ftp::ftp$s ftp
02555 
02556     # Turn off any fileevent handlers
02557 
02558     fileevent $sock writable {}     
02559     fileevent $sock readable {}
02560 
02561     # create local file for ftp::Get 
02562 
02563     if { [string match "get*" $ftp(State)]  && (!$ftp(inline))} {
02564 
02565     # A channel was specified by the caller. Use that instead of a
02566     # file.
02567 
02568     # SF Bug 1708350 <@>
02569     if {[info exists ftp(get:channel)]} {
02570         set ftp(DestCI) $ftp(get:channel)
02571         set rc 0
02572     } else {
02573         set rc [catch {set ftp(DestCI) [open $ftp(LocalFilename) w]} msg]
02574     }
02575         if { $rc != 0 } {
02576             DisplayMsg $s "$msg" error
02577             return 0
02578         }
02579     # TODO: Use non-blocking I/O
02580         if { [string equal $ftp(Type) "ascii"] } {
02581             fconfigure $ftp(DestCI) -buffering line -blocking 1
02582         } else {
02583             fconfigure $ftp(DestCI) -buffering line -translation binary -blocking 1
02584         }
02585     }   
02586 
02587     # append local file for ftp::Reget 
02588 
02589     if { [string match "reget*" $ftp(State)] } {
02590         set rc [catch {set ftp(DestCI) [open $ftp(LocalFilename) a]} msg]
02591         if { $rc != 0 } {
02592             DisplayMsg $s "$msg" error
02593             return 0
02594         }
02595     # TODO: Use non-blocking I/O
02596         if { [string equal $ftp(Type) "ascii"] } {
02597             fconfigure $ftp(DestCI) -buffering line -blocking 1
02598         } else {
02599             fconfigure $ftp(DestCI) -buffering line -translation binary -blocking 1
02600         }
02601     }   
02602 
02603 
02604     set ftp(Total) 0
02605     set ftp(Start_Time) [clock seconds]
02606      
02607     # calculate blocksize
02608      
02609     if { [ info exists ftp(Bytes_to_go) ] } {
02610             
02611     if { $ftp(Blocksize) <= $ftp(Bytes_to_go) } {
02612         set Blocksize $ftp(Blocksize)
02613     } else {
02614         set Blocksize $ftp(Bytes_to_go)
02615     }
02616     
02617     } else {
02618     set Blocksize $ftp(Blocksize)
02619     }
02620     
02621     # perform fcopy
02622     fcopy $ftp(SourceCI) $ftp(DestCI) \
02623         -command [list [namespace current]::CopyNext $s ] \
02624         -size $Blocksize
02625     return 1
02626 }
02627 
02628 /* */
02629 /* */
02630 /*  HandleList --*/
02631 /* */
02632 /*  Handles ascii data transfer for list commands*/
02633 /*  */
02634 /*  Arguments:*/
02635 /*  sock -      socket name (data channel)*/
02636 
02637 ret  ::ftp::HandleList (type s , type sock) {
02638     upvar ::ftp::ftp$s ftp
02639     variable VERBOSE
02640 
02641     if { ![eof $sock] } {
02642         set buffer [read $sock]
02643         if { $buffer != "" } {
02644             set ftp(List) [append ftp(List) $buffer]
02645         }   
02646     } else {
02647         close $sock
02648         catch {unset ftp(state.data)}
02649         if { $VERBOSE } {
02650             DisplayMsg $s "D: Port closed" data
02651         }
02652     }
02653     return
02654 }
02655 
02656 /* */
02657 /* */
02658 /*  HandleVar --*/
02659 /* */
02660 /*  Handles data transfer for get/put commands that use buffers instead*/
02661 /*  of files.*/
02662 /*  */
02663 /*  Arguments:*/
02664 /*  sock -      socket name (data channel)*/
02665 
02666 ret  ::ftp::HandleVar (type s , type sock) {
02667     upvar ::ftp::ftp$s ftp
02668     variable VERBOSE
02669 
02670     if {$ftp(Start_Time) == -1} {
02671         set ftp(Start_Time) [clock seconds]
02672     }
02673 
02674     if { ![eof $sock] } {
02675         set buffer [read $sock]
02676         if { $buffer != "" } {
02677             append ftp(GetData) $buffer
02678             incr ftp(Total) [string length $buffer]
02679         }   
02680     } else {
02681         close $sock
02682         catch {unset ftp(state.data)}
02683         if { $VERBOSE } {
02684             DisplayMsg $s "D: Port closed" data
02685         }
02686     }
02687     return
02688 }
02689 
02690 /* */
02691 /* */
02692 /*  HandleOutput --*/
02693 /* */
02694 /*  Handles data transfer for get/put commands that use buffers instead*/
02695 /*  of files.*/
02696 /*  */
02697 /*  Arguments:*/
02698 /*  sock -      socket name (data channel)*/
02699 
02700 ret  ::ftp::HandleOutput (type s , type sock) {
02701     upvar ::ftp::ftp$s ftp
02702     variable VERBOSE
02703 
02704     if {$ftp(Start_Time) == -1} {
02705         set ftp(Start_Time) [clock seconds]
02706     }
02707 
02708     if { $ftp(Total) < [string length $ftp(PutData)] } {
02709         set substr [string range $ftp(PutData) $ftp(Total) \
02710                 [expr {$ftp(Total) + $ftp(Blocksize)}]]
02711         if {[catch {puts -nonewline $sock "$substr"} result]} {
02712             close $sock
02713             catch {unset ftp(state.data)}
02714             if { $VERBOSE } {
02715                 DisplayMsg $s "D: Port closed" data
02716             }
02717         } else {
02718             incr ftp(Total) [string length $substr]
02719         }
02720     } else {
02721         fileevent $sock writable {}     
02722         close $sock
02723         catch {unset ftp(state.data)}
02724         if { $VERBOSE } {
02725             DisplayMsg $s "D: Port closed" data
02726         }
02727     }
02728     return
02729 }
02730 
02731 /* */
02732 /* */
02733 /*  CloseDataConn -- */
02734 /* */
02735 /*  Closes all sockets and files used by the data conection*/
02736 /* */
02737 /*  Arguments:*/
02738 /*  None.*/
02739 /* */
02740 /*  Returns:*/
02741 /*  None.*/
02742 /* */
02743 ret  ::ftp::CloseDataConn (type s ) {
02744     upvar ::ftp::ftp$s ftp
02745 
02746     # Protect the destination channel from destruction if it came
02747     # from the caller. Closing it is not our responsibility.
02748 
02749     if {[info exists ftp(get:channel)]} {
02750     catch {unset ftp(get:channel)}
02751     catch {unset ftp(DestCI)}
02752     }
02753 
02754     catch {after cancel $ftp(Wait)}
02755     catch {fileevent $ftp(DataSock) readable {}}
02756     catch {close $ftp(DataSock); unset ftp(DataSock)}
02757     catch {close $ftp(DestCI); unset ftp(DestCI)} 
02758     catch {close $ftp(SourceCI); unset ftp(SourceCI)}
02759     catch {close $ftp(DummySock); unset ftp(DummySock)}
02760     return
02761 }
02762 
02763 /* */
02764 /* */
02765 /*  InitDataConn --*/
02766 /* */
02767 /*  Configures new data channel for connection to ftp server */
02768 /*  ATTENTION! The new data channel "sock" is not the same as the */
02769 /*  server channel, it's a dummy.*/
02770 /*  */
02771 /*  Arguments:*/
02772 /*  sock -      the name of the new channel*/
02773 /*  addr -      the address, in network address notation, */
02774 /*          of the client's host,*/
02775 /*  port -      the client's port number*/
02776 
02777 ret  ::ftp::InitDataConn (type s , type sock , type addr , type port) {
02778     upvar ::ftp::ftp$s ftp
02779     variable VERBOSE
02780 
02781     # If the new channel is accepted, the dummy channel will be closed
02782 
02783     catch {close $ftp(DummySock); unset ftp(DummySock)}
02784 
02785     set ftp(state.data) 0
02786 
02787     # Configure translation and blocking modes
02788 
02789     set blocking 1
02790     if {[string length $ftp(Command)]} {
02791     set blocking 0
02792     }
02793 
02794     if { [string equal $ftp(Type) "ascii"] } {
02795         fconfigure $sock -buffering line -blocking $blocking
02796     } else {
02797         fconfigure $sock -buffering line -translation binary -blocking $blocking
02798     }
02799 
02800     # assign fileevent handlers, source and destination CI (Channel Identifier)
02801 
02802     # NB: this really does need to be -regexp [PT] 18Mar03
02803     switch -regexp -- $ftp(State) {
02804         list {
02805             fileevent $sock readable [list [namespace current]::HandleList $s $sock]
02806             set ftp(SourceCI) $sock
02807         }
02808         get {
02809             if {$ftp(inline)} {
02810                 set ftp(GetData) ""
02811                 set ftp(Start_Time) -1
02812                 set ftp(Total) 0
02813                 fileevent $sock readable [list [namespace current]::HandleVar $s $sock]
02814         } else {
02815                 fileevent $sock readable [list [namespace current]::HandleData $s $sock]
02816                 set ftp(SourceCI) $sock
02817         }
02818         }
02819         append -
02820         put {
02821             if {$ftp(inline)} {
02822                 set ftp(Start_Time) -1
02823                 set ftp(Total) 0
02824                 fileevent $sock writable [list [namespace current]::HandleOutput $s $sock]
02825         } else {
02826                 fileevent $sock writable [list [namespace current]::HandleData $s $sock]
02827                 set ftp(DestCI) $sock
02828         }
02829         }
02830     default {
02831         error "Unknown state \"$ftp(State)\""
02832     }
02833     }
02834 
02835     if { $VERBOSE } {
02836         DisplayMsg $s "D: Connection from $addr:$port" data
02837     }
02838     return
02839 }
02840 
02841 /* */
02842 /* */
02843 /*  OpenActiveConn --*/
02844 /* */
02845 /*  Opens a ftp data connection*/
02846 /*  */
02847 /*  Arguments:*/
02848 /*  None.*/
02849 /*  */
02850 /*  Returns:*/
02851 /*  0 -         no connection*/
02852 /*  1 -             connection established*/
02853 
02854 ret  ::ftp::OpenActiveConn (type s ) {
02855     upvar ::ftp::ftp$s ftp
02856     variable VERBOSE
02857 
02858     # Port address 0 is a dummy used to give the server the responsibility 
02859     # of getting free new port addresses for every data transfer.
02860     
02861     set rc [catch {set ftp(DummySock) [socket -server [list [namespace current]::InitDataConn $s] 0]} msg]
02862     if { $rc != 0 } {
02863         DisplayMsg $s "$msg" error
02864         return 0
02865     }
02866 
02867     # get a new local port address for data transfer and convert it to a format
02868     # which is useable by the PORT command
02869 
02870     set p [lindex [fconfigure $ftp(DummySock) -sockname] 2]
02871     if { $VERBOSE } {
02872         DisplayMsg $s "D: Port is $p" data
02873     }
02874     set ftp(DataPort) "[expr {$p / 256}],[expr {$p % 256}]"
02875 
02876     return 1
02877 }
02878 
02879 /* */
02880 /* */
02881 /*  OpenPassiveConn --*/
02882 /* */
02883 /*  Opens a ftp data connection*/
02884 /*  */
02885 /*  Arguments:*/
02886 /*  buffer - returned line from server control connection */
02887 /*  */
02888 /*  Returns:*/
02889 /*  0 -         no connection*/
02890 /*  1 -             connection established*/
02891 
02892 ret  ::ftp::OpenPassiveConn (type s , type buffer) {
02893     upvar ::ftp::ftp$s ftp
02894 
02895     if { [regexp -- {([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+)} $buffer all a1 a2 a3 a4 p1 p2] } {
02896         set ftp(LocalAddr) "$a1.$a2.$a3.$a4"
02897         set ftp(DataPort) "[expr {$p1 * 256 + $p2}]"
02898 
02899         # establish data connection for passive mode
02900 
02901         set rc [catch {set ftp(DataSock) [socket $ftp(LocalAddr) $ftp(DataPort)]} msg]
02902         if { $rc != 0 } {
02903             DisplayMsg $s "$msg" error
02904             return 0
02905         }
02906 
02907         InitDataConn $s $ftp(DataSock) $ftp(LocalAddr) $ftp(DataPort)
02908         return 1
02909     } else {
02910         return 0
02911     }
02912 }
02913 
02914 /* */
02915 /* */
02916 /*  OpenControlConn --*/
02917 /* */
02918 /*  Opens a ftp control connection*/
02919 /*  */
02920 /*  Arguments:*/
02921 /*  s   connection id*/
02922 /*  block   blocking or non-blocking mode*/
02923 /*  */
02924 /*  Returns:*/
02925 /*  0 -         no connection*/
02926 /*  1 -             connection established*/
02927 
02928 ret  ::ftp::OpenControlConn (type s , optional block =1) {
02929     upvar ::ftp::ftp$s ftp
02930     variable DEBUG
02931     variable VERBOSE
02932 
02933     # open a control channel
02934 
02935     set rc [catch {set ftp(CtrlSock) [socket $ftp(RemoteHost) $ftp(Port)]} msg]
02936     if { $rc != 0 } {
02937         if { $VERBOSE } {
02938             DisplayMsg $s "C: No connection to server!" error
02939         }
02940         if { $DEBUG } {
02941             DisplayMsg $s "[list $msg]" error
02942         }
02943         unset ftp(State)
02944         return 0
02945     }
02946 
02947     # configure control channel
02948 
02949     fconfigure $ftp(CtrlSock) -buffering line -blocking $block -translation {auto crlf}
02950     fileevent $ftp(CtrlSock) readable [list [namespace current]::StateHandler $s $ftp(CtrlSock)]
02951     
02952     # prepare local ip address for PORT command (convert pointed format
02953     # to comma format)
02954 
02955     set ftp(LocalAddr) [lindex [fconfigure $ftp(CtrlSock) -sockname] 0]
02956     set ftp(LocalAddr) [string map {. ,} $ftp(LocalAddr)]
02957 
02958     # report ready message
02959 
02960     set peer [fconfigure $ftp(CtrlSock) -peername]
02961     if { $VERBOSE } {
02962         DisplayMsg $s "C: Connection from [lindex $peer 0]:[lindex $peer 2]" control
02963     }
02964     
02965     return 1
02966 }
02967 
02968 /*  ::ftp::Command --*/
02969 /* */
02970 /*  Wrapper for evaluated user-supplied command callback*/
02971 /* */
02972 /*  Arguments:*/
02973 /*  cb  callback script*/
02974 /*  msg what happened*/
02975 /*  args    additional info*/
02976 /* */
02977 /*  Results:*/
02978 /*  Depends on callback script*/
02979 
02980 ret  ::ftp::Command (type cb , type msg , type args) {
02981     if {[string length $cb]} {
02982     uplevel #0 $cb [list $msg] $args
02983     }
02984 }
02985 
02986 /*  ==================================================================*/
02987 /*  ?????? Hmm, how to do multithreaded for tkcon?*/
02988 /*  added TkCon support*/
02989 /*  TkCon is (c) 1995-2001 Jeffrey Hobbs, http://tkcon.sourceforge.net/*/
02990 /*  started with: tkcon -load ftp*/
02991 if { [string equal [uplevel "/* 0" {info commands tkcon}] "tkcon"] } {*/
02992 
02993     /*  new ftp::List proc makes the output more readable*/
02994     ret  ::ftp::__ftp_ls (type args) {
02995         foreach i [eval ::ftp::List_org $args] {
02996             puts $i
02997         }
02998     }
02999 
03000     /*  rename the original ftp::List procedure*/
03001     rename ::ftp::List ::ftp::List_org
03002 
03003     alias ::ftp::List   ::ftp::__ftp_ls
03004     alias bye       catch {::ftp::Close; exit}
03005 
03006      ::ftp = ::VERBOSE 1
03007      ::ftp = ::DEBUG 0
03008 }
03009 
03010 /*  ==================================================================*/
03011 /*  At last, everything is fine, we can provide the package.*/
03012 
03013 package provide ftp [lindex {Revision: 2.4.8} 1]
03014 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1