1 /*- 2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $Id: ftp.c,v 1.1.1.1 1998/07/09 16:52:42 des Exp $ 29 */ 30 31 /* 32 * Portions of this code were taken from or based on ftpio.c: 33 * 34 * ---------------------------------------------------------------------------- 35 * "THE BEER-WARE LICENSE" (Revision 42): 36 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you 37 * can do whatever you want with this stuff. If we meet some day, and you think 38 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 39 * ---------------------------------------------------------------------------- 40 * 41 * Major Changelog: 42 * 43 * Dag-Erling Co�dan Sm�rgrav 44 * 9 Jun 1998 45 * 46 * Incorporated into libfetch 47 * 48 * Jordan K. Hubbard 49 * 17 Jan 1996 50 * 51 * Turned inside out. Now returns xfers as new file ids, not as a special 52 * `state' of FTP_t 53 * 54 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 55 * 56 */ 57 58 #include <sys/types.h> 59 #include <sys/socket.h> 60 #include <netinet/in.h> 61 #include <sys/errno.h> 62 63 #include <ctype.h> 64 #include <errno.h> 65 #include <netdb.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 71 #include "fetch.h" 72 #include "ftperr.c" 73 74 #define FTP_DEFAULT_TO_ANONYMOUS 75 #define FTP_ANONYMOUS_USER "ftp" 76 #define FTP_ANONYMOUS_PASSWORD "ftp" 77 78 #define ENDL "\r\n" 79 80 static url_t cached_host; 81 static FILE *cached_socket; 82 83 #ifndef NDEBUG 84 #define TRACE fprintf(stderr, "TRACE on line %d in " __FILE__ "\n", __LINE__); 85 #else 86 #define TRACE 87 #endif 88 89 /* 90 * Map error code to string 91 */ 92 static const char * 93 _ftp_errstring(int e) 94 { 95 struct ftperr *p = _ftp_errlist; 96 97 while ((p->num != -1) && (p->num != e)) 98 p++; 99 100 return p->string; 101 } 102 103 /* 104 * Set error code 105 */ 106 static void 107 _ftp_seterr(int e) 108 { 109 fetchLastErrCode = e; 110 fetchLastErrText = _ftp_errstring(e); 111 } 112 113 /* 114 * Set error code according to errno 115 */ 116 static void 117 _ftp_syserr(void) 118 { 119 fetchLastErrCode = errno; 120 fetchLastErrText = strerror(errno); 121 } 122 123 /* 124 * Get server response, check that first digit is a '2' 125 */ 126 static int 127 _ftp_chkerr(FILE *s, int *e) 128 { 129 char *line; 130 size_t len; 131 132 TRACE; 133 134 if (e) 135 *e = 0; 136 137 do { 138 if (((line = fgetln(s, &len)) == NULL) || (len < 4)) 139 { 140 _ftp_syserr(); 141 return -1; 142 } 143 } while (line[3] == '-'); 144 145 if (!isdigit(line[1]) || !isdigit(line[1]) 146 || !isdigit(line[2]) || (line[3] != ' ')) { 147 _ftp_seterr(-1); 148 return -1; 149 } 150 151 _ftp_seterr((line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0')); 152 153 if (e) 154 *e = fetchLastErrCode; 155 156 return (line[0] == '2') - 1; 157 } 158 159 /* 160 * Change remote working directory 161 */ 162 static int 163 _ftp_cwd(FILE *s, char *dir) 164 { 165 TRACE; 166 167 fprintf(s, "CWD %s\n", dir); 168 if (ferror(s)) { 169 _ftp_syserr(); 170 return -1; 171 } 172 return _ftp_chkerr(s, NULL); /* expecting 250 */ 173 } 174 175 /* 176 * Retrieve file 177 */ 178 static FILE * 179 _ftp_retrieve(FILE *cf, char *file, int pasv) 180 { 181 char *p; 182 183 TRACE; 184 185 /* change directory */ 186 if (((p = strrchr(file, '/')) != NULL) && (p != file)) { 187 *p = 0; 188 if (_ftp_cwd(cf, file) < 0) { 189 *p = '/'; 190 return NULL; 191 } 192 *p++ = '/'; 193 } else { 194 if (_ftp_cwd(cf, "/") < 0) 195 return NULL; 196 } 197 198 /* retrieve file; p now points to file name */ 199 fprintf(stderr, "Arrrgh! No! No! I can't do it! Leave me alone!\n"); 200 return NULL; 201 } 202 203 /* 204 * Store file 205 */ 206 static FILE * 207 _ftp_store(FILE *cf, char *file, int pasv) 208 { 209 TRACE; 210 211 cf = cf; 212 file = file; 213 pasv = pasv; 214 return NULL; 215 } 216 217 /* 218 * Log on to FTP server 219 */ 220 static FILE * 221 _ftp_connect(char *host, int port, char *user, char *pwd) 222 { 223 int sd, e; 224 FILE *f; 225 226 TRACE; 227 228 /* establish control connection */ 229 if ((sd = fetchConnect(host, port)) < 0) { 230 _ftp_syserr(); 231 return NULL; 232 } 233 if ((f = fdopen(sd, "r+")) == NULL) { 234 _ftp_syserr(); 235 goto ouch; 236 } 237 238 /* expect welcome message */ 239 if (_ftp_chkerr(f, NULL) < 0) 240 goto fouch; 241 242 /* send user name and password */ 243 fprintf(f, "USER %s" ENDL, user); 244 _ftp_chkerr(f, &e); 245 if (e == 331) { 246 /* server requested a password */ 247 fprintf(f, "PASS %s" ENDL, pwd); 248 _ftp_chkerr(f, &e); 249 } 250 if (e == 332) { 251 /* server requested an account */ 252 } 253 if (e != 230) /* won't let us near the WaReZ */ 254 goto fouch; 255 256 /* might as well select mode and type at once */ 257 #ifdef FTP_FORCE_STREAM_MODE 258 fprintf(f, "MODE S" ENDL); 259 if (_ftp_chkerr(f, NULL) < 0) 260 goto ouch; 261 #endif 262 fprintf(f, "TYPE I" ENDL); 263 if (_ftp_chkerr(f, NULL) < 0) 264 goto ouch; 265 266 /* done */ 267 return f; 268 269 ouch: 270 close(sd); 271 return NULL; 272 fouch: 273 fclose(f); 274 return NULL; 275 } 276 277 /* 278 * Disconnect from server 279 */ 280 static void 281 _ftp_disconnect(FILE *f) 282 { 283 TRACE; 284 285 fprintf(f, "QUIT" ENDL); 286 _ftp_chkerr(f, NULL); 287 fclose(f); 288 } 289 290 /* 291 * Check if we're already connected 292 */ 293 static int 294 _ftp_isconnected(url_t *url) 295 { 296 TRACE; 297 298 return (cached_socket 299 && (strcmp(url->host, cached_host.host) == 0) 300 && (strcmp(url->user, cached_host.user) == 0) 301 && (strcmp(url->pwd, cached_host.pwd) == 0) 302 && (url->port == cached_host.port)); 303 } 304 305 FILE * 306 fetchGetFTP(url_t *url, char *flags) 307 { 308 FILE *cf = NULL; 309 int e; 310 311 TRACE; 312 313 #ifdef DEFAULT_TO_ANONYMOUS 314 if (!url->user[0]) { 315 strcpy(url->user, FTP_ANONYMOUS_USER); 316 strcpy(url->pwd, FTP_ANONYMOUS_PASSWORD); 317 } 318 #endif 319 320 /* set default port */ 321 if (!url->port) 322 url->port = 21; 323 324 /* try to use previously cached connection */ 325 if (_ftp_isconnected(url)) { 326 fprintf(cached_socket, "PWD" ENDL); 327 _ftp_chkerr(cached_socket, &e); 328 if (e > 0) 329 cf = cached_socket; 330 } 331 332 /* connect to server */ 333 if (!cf) { 334 cf = _ftp_connect(url->host, url->port, url->user, url->pwd); 335 if (!cf) 336 return NULL; 337 if (cached_socket) 338 _ftp_disconnect(cached_socket); 339 cached_socket = cf; 340 memcpy(&cached_host, url, sizeof(url_t)); 341 } 342 343 /* initiate the transfer */ 344 return _ftp_retrieve(cf, url->doc, (flags && strchr(flags, 'p'))); 345 } 346 347 /* 348 * Upload a file. 349 * Hmmm, that's almost an exact duplicate of the above... 350 */ 351 FILE * 352 fetchPutFTP(url_t *url, char *flags) 353 { 354 FILE *cf = NULL; 355 int e; 356 357 #ifdef DEFAULT_TO_ANONYMOUS 358 if (!url->user[0]) { 359 strcpy(url->user, FTP_ANONYMOUS_USER); 360 strcpy(url->pwd, FTP_ANONYMOUS_PASSWORD); 361 } 362 #endif 363 364 /* set default port */ 365 if (!url->port) 366 url->port = 21; 367 368 /* try to use previously cached connection */ 369 if (_ftp_isconnected(url)) { 370 fprintf(cached_socket, "PWD" ENDL); 371 _ftp_chkerr(cached_socket, &e); 372 if (e > 0) 373 cf = cached_socket; 374 } 375 376 /* connect to server */ 377 if (!cf) { 378 cf = _ftp_connect(url->host, url->port, url->user, url->pwd); 379 if (!cf) 380 return NULL; 381 if (cached_socket) 382 _ftp_disconnect(cached_socket); 383 cached_socket = cf; 384 memcpy(&cached_host, url, sizeof(url_t)); 385 } 386 387 388 /* initiate the transfer */ 389 return _ftp_store(cf, url->doc, (flags && strchr(flags, 'p'))); 390 } 391