Mercurial > gftp.yaz
annotate lib/ssh.c @ 28:b3e16d1f19c9
2002-09-24 Brian Masney <masneyb@gftp.org>
* configure.in - take [external] off of AM_GNU_GETTEXT
* de.po, ja.po, nl.po, no.po, pl.po, sv.po, zh_CN.po - fixed
several format modifiers on some strings
author | masneyb |
---|---|
date | Tue, 24 Sep 2002 13:00:16 +0000 |
parents | 83090328581e |
children | c8ec7877432e |
rev | line source |
---|---|
1 | 1 /*****************************************************************************/ |
2 /* ssh.c - functions that will use the ssh protocol */ | |
3 /* Copyright (C) 1998-2002 Brian Masney <masneyb@gftp.org> */ | |
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 USA */ | |
18 /*****************************************************************************/ | |
19 | |
20 /* This will use Brian Wellington <bwelling@xbill.org>'s sftpserv program */ | |
21 /* on the remote. Some of this code is derived from the sftp client */ | |
22 | |
23 #include "gftp.h" | |
24 | |
25 #define CHDIR 10 | |
26 #define GETDIR 11 | |
27 #define TELLDIR 12 | |
28 #define SENDFILE 15 | |
29 #define NOFILEMATCH 16 | |
30 #define FILESIZE 20 | |
31 #define FILEMODE 21 | |
32 #define DATA 22 | |
33 #define ENDDATA 23 | |
34 #define FILEOK 24 | |
35 #define STREAM 25 | |
36 #define REQUEST 26 | |
37 #define FILENAME 27 | |
38 #define EXEC 28 | |
39 #define SKIPBYTES 29 | |
40 #define ERROR 30 | |
41 #define SUCCESS 31 | |
42 #define CLOSE 32 | |
43 #define SSH_VERSION 33 | |
44 #define CANCEL 34 | |
45 #define FILETIME 40 | |
46 | |
47 typedef struct ssh_parms_tag | |
48 { | |
49 char *buffer, /* The buffer we are reading the data from */ | |
50 *pos; /* Our position in this buffer */ | |
51 int enddata; /* Are we done reading the data */ | |
52 char channel; /* Data channel we are writing to */ | |
53 } ssh_parms; | |
54 | |
55 | |
56 typedef struct ssh_message_tag | |
57 { | |
58 char channel, | |
59 command; | |
60 gint32 len; | |
61 void *data; | |
62 } ssh_message; | |
63 | |
64 | |
65 static void ssh_destroy ( gftp_request * request ); | |
66 static int ssh_connect ( gftp_request * request ); | |
67 static void ssh_disconnect ( gftp_request * request ); | |
68 static long ssh_get_file ( gftp_request * request, | |
69 const char *filename, | |
70 FILE * fd, | |
71 off_t startsize ); | |
72 static int ssh_put_file ( gftp_request * request, | |
73 const char *filename, | |
74 FILE * fd, | |
75 off_t startsize, | |
76 off_t totalsize ); | |
77 static size_t ssh_get_next_file_chunk ( gftp_request * request, | |
78 char *buf, | |
79 size_t size ); | |
80 static size_t ssh_put_next_file_chunk ( gftp_request * request, | |
81 char *buf, | |
82 size_t size ); | |
83 static int ssh_end_transfer ( gftp_request * request ); | |
84 static int ssh_list_files ( gftp_request * request ); | |
85 static int ssh_get_next_file ( gftp_request * request, | |
86 gftp_file * fle, | |
87 FILE * fd ); | |
88 static int ssh_chdir ( gftp_request * request, | |
89 const char *directory ); | |
90 static char *ssh_exec ( gftp_request * request, | |
91 const char *command, | |
92 size_t len); | |
93 static int ssh_rmdir ( gftp_request * request, | |
94 const char *directory ); | |
95 static int ssh_rmfile ( gftp_request * request, | |
96 const char *file ); | |
97 static int ssh_mkdir ( gftp_request * request, | |
98 const char *newdir ); | |
99 static int ssh_rename ( gftp_request * request, | |
100 const char *oldname, | |
101 const char *newname ); | |
102 static int ssh_chmod ( gftp_request * request, | |
103 const char *file, | |
104 int mode ); | |
105 static int ssh_send_command ( gftp_request * request, | |
106 int cmdnum, | |
107 const char *command, | |
108 size_t len ); | |
109 static int ssh_read_response ( gftp_request * request, | |
110 ssh_message *message, | |
111 FILE * fd ); | |
112 static char *ssh_read_message ( gftp_request * request, | |
113 char *buf, | |
114 size_t len, | |
115 FILE * fd ); | |
116 static char *ssh_read_line ( gftp_request * request ); | |
117 static void ssh_log_command ( gftp_request * request, | |
118 int channel, | |
119 int cmdnum, | |
120 const char *command, | |
121 size_t len, | |
122 int direction ); | |
123 static size_t ssh_remove_spaces ( char *string ); | |
124 | |
125 void | |
126 ssh_init (gftp_request * request) | |
127 { | |
128 g_return_if_fail (request != NULL); | |
129 | |
130 request->protonum = GFTP_SSH_NUM; | |
131 request->init = ssh_init; | |
132 request->destroy = ssh_destroy; | |
133 request->connect = ssh_connect; | |
134 request->disconnect = ssh_disconnect; | |
135 request->get_file = ssh_get_file; | |
136 request->put_file = ssh_put_file; | |
137 request->transfer_file = NULL; | |
138 request->get_next_file_chunk = ssh_get_next_file_chunk; | |
139 request->put_next_file_chunk = ssh_put_next_file_chunk; | |
140 request->end_transfer = ssh_end_transfer; | |
141 request->list_files = ssh_list_files; | |
142 request->get_next_file = ssh_get_next_file; | |
143 request->set_data_type = NULL; | |
144 request->get_file_size = NULL; | |
145 request->chdir = ssh_chdir; | |
146 request->rmdir = ssh_rmdir; | |
147 request->rmfile = ssh_rmfile; | |
148 request->mkdir = ssh_mkdir; | |
149 request->rename = ssh_rename; | |
150 request->chmod = ssh_chmod; | |
151 request->set_file_time = NULL; | |
152 request->site = NULL; | |
153 request->parse_url = NULL; | |
154 request->url_prefix = "ssh"; | |
155 request->protocol_name = "SSH"; | |
156 request->need_hostport = 1; | |
157 request->need_userpass = ssh_need_userpass; | |
158 request->use_cache = 1; | |
159 request->use_threads = 1; | |
160 request->always_connected = 0; | |
161 request->protocol_data = g_malloc0 (sizeof (ssh_parms)); | |
162 gftp_set_config_options (request); | |
163 } | |
164 | |
165 | |
166 static void | |
167 ssh_destroy (gftp_request * request) | |
168 { | |
169 ssh_parms *params; | |
170 | |
171 g_return_if_fail (request != NULL); | |
172 g_return_if_fail (request->protonum == GFTP_SSH_NUM); | |
173 | |
174 params = request->protocol_data; | |
175 if (params->buffer) | |
176 { | |
177 g_free (params->buffer); | |
178 params->buffer = params->pos = NULL; | |
179 } | |
180 } | |
181 | |
182 | |
183 static int | |
184 ssh_connect (gftp_request * request) | |
185 { | |
186 char **args, *tempstr, pts_name[20], *p1, p2, *exepath, port[6]; | |
187 int version, fdm, fds, s[2]; | |
188 ssh_message message; | |
189 const gchar *errstr; | |
190 ssh_parms *params; | |
191 pid_t child; | |
192 | |
193 g_return_val_if_fail (request != NULL, -2); | |
194 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
195 g_return_val_if_fail (request->hostname != NULL, -2); | |
196 | |
197 params = request->protocol_data; | |
198 if (request->sockfd != NULL) | |
199 return (0); | |
200 | |
201 request->logging_function (gftp_logging_misc, request->user_data, | |
202 _("Opening SSH connection to %s\n"), | |
203 request->hostname); | |
204 | |
205 if (request->sftpserv_path == NULL || | |
206 *request->sftpserv_path == '\0') | |
207 { | |
208 p1 = ""; | |
209 p2 = ' '; | |
210 } | |
211 else | |
212 { | |
213 p1 = request->sftpserv_path; | |
214 p2 = '/'; | |
215 } | |
216 | |
217 *port = '\0'; | |
218 exepath = g_strdup_printf ("%s%csftpserv", p1, p2); | |
219 args = make_ssh_exec_args (request, exepath, 0, port); | |
220 | |
221 if (ssh_use_askpass) | |
222 { | |
223 fdm = fds = 0; | |
224 if (socketpair (AF_LOCAL, SOCK_STREAM, 0, s) < 0) | |
225 { | |
226 request->logging_function (gftp_logging_error, request->user_data, | |
227 _("Cannot create a socket pair: %s\n"), | |
228 g_strerror (errno)); | |
229 return (-2); | |
230 } | |
231 } | |
232 else | |
233 { | |
234 s[0] = s[1] = 0; | |
235 if ((fdm = ptym_open (pts_name)) < 0) | |
236 { | |
237 request->logging_function (gftp_logging_error, request->user_data, | |
238 _("Cannot open master pty %s: %s\n"), pts_name, | |
239 g_strerror (errno)); | |
240 return (-2); | |
241 } | |
242 } | |
243 | |
244 if ((child = fork ()) == 0) | |
245 { | |
246 setsid (); | |
247 if (ssh_use_askpass) | |
248 { | |
249 close (s[0]); | |
250 fds = s[1]; | |
251 } | |
252 else | |
253 { | |
254 if ((fds = ptys_open (fdm, pts_name)) < 0) | |
255 { | |
256 printf ("Cannot open slave pts %s: %s\n", pts_name, | |
257 g_strerror (errno)); | |
258 return (-1); | |
259 } | |
260 close (fdm); | |
261 } | |
262 | |
263 dup2 (fds, 0); | |
264 dup2 (fds, 1); | |
265 dup2 (fds, 2); | |
266 if (!ssh_use_askpass && fds > 2) | |
267 close (fds); | |
268 execvp (ssh_prog_name != NULL && *ssh_prog_name != '\0' ? | |
269 ssh_prog_name : "ssh", args); | |
270 | |
271 tempstr = _("Error: Cannot execute ssh: "); | |
272 write (1, tempstr, strlen (tempstr)); | |
273 errstr = g_strerror (errno); | |
274 write (1, errstr, strlen (errstr)); | |
275 write (1, "\n", 1); | |
276 return (-1); | |
277 } | |
278 else if (child > 0) | |
279 { | |
280 if (ssh_use_askpass) | |
281 { | |
282 close (s[1]); | |
283 fdm = s[0]; | |
284 } | |
285 tty_raw (fdm); | |
286 tempstr = ssh_start_login_sequence (request, fdm); | |
287 if (!tempstr || | |
288 !(strlen (tempstr) > 4 && strcmp (tempstr + strlen (tempstr) - 5, | |
289 "xsftp") == 0)) | |
290 { | |
291 request->logging_function (gftp_logging_error, request->user_data, | |
292 _("Error: Received wrong init string from server\n")); | |
293 g_free (args); | |
294 g_free (exepath); | |
295 return (-2); | |
296 } | |
297 g_free (args); | |
298 g_free (exepath); | |
299 g_free (tempstr); | |
300 | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
301 if ((request->sockfd = fdopen (fdm, "rb+")) == NULL) |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
302 { |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
303 request->logging_function (gftp_logging_error, request->user_data, |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
304 _("Cannot fdopen() socket: %s\n"), |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
305 g_strerror (errno)); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
306 close (fdm); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
307 return (-2); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
308 } |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
309 |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
310 if ((request->sockfd_write = fdopen (fdm, "wb+")) == NULL) |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
311 { |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
312 request->logging_function (gftp_logging_error, request->user_data, |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
313 _("Cannot fdopen() socket: %s\n"), |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
314 g_strerror (errno)); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
315 gftp_disconnect (request); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
316 return (-2); |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
317 } |
1 | 318 |
319 params->channel = 0; | |
320 version = htonl (7 << 4); | |
321 if (ssh_send_command (request, SSH_VERSION, (char *) &version, 4) < 0) | |
322 return (-2); | |
323 if (ssh_read_response (request, &message, NULL) != SSH_VERSION) | |
324 return (-2); | |
325 g_free (message.data); | |
326 | |
327 request->logging_function (gftp_logging_misc, request->user_data, | |
328 _("Successfully logged into SSH server %s\n"), | |
329 request->hostname); | |
330 } | |
331 else | |
332 { | |
333 request->logging_function (gftp_logging_error, request->user_data, | |
334 _("Cannot fork another process: %s\n"), | |
335 g_strerror (errno)); | |
336 g_free (args); | |
337 return (-1); | |
338 } | |
339 | |
340 ssh_chdir (request, request->directory); | |
341 | |
342 if (ssh_send_command (request, GETDIR, NULL, 0) < 0) | |
343 return (-1); | |
344 | |
345 if (ssh_read_response (request, &message, NULL) != TELLDIR) | |
346 { | |
347 request->logging_function (gftp_logging_error, request->user_data, | |
348 _("Could not get current working directory: %s\n"), | |
349 (char *) message.data); | |
350 g_free (message.data); | |
351 return (-1); | |
352 } | |
353 | |
354 if (request->directory) | |
355 g_free (request->directory); | |
356 request->directory = message.data; | |
357 | |
358 if (request->sockfd == NULL) | |
359 return (-2); | |
360 | |
361 return (0); | |
362 } | |
363 | |
364 | |
365 static void | |
366 ssh_disconnect (gftp_request * request) | |
367 { | |
368 g_return_if_fail (request != NULL); | |
369 g_return_if_fail (request->protonum == GFTP_SSH_NUM); | |
370 | |
371 if (request->sockfd != NULL) | |
372 { | |
373 request->logging_function (gftp_logging_misc, request->user_data, | |
374 _("Disconnecting from site %s\n"), | |
375 request->hostname); | |
376 fclose (request->sockfd); | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
377 request->sockfd = request->sockfd_write = NULL; |
1 | 378 } |
379 } | |
380 | |
381 | |
382 static long | |
383 ssh_get_file (gftp_request * request, const char *filename, FILE * fd, | |
384 off_t startsize) | |
385 { | |
386 ssh_message message; | |
387 ssh_parms *params; | |
388 off_t retsize; | |
389 int ret; | |
390 | |
391 g_return_val_if_fail (request != NULL, -2); | |
392 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
393 g_return_val_if_fail (filename != NULL, -2); | |
394 /* fd ignored for this protocol */ | |
395 | |
396 params = request->protocol_data; | |
397 params->channel = 0; | |
398 if (ssh_send_command (request, REQUEST, filename, strlen (filename) + 1) < 0) | |
399 return (-1); | |
400 | |
401 retsize = 0; | |
402 while (1) | |
403 { | |
404 ret = ssh_read_response (request, &message, NULL); | |
405 switch (ret) | |
406 { | |
407 case FILESIZE: | |
408 retsize = strtol ((char *) message.data, NULL, 10); | |
409 break; | |
410 case NOFILEMATCH: | |
411 request->logging_function (gftp_logging_misc, request->user_data, | |
412 _("Remote host could not find file %s\n"), | |
413 filename); | |
414 g_free (message.data); | |
415 return (-1); | |
416 case FILENAME: | |
417 params->channel = message.channel; | |
418 startsize = htonl (startsize); | |
419 if (ssh_send_command (request, SKIPBYTES, (char *) &startsize, 4) < 0) | |
420 { | |
421 g_free (message.data); | |
422 return (-1); | |
423 } | |
424 break; | |
425 case -1: | |
426 g_free (message.data); | |
427 return (-2); | |
428 } | |
429 | |
430 g_free (message.data); | |
431 if (ret == FILETIME) | |
432 break; | |
433 } | |
434 | |
435 return (retsize); | |
436 } | |
437 | |
438 | |
439 static int | |
440 ssh_put_file (gftp_request * request, const char *filename, FILE * fd, | |
441 off_t startsize, off_t totalsize) | |
442 { | |
443 ssh_message message; | |
444 ssh_parms *params; | |
445 char tempchar; | |
446 | |
447 g_return_val_if_fail (request != NULL, -2); | |
448 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
449 g_return_val_if_fail (filename != NULL, -2); | |
450 /* fd ignored for this protocol */ | |
451 | |
452 params = request->protocol_data; | |
453 params->channel = 0; | |
454 tempchar = 1; | |
455 if (ssh_send_command (request, SENDFILE, &tempchar, 1) < 0) | |
456 return (-1); | |
457 | |
458 params->channel = tempchar; | |
459 if (ssh_send_command (request, FILENAME, filename, strlen (filename) + 1) < 0) | |
460 return (-1); | |
461 | |
462 if (ssh_read_response (request, &message, NULL) < 0) | |
463 { | |
464 g_free (message.data); | |
465 return (-1); | |
466 } | |
467 | |
468 if (!(message.command == FILEOK && message.len == 1 | |
469 && *(char *) message.data != 0)) | |
470 { | |
471 g_free (message.data); | |
472 return (-1); | |
473 } | |
474 g_free (message.data); | |
475 | |
476 if (ssh_send_command (request, FILESIZE, (void *) &totalsize, 4) < 0) | |
477 return (-1); | |
478 | |
479 if (ssh_send_command (request, FILEMODE, "rw-r--r--", 9) < 0) | |
480 return (-1); | |
481 | |
482 if (ssh_send_command (request, FILETIME, (char *) 0, 4) < 0) | |
483 return (-1); | |
484 | |
485 if (startsize > 0) | |
486 { | |
487 startsize = htonl (startsize); | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
488 /* This protocol only supports files up to 2 gig in size. I truncate |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
489 the file size here just to suppress compiler warnings */ |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
490 if (ssh_send_command (request, SKIPBYTES, |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
1
diff
changeset
|
491 GINT_TO_POINTER ((gint32) startsize), 4) < 0) |
1 | 492 return (-1); |
493 } | |
494 | |
495 return (0); | |
496 } | |
497 | |
498 | |
499 static size_t | |
500 ssh_get_next_file_chunk (gftp_request * request, char *buf, size_t size) | |
501 { | |
502 ssh_message message; | |
503 size_t len; | |
504 | |
505 switch (ssh_read_response (request, &message, NULL)) | |
506 { | |
507 case DATA: | |
508 len = size > message.len ? message.len : size; | |
509 memcpy (buf, message.data, len); | |
510 g_free (message.data); | |
511 return (len); | |
512 case ENDDATA: | |
513 g_free (message.data); | |
514 if (ssh_read_response (request, &message, NULL) < 0) | |
515 { | |
516 g_free (message.data); | |
517 return (-1); | |
518 } | |
519 g_free (message.data); | |
520 return (0); | |
521 case -1: | |
522 g_free (message.data); | |
523 return (-1); | |
524 default: | |
525 g_free (message.data); | |
526 request->logging_function (gftp_logging_misc, request->user_data, | |
527 _("Received unexpected response from server\n")); | |
528 gftp_disconnect (request); | |
529 return (0); | |
530 } | |
531 } | |
532 | |
533 | |
534 static size_t | |
535 ssh_put_next_file_chunk (gftp_request * request, char *buf, size_t size) | |
536 { | |
537 if (size == 0) | |
538 { | |
539 if (ssh_send_command (request, ENDDATA, NULL, 0) < 0) | |
540 return (-2); | |
541 if (ssh_send_command (request, NOFILEMATCH, NULL, 0) < 0) | |
542 return (-2); | |
543 return (0); | |
544 } | |
545 return (ssh_send_command (request, DATA, buf, size) == 0 ? size : 0); | |
546 } | |
547 | |
548 | |
549 static int | |
550 ssh_end_transfer (gftp_request * request) | |
551 { | |
552 ssh_parms *params; | |
553 | |
554 g_return_val_if_fail (request != NULL, -2); | |
555 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
556 | |
557 params = request->protocol_data; | |
558 if (params->buffer) | |
559 g_free (params->buffer); | |
560 params->buffer = params->pos = NULL; | |
561 params->enddata = 0; | |
562 | |
563 return (0); | |
564 } | |
565 | |
566 | |
567 static int | |
568 ssh_list_files (gftp_request * request) | |
569 { | |
570 ssh_message message; | |
571 ssh_parms *params; | |
572 char *tempstr; | |
573 size_t len; | |
574 | |
575 g_return_val_if_fail (request != NULL, -2); | |
576 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
577 | |
578 params = request->protocol_data; | |
579 params->enddata = 0; | |
580 params->channel = 0; | |
581 request->logging_function (gftp_logging_misc, request->user_data, | |
582 _("Retrieving directory listing...\n")); | |
583 | |
584 tempstr = g_strconcat ("/bin/ls -al", NULL); | |
585 len = ssh_remove_spaces (tempstr); | |
586 | |
587 if (ssh_send_command (request, EXEC, tempstr, len) < 0) | |
588 { | |
589 g_free (tempstr); | |
590 return (-1); | |
591 } | |
592 g_free (tempstr); | |
593 | |
594 if (ssh_read_response (request, &message, NULL) != STREAM) | |
595 { | |
596 g_free (message.data); | |
597 request->logging_function (gftp_logging_misc, request->user_data, | |
598 _("Received unexpected response from server\n")); | |
599 gftp_disconnect (request); | |
600 return (-2); | |
601 } | |
602 g_free (message.data); | |
603 | |
604 return (0); | |
605 } | |
606 | |
607 | |
608 static int | |
609 ssh_get_next_file (gftp_request * request, gftp_file * fle, FILE * fd) | |
610 { | |
611 char *tempstr, *pos; | |
612 ssh_message message; | |
613 ssh_parms *params; | |
614 int ret, len; | |
615 | |
616 g_return_val_if_fail (request != NULL, -2); | |
617 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
618 g_return_val_if_fail (fle != NULL, -2); | |
619 | |
620 params = request->protocol_data; | |
621 if (request->last_dir_entry) | |
622 { | |
623 g_free (request->last_dir_entry); | |
624 request->last_dir_entry = NULL; | |
625 request->last_dir_entry_len = 0; | |
626 } | |
627 | |
628 len = 0; | |
629 while (1) | |
630 { | |
631 if (params->enddata && params->buffer == NULL) | |
632 { | |
633 request->logging_function (gftp_logging_misc, request->user_data, | |
634 _("Finished retrieving directory listing\n")); | |
635 params->enddata = 0; | |
636 return (0); | |
637 } | |
638 | |
639 if (!params->enddata && | |
640 (!params->pos || strchr (params->pos, '\n') == NULL)) | |
641 { | |
642 if ((ret = ssh_read_response (request, &message, fd)) < 0) | |
643 { | |
644 if (message.data) | |
645 g_free (message.data); | |
646 return (-2); | |
647 } | |
648 | |
649 if (!request->cached) | |
650 { | |
651 request->last_dir_entry_len = message.len + 6; | |
652 request->last_dir_entry = g_malloc (request->last_dir_entry_len + 1); | |
653 request->last_dir_entry[0] = message.channel; | |
654 request->last_dir_entry[1] = message.command; | |
655 len = htonl (message.len); | |
656 memcpy (&request->last_dir_entry[2], &len, 4); | |
657 memcpy (&request->last_dir_entry[6], message.data, message.len); | |
658 } | |
659 | |
660 if (ret == DATA) | |
661 { | |
662 if (params->pos == NULL || *params->pos == '\0') | |
663 { | |
664 if (params->buffer) | |
665 g_free (params->buffer); | |
666 params->buffer = params->pos = message.data; | |
667 } | |
668 else | |
669 { | |
670 tempstr = params->buffer; | |
671 params->buffer = g_malloc (strlen (params->pos) + | |
672 strlen ((char *) message.data) + 1); | |
673 strcpy (params->buffer, params->pos); | |
674 strcat (params->buffer, (char *) message.data); | |
675 g_free (tempstr); | |
676 g_free (message.data); | |
677 params->pos = params->buffer; | |
678 } | |
679 } | |
680 else | |
681 g_free (message.data); | |
682 } | |
683 else | |
684 ret = DATA; | |
685 | |
686 switch (ret) | |
687 { | |
688 case DATA: | |
689 if ((tempstr = ssh_read_line (request)) == NULL) | |
690 continue; | |
691 pos = tempstr; | |
692 while (*pos == ' ' || *pos == '\t') | |
693 pos++; | |
694 if (*pos == '\0') | |
695 { | |
696 g_free (tempstr); | |
697 break; | |
698 } | |
699 | |
700 if (gftp_parse_ls (tempstr, fle) != 0) | |
701 { | |
702 if (strncmp (tempstr, "total", strlen ("total")) && | |
703 strncmp (tempstr, _("total"), strlen (_("total")))) | |
704 request->logging_function (gftp_logging_misc, | |
705 request->user_data, | |
706 _("Warning: Cannot parse listing %s\n"), | |
707 tempstr); | |
708 gftp_file_destroy (fle); | |
709 g_free (tempstr); | |
710 tempstr = NULL; | |
711 continue; | |
712 } | |
713 len = strlen (tempstr); | |
714 g_free (tempstr); | |
715 break; | |
716 case ENDDATA: | |
717 params->enddata = 1; | |
718 break; | |
719 default: | |
720 request->logging_function (gftp_logging_misc, request->user_data, | |
721 _("Received unexpected response from server\n")); | |
722 gftp_disconnect (request); | |
723 return (-2); | |
724 } | |
725 | |
726 if (ret == DATA) | |
727 break; | |
728 } | |
729 | |
730 return (len); | |
731 } | |
732 | |
733 | |
734 static int | |
735 ssh_chdir (gftp_request * request, const char *directory) | |
736 { | |
737 ssh_message message; | |
738 int ret; | |
739 | |
740 g_return_val_if_fail (request != NULL, -2); | |
741 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
742 | |
743 if (directory != NULL && *directory != '\0') | |
744 { | |
745 if (ssh_send_command (request, CHDIR, directory, | |
746 strlen (directory) + 1) < 0) | |
747 return (-1); | |
748 | |
749 if ((ret = ssh_read_response (request, &message, NULL)) != SUCCESS) | |
750 { | |
751 request->logging_function (gftp_logging_error, request->user_data, | |
752 _("Could not change remote directory to %s: %s\n"), | |
753 directory, (char *) message.data); | |
754 g_free (message.data); | |
755 return (-1); | |
756 } | |
757 g_free (message.data); | |
758 } | |
759 | |
760 if (directory != request->directory) | |
761 { | |
762 if (ssh_send_command (request, GETDIR, NULL, 0) < 0) | |
763 return (-1); | |
764 | |
765 if (ssh_read_response (request, &message, NULL) != TELLDIR) | |
766 { | |
767 request->logging_function (gftp_logging_error, request->user_data, | |
768 _("Could not get current working directory: %s\n"), | |
769 (char *) message.data); | |
770 g_free (message.data); | |
771 return (-1); | |
772 } | |
773 | |
774 if (request->directory) | |
775 g_free (request->directory); | |
776 request->directory = message.data; | |
777 } | |
778 | |
779 return (0); | |
780 } | |
781 | |
782 | |
783 static char * | |
784 ssh_exec (gftp_request * request, const char *command, size_t len) | |
785 { | |
786 ssh_message message; | |
787 char *err, *pos; | |
788 int ret; | |
789 | |
790 g_return_val_if_fail (request != NULL, NULL); | |
791 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, NULL); | |
792 g_return_val_if_fail (command != NULL, NULL); | |
793 | |
794 err = NULL; | |
795 if (ssh_send_command (request, EXEC, command, len) < 0) | |
796 return (NULL); | |
797 | |
798 while (1) | |
799 { | |
800 ret = ssh_read_response (request, &message, NULL); | |
801 switch (ret) | |
802 { | |
803 case -1: | |
804 g_free (message.data); | |
805 gftp_disconnect (request); | |
806 return (message.data); | |
807 case DATA: | |
808 case ERROR: | |
809 pos = (char *) message.data+message.len-1; | |
810 if (*pos == '\n') | |
811 *pos = '\0'; | |
812 | |
813 if (err != NULL) | |
814 { | |
815 err = g_realloc (err, strlen (message.data) + strlen (err) + 1); | |
816 strcat (err, message.data); | |
817 } | |
818 else | |
819 { | |
820 err = g_malloc (strlen (message.data) + 1); | |
821 strcpy (err, message.data); | |
822 } | |
823 if (ret == ERROR) | |
824 { | |
825 g_free (message.data); | |
826 return (err); | |
827 } | |
828 break; | |
829 case SUCCESS: | |
830 g_free (message.data); | |
831 return (err); | |
832 case STREAM: | |
833 break; | |
834 case ENDDATA: | |
835 g_free (message.data); | |
836 return (err); | |
837 } | |
838 g_free (message.data); | |
839 } | |
840 } | |
841 | |
842 | |
843 static int | |
844 ssh_rmdir (gftp_request * request, const char *directory) | |
845 { | |
846 char *tempstr, *pos, *ret; | |
847 size_t len; | |
848 | |
849 g_return_val_if_fail (request != NULL, -2); | |
850 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
851 g_return_val_if_fail (directory != NULL, -2); | |
852 | |
853 tempstr = g_strconcat ("/bin/rmdir ", directory, NULL); | |
854 len = strlen (tempstr); | |
855 pos = tempstr; | |
856 while ((pos = strchr (pos, ' ')) != NULL) | |
857 *pos++ = '\0'; | |
858 if ((ret = ssh_exec (request, tempstr, len)) != NULL) | |
859 { | |
860 request->logging_function (gftp_logging_error, request->user_data, | |
861 _("Error: Could not remove directory %s: %s\n"), | |
862 directory, ret); | |
863 g_free (tempstr); | |
864 g_free (ret); | |
865 return (-2); | |
866 } | |
867 else | |
868 request->logging_function (gftp_logging_misc, request->user_data, | |
869 _("Successfully removed %s\n"), directory); | |
870 g_free (tempstr); | |
871 return (0); | |
872 } | |
873 | |
874 | |
875 static int | |
876 ssh_rmfile (gftp_request * request, const char *file) | |
877 { | |
878 char *tempstr, *pos, *ret; | |
879 size_t len; | |
880 | |
881 g_return_val_if_fail (request != NULL, -2); | |
882 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
883 g_return_val_if_fail (file != NULL, -2); | |
884 | |
885 tempstr = g_strconcat ("/bin/rm -f ", file, NULL); | |
886 len = strlen (tempstr); | |
887 pos = tempstr; | |
888 while ((pos = strchr (pos, ' ')) != NULL) | |
889 *pos++ = '\0'; | |
890 if ((ret = ssh_exec (request, tempstr, len)) != NULL) | |
891 { | |
892 request->logging_function (gftp_logging_error, request->user_data, | |
893 _("Error: Could not remove file %s: %s\n"), | |
894 file, ret); | |
895 g_free (tempstr); | |
896 g_free (ret); | |
897 return (-2); | |
898 } | |
899 else | |
900 request->logging_function (gftp_logging_misc, request->user_data, | |
901 _("Successfully removed %s\n"), file); | |
902 g_free (tempstr); | |
903 return (0); | |
904 } | |
905 | |
906 | |
907 static int | |
908 ssh_mkdir (gftp_request * request, const char *newdir) | |
909 { | |
910 char *tempstr, *pos, *ret; | |
911 size_t len; | |
912 | |
913 g_return_val_if_fail (request != NULL, -2); | |
914 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
915 g_return_val_if_fail (newdir != NULL, -2); | |
916 | |
917 tempstr = g_strconcat ("/bin/mkdir ", newdir, NULL); | |
918 len = strlen (tempstr); | |
919 pos = tempstr; | |
920 while ((pos = strchr (pos, ' ')) != NULL) | |
921 *pos++ = '\0'; | |
922 if ((ret = ssh_exec (request, tempstr, len)) != NULL) | |
923 { | |
924 request->logging_function (gftp_logging_error, request->user_data, | |
925 _("Error: Could not make directory %s: %s\n"), | |
926 newdir, ret); | |
927 g_free (tempstr); | |
928 g_free (ret); | |
929 return (-2); | |
930 } | |
931 else | |
932 request->logging_function (gftp_logging_misc, request->user_data, | |
933 _("Successfully made directory %s\n"), | |
934 newdir); | |
935 g_free (tempstr); | |
936 return (0); | |
937 } | |
938 | |
939 | |
940 static int | |
941 ssh_rename (gftp_request * request, const char *oldname, const char *newname ) | |
942 { | |
943 char *tempstr, *pos, *ret; | |
944 size_t len; | |
945 | |
946 g_return_val_if_fail (request != NULL, -2); | |
947 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
948 g_return_val_if_fail (oldname != NULL, -2); | |
949 g_return_val_if_fail (newname != NULL, -2); | |
950 | |
951 tempstr = g_strconcat ("/bin/mv ", oldname, " ", newname, NULL); | |
952 len = strlen (tempstr); | |
953 pos = tempstr; | |
954 while ((pos = strchr (pos, ' ')) != NULL) | |
955 *pos++ = '\0'; | |
956 if ((ret = ssh_exec (request, tempstr, len)) != NULL) | |
957 { | |
958 request->logging_function (gftp_logging_misc, request->user_data, | |
959 _("Error: Could not rename %s to %s: %s\n"), | |
960 oldname, newname, ret); | |
961 g_free (tempstr); | |
962 g_free (ret); | |
963 return (-2); | |
964 } | |
965 else | |
966 request->logging_function (gftp_logging_misc, request->user_data, | |
967 _("Successfully renamed %s to %s\n"), | |
968 oldname, newname); | |
969 g_free (tempstr); | |
970 return (0); | |
971 } | |
972 | |
973 | |
974 static int | |
975 ssh_chmod (gftp_request * request, const char *file, int mode) | |
976 { | |
977 char *tempstr, *pos, *ret; | |
978 size_t len; | |
979 | |
980 g_return_val_if_fail (request != NULL, -2); | |
981 g_return_val_if_fail (request->protonum == GFTP_SSH_NUM, -2); | |
982 g_return_val_if_fail (file != NULL, -2); | |
983 | |
984 tempstr = g_malloc (strlen (file) + (mode / 10) + 14); | |
985 sprintf (tempstr, "/bin/chmod %d %s", mode, file); | |
986 len = strlen (tempstr); | |
987 pos = tempstr; | |
988 while ((pos = strchr (pos, ' ')) != NULL) | |
989 *pos++ = '\0'; | |
990 if ((ret = ssh_exec (request, tempstr, len)) != NULL) | |
991 { | |
992 request->logging_function (gftp_logging_misc, request->user_data, | |
993 _("Error: Could not change mode of %s to %d: %s\n"), | |
994 file, mode, ret); | |
995 g_free (tempstr); | |
996 g_free (ret); | |
997 return (-2); | |
998 } | |
999 else | |
1000 request->logging_function (gftp_logging_misc, request->user_data, | |
1001 _("Successfully changed mode of %s to %d\n"), | |
1002 file, mode); | |
1003 g_free (tempstr); | |
1004 return (0); | |
1005 } | |
1006 | |
1007 | |
1008 static int | |
1009 ssh_send_command (gftp_request * request, int cmdnum, const char *command, | |
1010 size_t len) | |
1011 { | |
1012 ssize_t wrote; | |
1013 ssh_parms * params; | |
1014 char *buf; | |
1015 int clen; | |
1016 | |
1017 params = request->protocol_data; | |
1018 clen = htonl (len); | |
1019 buf = g_malloc (len + 6); | |
1020 buf[0] = params->channel; | |
1021 buf[1] = cmdnum; | |
1022 memcpy (&buf[2], &clen, 4); | |
1023 if (command) | |
1024 memcpy (&buf[6], command, len); | |
1025 ssh_log_command (request, params->channel, cmdnum, command, len, 1); | |
1026 | |
1027 wrote = fwrite (buf, 1, len + 6, request->sockfd_write); | |
1028 if (ferror (request->sockfd_write)) | |
1029 { | |
1030 request->logging_function (gftp_logging_error, request->user_data, | |
1031 _("Error: Could not write to socket: %s\n"), | |
1032 g_strerror (errno)); | |
1033 gftp_disconnect (request); | |
1034 g_free (buf); | |
1035 return (-1); | |
1036 } | |
1037 | |
1038 g_free (buf); | |
1039 | |
1040 fflush (request->sockfd_write); | |
1041 if (ferror (request->sockfd_write)) | |
1042 { | |
1043 request->logging_function (gftp_logging_error, request->user_data, | |
1044 _("Error: Could not write to socket: %s\n"), | |
1045 g_strerror (errno)); | |
1046 gftp_disconnect (request); | |
1047 return (-1); | |
1048 } | |
1049 return 0; | |
1050 | |
1051 } | |
1052 | |
1053 static int | |
1054 ssh_read_response (gftp_request * request, ssh_message *message, FILE * fd) | |
1055 { | |
1056 char buf[6]; | |
1057 | |
1058 if (ssh_read_message (request, buf, 6, fd) == NULL) | |
1059 return (-1); | |
1060 | |
1061 message->channel = buf[0]; | |
1062 message->command = buf[1]; | |
1063 memcpy (&message->len, buf + 2, 4); | |
1064 message->len = ntohl (message->len); | |
1065 if (message->len > 8192) | |
1066 { | |
1067 request->logging_function (gftp_logging_error, request->user_data, | |
1068 _("Error: Message size %d too big from server\n"), | |
1069 message->len); | |
1070 memset (message, 0, sizeof (*message)); | |
1071 gftp_disconnect (request); | |
1072 return (-1); | |
1073 } | |
1074 | |
1075 message->data = g_malloc (message->len + 1); | |
1076 | |
1077 if (message->len > 0 && ssh_read_message (request, | |
1078 (char *) message->data, message->len, fd) == NULL) | |
1079 return (-1); | |
1080 | |
1081 ((char *) message->data)[message->len] = '\0'; | |
1082 ssh_log_command (request, message->channel, message->command, message->data, | |
1083 message->len, 0); | |
1084 return (message->command); | |
1085 } | |
1086 | |
1087 | |
1088 static char * | |
1089 ssh_read_message (gftp_request * request, char *buf, size_t len, FILE * fd) | |
1090 { | |
1091 if (fd == NULL) | |
1092 fd = request->sockfd; | |
1093 | |
1094 fread (buf, len, 1, fd); | |
1095 if (ferror (fd)) | |
1096 { | |
1097 request->logging_function (gftp_logging_error, request->user_data, | |
1098 _("Error: Could not read from socket: %s\n"), | |
1099 g_strerror (errno)); | |
1100 gftp_disconnect (request); | |
1101 return (NULL); | |
1102 } | |
1103 | |
1104 return (buf); | |
1105 } | |
1106 | |
1107 | |
1108 static char * | |
1109 ssh_read_line (gftp_request * request) | |
1110 { | |
1111 char *retstr, *pos, tempchar; | |
1112 ssh_parms *buffer; | |
1113 | |
1114 pos = NULL; | |
1115 buffer = request->protocol_data; | |
1116 if (!buffer->enddata && (pos = strchr (buffer->pos, '\n')) == NULL) | |
1117 return (NULL); | |
1118 | |
1119 if (pos == NULL) | |
1120 { | |
1121 pos = buffer->pos + strlen (buffer->pos) - 1; | |
1122 tempchar = '\0'; | |
1123 } | |
1124 else | |
1125 tempchar = pos + 1 == '\0' ? '\0' : '1'; | |
1126 | |
1127 if (*(pos-1) == '\r') | |
1128 *(pos-1) = '\0'; | |
1129 else | |
1130 *pos = '\0'; | |
1131 | |
1132 retstr = g_malloc (strlen (buffer->pos) + 1); | |
1133 strcpy (retstr, buffer->pos); | |
1134 | |
1135 if (tempchar != '\0' && *buffer->pos != '\0') | |
1136 { | |
1137 buffer->pos = pos + 1; | |
1138 while (*buffer->pos == '\r' || *buffer->pos == '\n') | |
1139 buffer->pos++; | |
1140 } | |
1141 else | |
1142 { | |
1143 g_free (buffer->buffer); | |
1144 buffer->buffer = buffer->pos = NULL; | |
1145 } | |
1146 return (retstr); | |
1147 } | |
1148 | |
1149 | |
1150 static void | |
1151 ssh_log_command (gftp_request * request, int channel, int cmdnum, | |
1152 const char *command, size_t len, int direction) | |
1153 { | |
1154 const char *pos; | |
1155 char *tempstr; | |
1156 int ok; | |
1157 | |
1158 switch (cmdnum) | |
1159 { | |
1160 case CHDIR: | |
1161 tempstr = "CHDIR "; | |
1162 break; | |
1163 case GETDIR: | |
1164 tempstr = "GETDIR "; | |
1165 break; | |
1166 case TELLDIR: | |
1167 tempstr = "TELLDIR "; | |
1168 break; | |
1169 case SENDFILE: | |
1170 tempstr = "SENDFILE "; | |
1171 break; | |
1172 case FILESIZE: | |
1173 tempstr = "FILESIZE "; | |
1174 break; | |
1175 case FILEMODE: | |
1176 tempstr = "FILEMODE "; | |
1177 break; | |
1178 case ENDDATA: | |
1179 tempstr = "ENDDATA "; | |
1180 break; | |
1181 case FILEOK: | |
1182 tempstr = "FILEOK "; | |
1183 break; | |
1184 case STREAM: | |
1185 tempstr = "STREAM "; | |
1186 break; | |
1187 case REQUEST: | |
1188 tempstr = "REQUEST "; | |
1189 break; | |
1190 case FILENAME: | |
1191 tempstr = "FILENAME "; | |
1192 break; | |
1193 case EXEC: | |
1194 tempstr = "EXEC "; | |
1195 break; | |
1196 case SKIPBYTES: | |
1197 tempstr = "SKIPBYTES "; | |
1198 break; | |
1199 case ERROR: | |
1200 tempstr = "ERROR: "; | |
1201 break; | |
1202 case SUCCESS: | |
1203 tempstr = "SUCCESS "; | |
1204 break; | |
1205 case CLOSE: | |
1206 tempstr = "CLOSE "; | |
1207 break; | |
1208 case SSH_VERSION: | |
1209 tempstr = "VERSION "; | |
1210 break; | |
1211 case CANCEL: | |
1212 tempstr = "CANCEL "; | |
1213 break; | |
1214 case FILETIME: | |
1215 tempstr = "FILETIME "; | |
1216 break; | |
1217 default: | |
1218 return; | |
1219 } | |
1220 | |
1221 ok = 0; | |
1222 if (command) | |
1223 { | |
1224 for (pos = command; pos < command + len; pos++) | |
1225 { | |
1226 if (*pos == '\0') | |
1227 { | |
1228 ok = 1; | |
1229 break; | |
1230 } | |
1231 } | |
1232 } | |
1233 | |
1234 request->logging_function (direction == GFTP_DIRECTION_DOWNLOAD ? | |
1235 gftp_logging_send : gftp_logging_recv, | |
1236 request->user_data, "%d: %s %s\n", channel, | |
1237 tempstr, ok ? command : ""); | |
1238 } | |
1239 | |
1240 | |
1241 static size_t | |
1242 ssh_remove_spaces ( char *string ) | |
1243 { | |
1244 size_t len; | |
1245 char *pos; | |
1246 | |
1247 for (pos = string, len = 0; *pos != '\0'; len++, pos++) | |
1248 { | |
1249 if (*pos == ' ') | |
1250 *pos = '\0'; | |
1251 } | |
1252 return (len); | |
1253 } | |
1254 |