1 /*-------------------------------------------------------------------------- 2 Copyright 1999, 2000 by Dan Kegel http://www.kegel.com/ 3 See the file COPYING 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 --------------------------------------------------------------------------*/ 19 20 #ifndef ftp_client_pipe_h 21 #define ftp_client_pipe_h 22 23 #include "eclock.h" 24 #include "nbbio.h" 25 #include "ftp_client_proto.h" 26 #include "Poller.h" 27 #include "Sked.h" 28 29 #define ftp_client_pipe_LINELEN 500 30 31 class ftp_client_pipe_datainterface_t; 32 33 /**-------------------------------------------------------------------------- 34 Module to handle the networking calls for the client side of the FTP 35 protocol. Delegates the work of parsing and generating messages to 36 the ftp_client_protocol module. 37 --------------------------------------------------------------------------*/ 38 39 class ftp_client_pipe_t : public SkedClient, Poller::Client { 40 enum state_t { IDLE, GETTING, SLEEPING, PUTTING }; 41 state_t m_state; 42 43 /// control socket's file descriptor, or -1 44 int m_cfd; 45 46 /// whether nonblocking connect() in progress on cfd 47 bool m_cfd_connecting; 48 49 /// data socket's file descriptor, or -1 50 int m_dfd; 51 52 // whether nonblocking connect() in progress on dfd 53 bool m_dfd_connecting; 54 55 /// A circular buffer for writing data to m_cfd 56 nbbio m_obuf; 57 58 /// A circular buffer for reading data from m_cfd 59 nbbio m_ibuf; 60 61 /// A line buffer for use with m_ibuf.readline() 62 char m_iline[ftp_client_pipe_LINELEN+1]; // +1 for NUL 63 /// true if unused line ready in m_iline 64 bool m_iline_full; 65 66 /* 67 Bandwidth usage throttling. Roughly, after each read, 68 a dead time is set up equal to the min desired interval for 69 that many bits to be read, and the next read is not allowed 70 to happen until that dead time has passed. 71 Only reads are throttled. Only the data channel is throttled yet. 72 At 28kbits/sec, and MTU of 700 bytes, each TU takes 5600/28 73 = 200 msec. If eclock_hertz is 100, that's 20 ticks, which is ok. 74 At 300kbits/sec, and MTU of 1500 bytes, each TU takes 1500*8/300 75 = 40 ms, or 4 ticks, which is a little too coarse for comfort. 76 Throttling should not be used for speeds above 64kbits/sec until we 77 implement fractional tick accumulation. 78 */ 79 80 /// How many bytes to read per eclock tick 81 int m_bytesPerTick; 82 /// How many bytes to read per second 83 int m_bytesPerSec; 84 /// How many bytes from last read we haven't slept for 85 int m_bytesUnsleptFor; 86 /// scheduler to manage sleeping 87 Sked *m_sked; 88 /// events accumulated since we fell asleep 89 short m_dfd_events; 90 /// desired wake up time 91 clock_t m_wakeup; 92 93 /** m_datainterface is what generates and consumes ftp payload data 94 * on our command; it's implemented by the app. 95 * We simply set up connections and call him to transfer each chunk. 96 */ 97 ftp_client_pipe_datainterface_t *m_datainterface; 98 99 /// Kludge: avoid recursive calls to m_datainterface->ftpCmdDone() 100 int m_in_ftpCmdDone; 101 102 /** 103 Call m_datainterface->ftpCmdDone() if not already in it; avoids re-entry. 104 Ah, the joys of immediate callbacks. 105 */ 106 void call_ftpCmdDone(int xerr, int status, const char *statusbuf); 107 108 /// Shutdown and notify the client that we have an error. shutdownAndNotify()109 void shutdownAndNotify() { 110 shutdown(); 111 call_ftpCmdDone(EPIPE, 0, "shutdown"); 112 } 113 114 /** a reference to the parent's poller */ 115 Poller *m_poller; 116 117 /** 0, or a reference to the local address to use for all connections */ 118 struct sockaddr_in *m_local_addr; 119 120 /** m_proto is what generates and consumes ftp control messages; 121 * it's controlled by the app (via our get(), put(), etc. wrappers). 122 * We simply ferry those messages to and from the server. 123 */ 124 ftp_client_proto_t m_proto; 125 126 /**---------------------------------------------------------------------- 127 Callback function. 128 When the specified time has elapsed, Sked::runAll calls this method, 129 which takes care of any read request posted by a call to handle_io 130 while we were asleep. Any errors that happen during this call 131 are reported via ftpCommandDone(). 132 ----------------------------------------------------------------------*/ 133 void skedCallback(clock_t now); 134 135 /// Call this to get the durn thing to start outputting stuff to server 136 int kickstart(void); 137 138 /// Handle events on the data file descriptor. 139 int notifyPollEvent_dfd(Poller::PollEvent *event); 140 141 /// Handle events on the control file descriptor. 142 int notifyPollEvent_cfd(Poller::PollEvent *event); 143 144 public: 145 /**---------------------------------------------------------------------- 146 Initialize this object, and mark it as not connected. 147 The calling object should implement the ftp_client_pipe_datainterface_t 148 interface and pass a pointer to itself. 149 The remining arguments are a pointer to the shared global scheduler, 150 and the maximum bytes per second to allow this client. 151 After each call to handle_data_read(), this module will use the 152 scheduler to sleep long enough to stay under the specified bandwidth. 153 @param local_addr if not 0, local_addr specifies the local IP address 154 to use for the local side of the connection. Must be stable pointer. 155 ----------------------------------------------------------------------*/ 156 void init(ftp_client_pipe_datainterface_t * datainterface, Sked *sked, 157 int max_bytes_per_sec, Poller *poller, struct sockaddr_in *local_addr); 158 159 /*---------------------------------------------------------------------- 160 Initialize this object and start a connection to the given server. 161 ----------------------------------------------------------------------*/ 162 int connect(const char *hostname, int port); 163 164 /**---------------------------------------------------------------------- 165 Call this when done with the session. 166 Closes the file descriptors. 167 Returns 0 on success, else unix error code. 168 ----------------------------------------------------------------------*/ 169 int shutdown(); 170 171 /**---------------------------------------------------------------------- 172 The operating system has told us that one of our file descriptors 173 is ready for I/O. Deal with it. 174 175 Returns 0 on success, Unix error code on failure. 176 If this returns an error, call shutdown() to close this session. 177 178 This is normally called by the app after a call to poll() or 179 sigtimedwait(), or internally by skedCallback(). 180 ----------------------------------------------------------------------*/ 181 int notifyPollEvent(Poller::PollEvent *event); 182 183 /* Wrappers for protocol routines - these call the protocol 184 * routine, then kickstart the I/O 185 */ 186 /**--------------------------------------------------------------------- 187 Get the status of the last login, quit, cd, ls, or get call. 188 If the operation is still in progress, returns 0. 189 If the operation has succeeded, returns a value between 200 and 299. 190 ASCII version of result is returned in given buffer, if buf != NULL. 191 ---------------------------------------------------------------------*/ getStatus(char * buf,size_t buflen)192 int getStatus(char *buf, size_t buflen) { return m_proto.getStatus(buf, buflen); } 193 194 /**--------------------------------------------------------------------- 195 Log in to the server. 196 datainterface->ftpCmdDone() will be called when done. 197 ---------------------------------------------------------------------*/ 198 int login(const char *username, const char *password); 199 200 /**--------------------------------------------------------------------- 201 Log out from the server. This triggers the QUIT command. 202 datainterface->ftpCmdDone() will be called when done. 203 ---------------------------------------------------------------------*/ 204 int quit(); 205 206 /**--------------------------------------------------------------------- 207 Change directories. 208 If dir is "..", the CDUP command is used instead of CD. 209 datainterface->ftpCmdDone() will be called when done. 210 ---------------------------------------------------------------------*/ 211 int cd(const char *dir); 212 213 /**--------------------------------------------------------------------- 214 Setting transfer type. 215 datainterface->ftpCmdDone() will be called when done. 216 ---------------------------------------------------------------------*/ 217 int type(const char *ttype); 218 219 /**--------------------------------------------------------------------- 220 Retrieve file size. 221 datainterface->ftpCmdDone() will be called when done; the size 222 of the file can be parsed out of the third parameter. 223 ---------------------------------------------------------------------*/ 224 int size(const char *fname); 225 226 /**--------------------------------------------------------------------- 227 List the given directory's contents. 228 If dirname is NULL, the current directory is listed. 229 If passive is true, PASV mode is used, else PORT mode is used. 230 datainterface->ftpCmdDone() will be called when done. 231 ---------------------------------------------------------------------*/ 232 int ls(const char *dirname, bool passive); 233 234 /**--------------------------------------------------------------------- 235 Retrieve the given file's contents. 236 If passive is true, PASV mode is used, else PORT mode is used. 237 datainterface->ftpCmdDone() will be called when done. 238 ---------------------------------------------------------------------*/ 239 int get(const char *fname, bool passive); 240 }; 241 242 /**---------------------------------------------------------------------- 243 Interface that must be implemented by user code to handle FTP data 244 blocks passed to it by this module. 245 246 @see ftp_client_pipe_t 247 ----------------------------------------------------------------------*/ 248 class ftp_client_pipe_datainterface_t { 249 public: 250 /**---------------------------------------------------------------------- 251 Hook for application-supplied function. 252 ftp_client_pipe::handle_io() calls this to tell the app to do 253 a chunk of the current file transfer. 254 255 Function must issue a single read or write on the fd (as appropriate 256 for the call that triggered the transfer). 257 If read reads zero bytes, or a fatal Unix error, the transfer is over. 258 When the transfer is over, this routine must return '0' without 259 closing the file. 260 If the transfer is not over, this routine must return the 261 number of bytes tranferred during this call, or -1 times the Unix 262 error code caused by read(). 263 The app must not call any ftp_client_pipe_t method inside this callback. 264 On success, returns the number of bytes transferred, or 0 for EOF. 265 On error, returns -1 times the Unix error code. 266 Returning -EWOULDBLOCK will not terminate the transfer. 267 ----------------------------------------------------------------------*/ 268 virtual int handle_data_io(int fd, short revents, clock_t now) = 0; 269 270 /**---------------------------------------------------------------------- 271 Hook for application-supplied function. 272 When any command (cd, ls, get, put, ...) finishes, this function is 273 called to alert the app. 274 275 If xerr is zero, the local part of the command succeeded; the 276 server's numerical response is in status. The app may call getStatus() 277 to retrieve the text of the server's response. 278 If xerr is nonzero, the local part of the command failed (perhaps 279 a data connection could not be established), and the app should 280 give up on this session (by calling shutdown() and starting over). 281 282 The app may call ftp_client_pipe_t methods inside this callback. 283 ----------------------------------------------------------------------*/ 284 virtual void ftpCmdDone(int xerr, int status, const char *statusbuf) = 0; 285 286 /// Get an integer that identifies this object. Used in log messages. getID(void)287 virtual int getID(void) { return 0; } 288 }; 289 #endif 290