/[axel]/branches/2.x/ftp.c
ViewVC logotype

Contents of /branches/2.x/ftp.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations) (download)
Fri Jan 11 11:57:58 2008 UTC (5 years, 5 months ago) by appaji-guest
Original Path: trunk/ftp.c
File MIME type: text/plain
File size: 8147 byte(s)
Import 1.0a
1 /********************************************************************\
2 * Axel -- A lighter download accelerator for Linux and other Unices. *
3 * *
4 * Copyright 2001 Wilmer van der Gaast *
5 \********************************************************************/
6
7 /* FTP control file */
8
9 /*
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License with
21 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
22 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23 Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "axel.h"
27
28 int ftp_connect( ftp_t *conn, char *host, int port, char *user, char *pass )
29 {
30 conn->data_fd = -1;
31 conn->message = malloc( MAX_STRING );
32
33 if( ( conn->fd = tcp_connect( host, port, conn->local_if ) ) == -1 )
34 {
35 sprintf( conn->message, _("Unable to connect to server %s:%i\n"), host, port );
36 return( 0 );
37 }
38
39 if( ftp_wait( conn ) / 100 != 2 )
40 return( 0 );
41
42 ftp_command( conn, "USER %s", user );
43 if( ftp_wait( conn ) / 100 != 2 )
44 {
45 if( conn->status / 100 == 3 )
46 {
47 ftp_command( conn, "PASS %s", pass );
48 if( ftp_wait( conn ) / 100 != 2 )
49 return( 0 );
50 }
51 else
52 {
53 return( 0 );
54 }
55 }
56
57 /* ASCII mode sucks. Just use Binary.. */
58 ftp_command( conn, "TYPE I" );
59 if( ftp_wait( conn ) / 100 != 2 )
60 return( 0 );
61
62 return( 1 );
63 }
64
65 void ftp_disconnect( ftp_t *conn )
66 {
67 if( conn->fd > 0 )
68 close( conn->fd );
69 if( conn->data_fd > 0 )
70 close( conn->data_fd );
71 if( conn->message )
72 {
73 free( conn->message );
74 conn->message = NULL;
75 }
76
77 *conn->cwd = 0;
78 conn->fd = conn->data_fd = -1;
79 }
80
81 /* Change current working directory */
82 int ftp_cwd( ftp_t *conn, char *cwd )
83 {
84 /* Necessary at all? */
85 if( strncmp( conn->cwd, cwd, MAX_STRING ) == 0 )
86 return( 1 );
87
88 ftp_command( conn, "CWD %s", cwd );
89 if( ftp_wait( conn ) / 100 != 2 )
90 return( 0 );
91
92 strncpy( conn->cwd, cwd, MAX_STRING );
93
94 return( 1 );
95 }
96
97 /* Get file size. Should work with all reasonable servers now */
98 int ftp_size( ftp_t *conn, char *file, int maxredir )
99 {
100 int i, j, size = MAX_STRING;
101 char *reply, *s, fn[MAX_STRING];
102
103 /* Try the SIZE command first, if possible */
104 if( !strchr( file, '*' ) && !strchr( file, '?' ) )
105 {
106 ftp_command( conn, "SIZE %s", file );
107 if( ftp_wait( conn ) / 100 == 2 )
108 {
109 sscanf( conn->message, "%*i %i", &i );
110 return( i );
111 }
112 else if( conn->status / 10 != 50 )
113 {
114 sprintf( conn->message, _("File not found.\n") );
115 return( -1 );
116 }
117 }
118
119 if( maxredir == 0 )
120 {
121 sprintf( conn->message, _("Too many redirects.\n") );
122 return( -1 );
123 }
124
125 if( !ftp_data( conn ) )
126 return( -1 );
127
128 ftp_command( conn, "LIST %s", file );
129 if( ftp_wait( conn ) / 100 != 1 )
130 return( -1 );
131
132 /* Read reply from the server. */
133 reply = malloc( size );
134 memset( reply, 0, size );
135 *reply = '\n';
136 i = 1;
137 while( ( j = read( conn->data_fd, reply + i, size - i - 3 ) ) > 0 )
138 {
139 i += j;
140 reply[i] = 0;
141 if( size - i <= 10 )
142 {
143 size *= 2;
144 reply = realloc( reply, size );
145 memset( reply + size / 2, 0, size / 2 );
146 }
147 }
148 close( conn->data_fd );
149 conn->data_fd = -1;
150
151 if( ftp_wait( conn ) / 100 != 2 )
152 {
153 free( reply );
154 return( -1 );
155 }
156
157 #ifdef DEBUG
158 fprintf( stderr, reply );
159 #endif
160
161 /* Count the number of probably legal matches: Files&Links only */
162 j = 0;
163 for( i = 1; reply[i] && reply[i+1]; i ++ )
164 if( reply[i] == '-' || reply[i] == 'l' )
165 j ++;
166 else
167 while( reply[i] != '\n' && reply[i] )
168 i ++;
169
170 /* No match or more than one match */
171 if( j != 1 )
172 {
173 if( j == 0 )
174 sprintf( conn->message, _("File not found.\n") );
175 else
176 sprintf( conn->message, _("Multiple matches for this URL.\n") );
177 free( reply );
178 return( -1 );
179 }
180
181 /* Symlink handling */
182 if( ( s = strstr( reply, "\nl" ) ) != NULL )
183 {
184 /* Get the real filename */
185 sscanf( s, "%*s %*i %*s %*s %*i %*s %*i %*s %100s", fn );
186 strcpy( file, fn );
187
188 /* Get size of the file linked to */
189 strncpy( fn, strstr( s, "->" ) + 3, MAX_STRING );
190 free( reply );
191 if( ( reply = strchr( fn, '\r' ) ) != NULL )
192 *reply = 0;
193 if( ( reply = strchr( fn, '\n' ) ) != NULL )
194 *reply = 0;
195 return( ftp_size( conn, fn, maxredir - 1 ) );
196 }
197 /* Normal file, so read the size! And read filename because of
198 possible wildcards. */
199 else
200 {
201 s = strstr( reply, "\n-" );
202 i = sscanf( s, "%*s %*i %*s %*s %i %*s %*i %*s %100s", &size, fn );
203 if( i < 2 )
204 {
205 i = sscanf( s, "%*s %*i %i %*i %*s %*i %*i %100s", &size, fn );
206 if( i < 2 )
207 {
208 return( -2 );
209 }
210 }
211 strcpy( file, fn );
212
213 free( reply );
214 return( size );
215 }
216 }
217
218 /* Open a data connection. Only Passive mode supported yet, easier.. */
219 int ftp_data( ftp_t *conn )
220 {
221 int i, info[6];
222 char host[MAX_STRING];
223
224 /* Already done? */
225 if( conn->data_fd > 0 )
226 return( 0 );
227
228 /* if( conn->ftp_mode == FTP_PASSIVE )
229 {
230 */ ftp_command( conn, "PASV" );
231 if( ftp_wait( conn ) / 100 != 2 )
232 return( 0 );
233 *host = 0;
234 for( i = 0; conn->message[i]; i ++ )
235 {
236 if( sscanf( &conn->message[i], "%i,%i,%i,%i,%i,%i",
237 &info[0], &info[1], &info[2], &info[3],
238 &info[4], &info[5] ) == 6 )
239 {
240 sprintf( host, "%i.%i.%i.%i",
241 info[0], info[1], info[2], info[3] );
242 break;
243 }
244 }
245 if( !*host )
246 {
247 sprintf( conn->message, _("Error opening passive data connection.\n") );
248 return( 0 );
249 }
250 if( ( conn->data_fd = tcp_connect( host,
251 info[4] * 256 + info[5], conn->local_if ) ) == -1 )
252 {
253 sprintf( conn->message, _("Error opening passive data connection.\n") );
254 return( 0 );
255 }
256
257 return( 1 );
258 /* }
259 else
260 {
261 sprintf( conn->message, _("Active FTP not implemented yet.\n" ) );
262 return( 0 );
263 } */
264 }
265
266 /* Send a command to the server */
267 int ftp_command( ftp_t *conn, char *format, ... )
268 {
269 va_list params;
270 char cmd[MAX_STRING];
271
272 va_start( params, format );
273 vsnprintf( cmd, MAX_STRING - 3, format, params );
274 strcat( cmd, "\r\n" );
275 va_end( params );
276
277 #ifdef DEBUG
278 fprintf( stderr, "fd(%i)<--%s", conn->fd, cmd );
279 #endif
280
281 if( write( conn->fd, cmd, strlen( cmd ) ) != strlen( cmd ) )
282 {
283 sprintf( conn->message, _("Error writing command %s\n"), format );
284 return( 0 );
285 }
286 else
287 {
288 return( 1 );
289 }
290 }
291
292 /* Read status from server. Should handle multi-line replies correctly.
293 Multi-line replies suck... */
294 int ftp_wait( ftp_t *conn )
295 {
296 int size = MAX_STRING, r = 0, complete, i, j;
297 char *s;
298
299 conn->message = realloc( conn->message, size );
300
301 do
302 {
303 do
304 {
305 r += i = read( conn->fd, conn->message + r, 1 );
306 if( i <= 0 )
307 {
308 sprintf( conn->message, _("Connection gone.\n") );
309 return( -1 );
310 }
311 if( ( r + 10 ) >= size )
312 {
313 size += MAX_STRING;
314 conn->message = realloc( conn->message, size );
315 }
316 }
317 while( conn->message[r-1] != '\n' );
318 conn->message[r] = 0;
319 sscanf( conn->message, "%i", &conn->status );
320 if( conn->message[3] == ' ' )
321 complete = 1;
322 else
323 complete = 0;
324
325 for( i = 0; conn->message[i]; i ++ ) if( conn->message[i] == '\n' )
326 {
327 if( complete == 1 )
328 {
329 complete = 2;
330 break;
331 }
332 if( conn->message[i+4] == ' ' )
333 {
334 j = -1;
335 sscanf( &conn->message[i+1], "%3i", &j );
336 if( j == conn->status )
337 complete = 1;
338 }
339 }
340 }
341 while( complete != 2 );
342
343 #ifdef DEBUG
344 fprintf( stderr, "fd(%i)-->%s", conn->fd, conn->message );
345 #endif
346
347 if( ( s = strchr( conn->message, '\n' ) ) != NULL )
348 *s = 0;
349 if( ( s = strchr( conn->message, '\r' ) ) != NULL )
350 *s = 0;
351 conn->message = realloc( conn->message, max( strlen( conn->message ) + 1, MAX_STRING ) );
352
353 return( conn->status );
354 }

  ViewVC Help
Powered by ViewVC 1.1.5