1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: Benjamin Otte <otte@gnome.org>
21  */
22 
23 #include <config.h>
24 
25 #include <stdio.h> /* for sscanf() */
26 #include <stdlib.h> /* for exit() */
27 
28 #include <glib/gi18n.h>
29 
30 #include "gvfsftptask.h"
31 
32 /*** DOCS ***/
33 
34 /**
35  * GVfsFtpResponseFlags:
36  * @G_VFS_FTP_PASS_100: Don't treat 1XX responses, but return them
37  * @G_VFS_FTP_PASS_300: Don't treat 3XX responses, but return them
38  * @G_VFS_FTP_PASS_400: Don't treat 4XX responses, but return them
39  * @G_VFS_FTP_PASS_500: Don't treat 5XX responses, but return them
40  * @G_VFS_FTP_PASS_550: Don't treat 550 responses, but return them
41  * @G_VFS_FTP_FAIL_200: Fail on a 2XX response
42  *
43  * These flags can be passed to gvfs_ftp_task_receive() (and in
44  * turn gvfs_ftp_task_send()) to influence the behavior of the functions.
45  */
46 
47 /**
48  * G_VFS_FTP_G_VFS_FTP_GROUP:
49  * @response: a valid ftp response
50  *
51  * Determines the group the given @response belongs to. The group is the first
52  * digit of the reply.
53  *
54  * Returns: The group the response code belonged to from 1-5
55  */
56 
57 /**
58  * G_VFS_FTP_TASK_INIT:
59  * @backend: the backend used by this task
60  * @job: the job that initiated the task or %NULL if none
61  *
62  * Initializes a new task structure for the given backend and job.
63  */
64 
65 /**
66  * GVfsFtpErrorFunc:
67  * @task: task to handle
68  * @data: data argument provided to g_vfs_ftp_task_send_and_check()
69  *
70  * Function prototype for error checking functions used by
71  * g_vfs_ftp_task_send_and_check(). When called, these functions are supposed
72  * to check a specific error condition and if met, set an error on the passed
73  * @task.
74  */
75 
76 /*** CODE ***/
77 
78 gboolean
g_vfs_ftp_task_login(GVfsFtpTask * task,const char * username,const char * password)79 g_vfs_ftp_task_login (GVfsFtpTask *task,
80                       const char * username,
81                       const char * password)
82 {
83   guint status;
84 
85   g_return_val_if_fail (task != NULL, FALSE);
86   g_return_val_if_fail (username != NULL, FALSE);
87   g_return_val_if_fail (password != NULL, FALSE);
88 
89   if (g_vfs_ftp_task_is_in_error (task))
90     return FALSE;
91 
92   status = g_vfs_ftp_task_send (task, G_VFS_FTP_PASS_300,
93                                 "USER %s", username);
94 
95   if (G_VFS_FTP_RESPONSE_GROUP (status) == 3)
96     {
97       /* rationale for choosing the default password:
98        * - some ftp servers expect something that looks like an email address
99        * - we don't want to send the user's name or address, as that would be
100        *   a privacy problem
101        * - we want to give ftp server administrators a chance to notify us of
102        *   problems with our client.
103        * - we don't want to drown in spam.
104        */
105       if (password == NULL || password[0] == 0)
106         password = "gvfsd-ftp-" VERSION "@example.com";
107       status = g_vfs_ftp_task_send (task, 0,
108         			    "PASS %s", password);
109     }
110 
111   return status;
112 }
113 
114 /**
115  * g_vfs_ftp_task_setup_connection:
116  * @task: the task
117  *
118  * Sends all commands necessary to put the connection into a usable state,
119  * like setting the transfer mode to binary. Note that passive mode will
120  * will be set on a case-by-case basis when opening a data connection.
121  **/
122 void
g_vfs_ftp_task_setup_connection(GVfsFtpTask * task)123 g_vfs_ftp_task_setup_connection (GVfsFtpTask *task)
124 {
125   g_return_if_fail (task != NULL);
126 
127   /* only binary transfers please */
128   g_vfs_ftp_task_send (task, 0, "TYPE I");
129   if (g_vfs_ftp_task_is_in_error (task))
130     return;
131 
132 #if 0
133   /* RFC 2428 suggests to send this to make NAT routers happy */
134   /* XXX: Disabled for the following reasons:
135    * - most ftp clients don't use it
136    * - lots of broken ftp servers can't see the difference between
137    *   "EPSV" and "EPSV ALL"
138    * - impossible to dynamically fall back to regular PASV in case
139    *   EPSV doesn't work for some reason.
140    * If this makes your ftp connection fail, please file a bug and we will
141    * try to invent a way to make this all work. Until then, we'll just
142    * ignore the RFC.
143    */
144   if (g_vfs_backend_ftp_has_feature (task->backend, g_VFS_FTP_FEATURE_EPSV))
145     g_vfs_ftp_task_send (task, 0, "EPSV ALL");
146   g_vfs_ftp_task_clear_error (task);
147 #endif
148 
149   /* instruct server that we'll give and assume we get utf8 */
150   if (g_vfs_backend_ftp_has_feature (task->backend, G_VFS_FTP_FEATURE_UTF8))
151     {
152       if (!g_vfs_ftp_task_send (task, 0, "OPTS UTF8 ON"))
153         g_vfs_ftp_task_clear_error (task);
154     }
155 }
156 
157 
158 static void
do_broadcast(GCancellable * cancellable,GCond * cond)159 do_broadcast (GCancellable *cancellable, GCond *cond)
160 {
161   g_cond_broadcast (cond);
162 }
163 
164 /* Decide whether to allow verification errors for control and data connections
165  * after the initial connection.  The connection is only allowed if the
166  * identity is the same as for the initial connection. */
167 static gboolean
reconnect_certificate_cb(GTlsConnection * conn,GTlsCertificate * certificate,GTlsCertificateFlags errors,gpointer user_data)168 reconnect_certificate_cb (GTlsConnection *conn,
169                           GTlsCertificate *certificate,
170                           GTlsCertificateFlags errors,
171                           gpointer user_data)
172 {
173   GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (user_data);
174 
175   /* If the verification result has changed in some way, abort the
176    * connection. */
177   if (errors != ftp->certificate_errors)
178     return FALSE;
179 
180   /* Only allow the connection if the certificate presented is the same as for
181    * the initial connection which the user accepted. */
182   return ftp->certificate &&
183          g_tls_certificate_is_same (certificate, ftp->certificate);
184 }
185 
186 /**
187  * g_vfs_ftp_task_acquire_connection:
188  * @task: a task without an associated connection
189  *
190  * Acquires a new connection for use by this @task. This uses the connection
191  * pool of @task's backend, so it reuses previously opened connections and
192  * does not reopen new connections unnecessarily. If all connections are busy,
193  * it waits %G_VFS_FTP_TIMEOUT_IN_SECONDS seconds for a new connection to
194  * become available. Keep in mind that a newly acquired connection might have
195  * timed out and therefore closed by the FTP server. You must account for
196  * this when sending the first command to the server.
197  *
198  * Returns: %TRUE if a connection could be acquired, %FALSE if an error
199  *          occured
200  **/
201 static gboolean
g_vfs_ftp_task_acquire_connection(GVfsFtpTask * task)202 g_vfs_ftp_task_acquire_connection (GVfsFtpTask *task)
203 {
204   GVfsBackendFtp *ftp;
205   gint64 end_time;
206   gulong id;
207 
208   g_return_val_if_fail (task != NULL, FALSE);
209   g_return_val_if_fail (task->conn == NULL, FALSE);
210 
211   if (g_vfs_ftp_task_is_in_error (task))
212     return FALSE;
213 
214   ftp = task->backend;
215   g_mutex_lock (&ftp->mutex);
216   id = g_cancellable_connect (task->cancellable,
217         		      G_CALLBACK (do_broadcast),
218         		      &ftp->cond, NULL);
219   while (task->conn == NULL && ftp->queue != NULL)
220     {
221       if (g_cancellable_is_cancelled (task->cancellable))
222         {
223           task->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
224         		                     _("Operation was cancelled"));
225           break;
226         }
227 
228       task->conn = g_queue_pop_head (ftp->queue);
229       if (task->conn != NULL)
230         {
231           if (g_vfs_ftp_connection_is_usable (task->conn))
232             break;
233 
234           g_vfs_ftp_connection_free (task->conn);
235           task->conn = NULL;
236         }
237 
238       if (ftp->connections < ftp->max_connections)
239         {
240           static GThread *last_thread = NULL;
241           /* Save current number of connections here, so we can limit maximum
242            * connections later.
243            * This is necessary for threading reasons (connections can be
244            * opened or closed while we are still in the opening process. */
245           guint maybe_max_connections = ftp->connections;
246 
247           ftp->connections++;
248           last_thread = g_thread_self ();
249           g_mutex_unlock (&ftp->mutex);
250           task->conn = g_vfs_ftp_connection_new (ftp->addr, task->cancellable, &task->error);
251           if (G_LIKELY (task->conn != NULL))
252             {
253               g_vfs_ftp_task_initial_handshake (task, reconnect_certificate_cb, ftp);
254               g_vfs_ftp_task_login (task, ftp->user, ftp->password);
255               g_vfs_ftp_task_setup_connection (task);
256               if (G_LIKELY (!g_vfs_ftp_task_is_in_error (task)))
257                 goto out_unlocked;
258 
259               g_vfs_ftp_connection_free (task->conn);
260               task->conn = NULL;
261             }
262 
263           g_mutex_lock (&ftp->mutex);
264           ftp->connections--;
265           /* If this value is still equal to our thread it means there were no races
266            * trying to open connections and the maybe_max_connections value is
267            * reliable. */
268           if (last_thread == g_thread_self () &&
269               !g_vfs_ftp_task_error_matches (task, G_IO_ERROR, G_IO_ERROR_CANCELLED))
270             {
271               g_print ("maybe: %u, max %u (due to %s)\n", maybe_max_connections, ftp->max_connections, task->error->message);
272               ftp->max_connections = MIN (ftp->max_connections, maybe_max_connections);
273               if (ftp->max_connections == 0)
274                 {
275                   g_debug ("no more connections left, exiting...\n");
276                   /* FIXME: shut down properly */
277                   exit (0);
278                 }
279             }
280 
281           g_vfs_ftp_task_clear_error (task);
282           continue;
283         }
284 
285       end_time = g_get_monotonic_time () + G_VFS_FTP_TIMEOUT_IN_SECONDS * G_TIME_SPAN_SECOND;
286       if (ftp->busy_connections >= ftp->connections ||
287           !g_cond_wait_until (&ftp->cond, &ftp->mutex, end_time))
288         {
289           task->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_BUSY,
290         		                     _("The FTP server is busy. Try again later"));
291           break;
292         }
293     }
294   if (!ftp->queue)
295     task->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED,
296                                        _("Backend currently unmounting"));
297   g_mutex_unlock (&ftp->mutex);
298 
299 out_unlocked:
300   g_cancellable_disconnect (task->cancellable, id);
301 
302   return task->conn != NULL;
303 }
304 
305 /**
306  * g_vfs_ftp_task_release_connection:
307  * @task: a task
308  *
309  * Releases the connection in use by @task to the backend's connection pool,
310  * or frees it if it is in an error state. You must use this function to free
311  * a @task's connection, never use g_vfs_ftp_connection_free() directly. If
312  * the task does not have a current connection, this function just returns.
313  *
314  * This function also closes all potentially open data connections.
315  **/
316 static void
g_vfs_ftp_task_release_connection(GVfsFtpTask * task)317 g_vfs_ftp_task_release_connection (GVfsFtpTask *task)
318 {
319   g_return_if_fail (task != NULL);
320 
321   /* we allow task->conn == NULL to ease error cases */
322   if (task->conn == NULL)
323     return;
324 
325   g_vfs_ftp_task_close_data_connection (task);
326 
327   g_mutex_lock (&task->backend->mutex);
328   if (task->backend->queue && g_vfs_ftp_connection_is_usable (task->conn))
329     {
330       g_queue_push_tail (task->backend->queue, task->conn);
331       g_cond_signal (&task->backend->cond);
332     }
333   else
334     {
335       task->backend->connections--;
336       g_vfs_ftp_connection_free (task->conn);
337     }
338   g_mutex_unlock (&task->backend->mutex);
339   task->conn = NULL;
340 }
341 
342 /**
343  * g_vfs_ftp_task_done:
344  * @task: the task to finalize
345  *
346  * Finalizes the given task and clears all memory in use. It also marks the
347  * associated job as success or failure depending on the error state of the
348  * task.
349  **/
350 void
g_vfs_ftp_task_done(GVfsFtpTask * task)351 g_vfs_ftp_task_done (GVfsFtpTask *task)
352 {
353   g_return_if_fail (task != NULL);
354 
355   g_vfs_ftp_task_release_connection (task);
356 
357   if (task->job)
358     {
359       if (g_vfs_ftp_task_is_in_error (task))
360         g_vfs_job_failed_from_error (task->job, task->error);
361       else
362         g_vfs_job_succeeded (task->job);
363     }
364 
365   g_vfs_ftp_task_clear_error (task);
366 }
367 
368 /**
369  * g_vfs_ftp_task_set_error_from_response:
370  * @task: the task
371  * @response: the response code
372  *
373  * Sets the @task into an error state. The exact error is determined from the
374  * @response code.
375  **/
376 void
g_vfs_ftp_task_set_error_from_response(GVfsFtpTask * task,guint response)377 g_vfs_ftp_task_set_error_from_response (GVfsFtpTask *task, guint response)
378 {
379   const char *msg;
380   int code;
381 
382   g_return_if_fail (task != NULL);
383   g_return_if_fail (task->error == NULL);
384 
385   /* Please keep this list ordered by response code,
386    * but group responses with the same message. */
387   switch (response)
388     {
389       case 332: /* Need account for login. */
390       case 532: /* Need account for storing files. */
391         /* FIXME: implement a sane way to handle accounts. */
392         code = G_IO_ERROR_NOT_SUPPORTED;
393         msg = _("Accounts are unsupported");
394         break;
395       case 421: /* Service not available, closing control connection. */
396         code = G_IO_ERROR_FAILED;
397         msg = _("Host closed connection");
398         break;
399       case 425: /* Can't open data connection. */
400         code = G_IO_ERROR_CLOSED;
401         msg = _("Cannot open data connection. Maybe your firewall prevents this?");
402         break;
403       case 426: /* Connection closed; transfer aborted. */
404         code = G_IO_ERROR_CLOSED;
405         msg = _("Data connection closed");
406         break;
407       case 450: /* Requested file action not taken. File unavailable (e.g., file busy). */
408       case 550: /* Requested action not taken. File unavailable (e.g., file not found, no access). */
409         /* FIXME: This is a lot of different errors. So we have to pretend to
410          * be smart here. */
411         code = G_IO_ERROR_FAILED;
412         msg = _("Operation failed");
413         break;
414       case 451: /* Requested action aborted: local error in processing. */
415         code = G_IO_ERROR_FAILED;
416         msg = _("Operation failed");
417         break;
418       case 452: /* Requested action not taken. Insufficient storage space in system. */
419       case 552:
420         code = G_IO_ERROR_NO_SPACE;
421         msg = _("No space left on server");
422         break;
423       case 500: /* Syntax error, command unrecognized. */
424       case 501: /* Syntax error in parameters or arguments. */
425       case 502: /* Command not implemented. */
426       case 503: /* Bad sequence of commands. */
427       case 504: /* Command not implemented for that parameter. */
428         code = G_IO_ERROR_NOT_SUPPORTED;
429         msg = _("Operation not supported");
430         break;
431       case 522: /* EPRT: unsupported network protocol */
432         code = G_IO_ERROR_NOT_SUPPORTED;
433         msg = _("Unsupported network protocol");
434         break;
435       case 530: /* Not logged in. */
436         code = G_IO_ERROR_PERMISSION_DENIED;
437         msg = _("Permission denied");
438         break;
439       case 551: /* Requested action aborted: page type unknown. */
440         code = G_IO_ERROR_FAILED;
441         msg = _("Page type unknown");
442         break;
443       case 553: /* Requested action not taken. File name not allowed. */
444         code = G_IO_ERROR_INVALID_FILENAME;
445         msg = _("Invalid filename");
446         break;
447       default:
448         code = G_IO_ERROR_FAILED;
449         msg = _("Invalid reply");
450         break;
451     }
452 
453   g_set_error_literal (&task->error, G_IO_ERROR, code, msg);
454 }
455 
456 /**
457  * g_vfs_ftp_task_give_connection:
458  * @task: the task
459  * @conn: the connection that the @task should use
460  *
461  * Forces a given @task to do I/O using the given connection. The @task must
462  * not have a connection associated with itself. The @task will take
463  * ownership of @conn.
464  **/
465 void
g_vfs_ftp_task_give_connection(GVfsFtpTask * task,GVfsFtpConnection * conn)466 g_vfs_ftp_task_give_connection (GVfsFtpTask *      task,
467                                 GVfsFtpConnection *conn)
468 {
469   g_return_if_fail (task != NULL);
470   g_return_if_fail (task->conn == NULL);
471 
472   task->conn = conn;
473   /* this connection is not busy anymore */
474   g_mutex_lock (&task->backend->mutex);
475   g_assert (task->backend->busy_connections > 0);
476   task->backend->busy_connections--;
477   g_mutex_unlock (&task->backend->mutex);
478 }
479 
480 /**
481  * g_vfs_ftp_task_take_connection:
482  * @task: the task
483  *
484  * Acquires the connection in use by the @task, so it can later be used with
485  * g_vfs_ftp_task_give_connection(). This or any other task will not use the
486  * connection anymore. The @task must have a connection in use.
487  *
488  * Returns: The connection that @task was using. You acquire ownership of
489  *          the connection.
490  **/
491 GVfsFtpConnection *
g_vfs_ftp_task_take_connection(GVfsFtpTask * task)492 g_vfs_ftp_task_take_connection (GVfsFtpTask *task)
493 {
494   GVfsFtpConnection *conn;
495   GVfsBackendFtp *ftp;
496 
497   g_return_val_if_fail (task != NULL, NULL);
498   g_return_val_if_fail (task->conn != NULL, NULL);
499 
500   conn = task->conn;
501   task->conn = NULL;
502 
503   ftp = task->backend;
504   /* mark this connection as busy */
505   g_mutex_lock (&ftp->mutex);
506   ftp->busy_connections++;
507   /* if all connections are busy, signal all waiting threads,
508    * so they stop waiting and return BUSY earlier */
509   if (ftp->busy_connections >= ftp->connections)
510     g_cond_broadcast (&ftp->cond);
511   g_mutex_unlock (&ftp->mutex);
512 
513   return conn;
514 }
515 
516 /**
517  * g_vfs_ftp_task_send:
518  * @task: the sending task
519  * @flags: response flags to use when sending
520  * @format: format string to construct command from
521  *          (without trailing \r\n)
522  * @...: arguments to format string
523  *
524  * Shortcut to calling g_vfs_ftp_task_send_and_check() with the reply, funcs
525  * and data arguments set to %NULL. See that function for details.
526  *
527  * Returns: 0 on error or the received FTP code otherwise.
528  **/
529 guint
g_vfs_ftp_task_send(GVfsFtpTask * task,GVfsFtpResponseFlags flags,const char * format,...)530 g_vfs_ftp_task_send (GVfsFtpTask *        task,
531                      GVfsFtpResponseFlags flags,
532                      const char *         format,
533                      ...)
534 {
535   va_list varargs;
536   guint response;
537 
538   g_return_val_if_fail (task != NULL, 0);
539   g_return_val_if_fail (format != NULL, 0);
540 
541   va_start (varargs, format);
542   response = g_vfs_ftp_task_sendv (task,
543         			   flags,
544                                    NULL,
545         			   format,
546         			   varargs);
547   va_end (varargs);
548   return response;
549 }
550 
551 /**
552  * g_vfs_ftp_task_send_and_check:
553  * @task: the sending task
554  * @flags: response flags to use when sending
555  * @funcs: %NULL or %NULL-terminated array of functions used to determine the
556  *         exact failure case upon a "550 Operation Failed" reply. This is
557  *         often necessary
558  * @data: data to pass to @funcs.
559  * @reply: %NULL or pointer to take a char array containing the full reply of
560  *         the ftp server upon successful reply. Use g_strfreev() to free
561  *         after use.
562  * @format: format string to construct command from
563  *          (without trailing \r\n)
564  * @...: arguments to format string
565  *
566  * Takes an ftp command in printf-style @format, potentially acquires a
567  * connection automatically, sends the command and waits for an answer from
568  * the ftp server. Without any @flags, FTP response codes other than 2xx cause
569  * an error. If @reply is not %NULL, the full reply will be put into a
570  * %NULL-terminated string array that must be freed with g_strfreev() after
571  * use.
572  * If @funcs is set, the 550 response code will cause all of these functions to
573  * be called in order passing them the @task and @data arguments given to this
574  * function until one of them sets an error on @task. This error will then be
575  * returned from this function. If none of those functions sets an error, the
576  * generic error for the 550 response will be used.
577  * If an error has been set on @task previously, this function will do nothing.
578  *
579  * Returns: 0 on error or the received FTP code otherwise.
580  **/
581 guint
g_vfs_ftp_task_send_and_check(GVfsFtpTask * task,GVfsFtpResponseFlags flags,const GVfsFtpErrorFunc * funcs,gpointer data,char *** reply,const char * format,...)582 g_vfs_ftp_task_send_and_check (GVfsFtpTask *           task,
583                                GVfsFtpResponseFlags    flags,
584                                const GVfsFtpErrorFunc *funcs,
585                                gpointer                data,
586                                char ***                reply,
587                                const char *            format,
588                                ...)
589 {
590   va_list varargs;
591   guint response;
592 
593   g_return_val_if_fail (task != NULL, 0);
594   g_return_val_if_fail (format != NULL, 0);
595   g_return_val_if_fail (funcs == NULL || funcs[0] != NULL, 0);
596 
597   if (funcs)
598     {
599       g_return_val_if_fail ((flags & G_VFS_FTP_PASS_550) == 0, 0);
600       flags |= G_VFS_FTP_PASS_550;
601     }
602 
603   va_start (varargs, format);
604   response = g_vfs_ftp_task_sendv (task,
605         			   flags,
606                                    reply,
607         			   format,
608         			   varargs);
609   va_end (varargs);
610 
611   if (response == 550 && funcs)
612     {
613       /* close a potentially open data connection, the error handlers
614        * might try to open new ones and that would cause assertions */
615       g_vfs_ftp_task_close_data_connection (task);
616 
617       while (*funcs && !g_vfs_ftp_task_is_in_error (task))
618         {
619           (*funcs) (task, data);
620           funcs++;
621         }
622       if (!g_vfs_ftp_task_is_in_error (task))
623           g_vfs_ftp_task_set_error_from_response (task, response);
624       response = 0;
625     }
626 
627   return response;
628 }
629 
630 /**
631  * g_vfs_ftp_task_sendv:
632  * @task: the sending task
633  * @flags: response flags to use when receiving the reply
634  * @reply: %NULL or pointer to char array that takes the full reply from the
635  *         server
636  * @format: format string to construct command from
637  *          (without trailing \r\n)
638  * @varargs: arguments to format string
639  *
640  * This is the varargs version of g_vfs_ftp_task_send(). See that function
641  * for details.
642  *
643  * Returns: the received FTP code or 0 on error.
644  **/
645 guint
g_vfs_ftp_task_sendv(GVfsFtpTask * task,GVfsFtpResponseFlags flags,char *** reply,const char * format,va_list varargs)646 g_vfs_ftp_task_sendv (GVfsFtpTask *          task,
647                       GVfsFtpResponseFlags   flags,
648                       char ***               reply,
649                       const char *           format,
650                       va_list                varargs)
651 {
652   GString *command;
653   gboolean retry_on_timeout = FALSE;
654   guint response;
655 
656   if (g_vfs_ftp_task_is_in_error (task))
657     return 0;
658 
659   command = g_string_new ("");
660   g_string_append_vprintf (command, format, varargs);
661   g_string_append (command, "\r\n");
662 
663 retry:
664   if (task->conn == NULL)
665     {
666       if (!g_vfs_ftp_task_acquire_connection (task))
667         {
668           g_string_free (command, TRUE);
669           return 0;
670         }
671       retry_on_timeout = TRUE;
672     }
673 
674   g_vfs_ftp_connection_send (task->conn,
675                              command->str,
676                              command->len,
677                              task->cancellable,
678                              &task->error);
679 
680   response = g_vfs_ftp_task_receive (task, flags, reply);
681 
682   /* NB: requires adaption if we allow passing 4xx responses */
683   if (retry_on_timeout &&
684       g_vfs_ftp_task_is_in_error (task) &&
685       !g_vfs_ftp_connection_is_usable (task->conn))
686     {
687       g_vfs_ftp_task_clear_error (task);
688       g_vfs_ftp_task_release_connection (task);
689       goto retry;
690     }
691 
692   g_string_free (command, TRUE);
693   return response;
694 }
695 
696 /**
697  * g_vfs_ftp_task_receive:
698  * @task: the receiving task
699  * @flags: response flags to use
700  * @reply: %NULL or pointer to char array that takes the full reply from the
701  *         server
702  *
703  * Unless @task is in an error state, this function receives a reply from
704  * the @task's connection. The @task must have a connection set, which will
705  * happen when either g_vfs_ftp_task_send() or
706  * g_vfs_ftp_task_give_connection() have been called on the @task before.
707  * Unless @flags are given, all reply codes not in the 200s cause an error.
708  * If @task is in an error state when calling this function, nothing will
709  * happen and the function will just return.
710  *
711  * Returns: the received FTP code or 0 on error.
712  **/
713 guint
g_vfs_ftp_task_receive(GVfsFtpTask * task,GVfsFtpResponseFlags flags,char *** reply)714 g_vfs_ftp_task_receive (GVfsFtpTask *        task,
715                         GVfsFtpResponseFlags flags,
716                         char ***             reply)
717 {
718   guint response;
719 
720   g_return_val_if_fail (task != NULL, 0);
721   if (g_vfs_ftp_task_is_in_error (task))
722     return 0;
723   g_return_val_if_fail (task->conn != NULL, 0);
724 
725   response = g_vfs_ftp_connection_receive (task->conn,
726                                            reply,
727                                            task->cancellable,
728                                            &task->error);
729 
730   switch (G_VFS_FTP_RESPONSE_GROUP (response))
731     {
732       case 0:
733         return 0;
734       case 1:
735         if (flags & G_VFS_FTP_PASS_100)
736           break;
737         g_vfs_ftp_task_set_error_from_response (task, response);
738         break;
739       case 2:
740         if (flags & G_VFS_FTP_FAIL_200)
741           g_vfs_ftp_task_set_error_from_response (task, response);
742         break;
743       case 3:
744         if (flags & G_VFS_FTP_PASS_300)
745           break;
746         g_vfs_ftp_task_set_error_from_response (task, response);
747         break;
748       case 4:
749         g_vfs_ftp_task_set_error_from_response (task, response);
750         break;
751       case 5:
752         if ((flags & G_VFS_FTP_PASS_500) ||
753             (response == 550 && (flags & G_VFS_FTP_PASS_550)))
754           break;
755         g_vfs_ftp_task_set_error_from_response (task, response);
756         break;
757       default:
758         g_assert_not_reached ();
759         break;
760     }
761 
762   if (g_vfs_ftp_task_is_in_error (task))
763     {
764       if (response != 0 && reply)
765         {
766           g_strfreev (*reply);
767           *reply = NULL;
768         }
769       response = 0;
770     }
771 
772   return response;
773 }
774 
775 /**
776  * g_vfs_ftp_task_close_data_connection:
777  * @task: a task potentially having an open data connection
778  *
779  * Closes any data connection @task might have opened.
780  */
781 void
g_vfs_ftp_task_close_data_connection(GVfsFtpTask * task)782 g_vfs_ftp_task_close_data_connection (GVfsFtpTask *task)
783 {
784   g_return_if_fail (task != NULL);
785 
786   if (task->conn == NULL)
787     return;
788 
789   g_vfs_ftp_connection_close_data_connection (task->conn);
790 }
791 
792 static GSocketAddress *
g_vfs_ftp_task_create_remote_address(GVfsFtpTask * task,guint port)793 g_vfs_ftp_task_create_remote_address (GVfsFtpTask *task, guint port)
794 {
795   GSocketAddress *old, *new;
796 
797   old = g_vfs_ftp_connection_get_address (task->conn, &task->error);
798   if (old == NULL)
799     return NULL;
800   g_assert (G_IS_INET_SOCKET_ADDRESS (old));
801   new = g_inet_socket_address_new (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (old)), port);
802 
803   return new;
804 }
805 
806 static GVfsFtpMethod
g_vfs_ftp_task_setup_data_connection_epsv(GVfsFtpTask * task,GVfsFtpMethod method)807 g_vfs_ftp_task_setup_data_connection_epsv (GVfsFtpTask *task, GVfsFtpMethod method)
808 {
809   const char *s;
810   char **reply;
811   guint port;
812   GSocketAddress *addr;
813   guint status;
814   gboolean success;
815 
816   g_assert (task->error == NULL);
817 
818   status = g_vfs_ftp_task_send_and_check (task, G_VFS_FTP_PASS_500, NULL, NULL, &reply, "EPSV");
819   if (G_VFS_FTP_RESPONSE_GROUP (status) != 2)
820     return G_VFS_FTP_METHOD_ANY;
821 
822   /* FIXME: parse multiple lines? */
823   s = strrchr (reply[0], '(');
824   if (!s)
825     goto fail;
826 
827   s += 4;
828   port = strtoul (s, NULL, 10);
829   if (port == 0)
830     goto fail;
831 
832   g_strfreev (reply);
833   addr = g_vfs_ftp_task_create_remote_address (task, port);
834   if (addr == NULL)
835     return G_VFS_FTP_METHOD_ANY;
836 
837   success = g_vfs_ftp_connection_open_data_connection (task->conn,
838                                                        addr,
839                                                        task->cancellable,
840                                                        &task->error);
841   g_object_unref (addr);
842   return success ? G_VFS_FTP_METHOD_EPSV : G_VFS_FTP_METHOD_ANY;
843 
844 fail:
845   g_strfreev (reply);
846   return G_VFS_FTP_METHOD_ANY;
847 }
848 
849 static GVfsFtpMethod
g_vfs_ftp_task_setup_data_connection_pasv(GVfsFtpTask * task,GVfsFtpMethod method)850 g_vfs_ftp_task_setup_data_connection_pasv (GVfsFtpTask *task, GVfsFtpMethod method)
851 {
852   guint ip1, ip2, ip3, ip4, port1, port2;
853   char **reply;
854   const char *s;
855   GSocketAddress *addr;
856   guint status;
857   gboolean success;
858 
859   status = g_vfs_ftp_task_send_and_check (task, 0, NULL, NULL, &reply, "PASV");
860   if (status == 0)
861     return G_VFS_FTP_METHOD_ANY;
862 
863   /* parse response and try to find the address to connect to.
864    * This code does the same as curl.
865    */
866   for (s = reply[0]; *s; s++)
867     {
868       if (sscanf (s, "%u,%u,%u,%u,%u,%u",
869         	 &ip1, &ip2, &ip3, &ip4,
870         	 &port1, &port2) == 6)
871        break;
872     }
873   if (*s == 0)
874     {
875       g_strfreev (reply);
876       g_set_error_literal (&task->error, G_IO_ERROR, G_IO_ERROR_FAILED,
877         		   _("Invalid reply"));
878       return G_VFS_FTP_METHOD_ANY;
879     }
880   g_strfreev (reply);
881 
882   if (method == G_VFS_FTP_METHOD_PASV || method == G_VFS_FTP_METHOD_ANY)
883     {
884       guint8 ip[4];
885       GInetAddress *inet_addr;
886 
887       ip[0] = ip1;
888       ip[1] = ip2;
889       ip[2] = ip3;
890       ip[3] = ip4;
891       inet_addr = g_inet_address_new_from_bytes (ip, G_SOCKET_FAMILY_IPV4);
892       addr = g_inet_socket_address_new (inet_addr, port1 << 8 | port2);
893       g_object_unref (inet_addr);
894 
895       success = g_vfs_ftp_connection_open_data_connection (task->conn,
896                                                            addr,
897                                                            task->cancellable,
898                                                            &task->error);
899       g_object_unref (addr);
900       if (success)
901         return G_VFS_FTP_METHOD_PASV;
902       if (g_vfs_ftp_task_is_in_error (task) && method != G_VFS_FTP_METHOD_ANY)
903         return G_VFS_FTP_METHOD_ANY;
904 
905       g_vfs_ftp_task_clear_error (task);
906     }
907 
908   if (method == G_VFS_FTP_METHOD_PASV_ADDR || method == G_VFS_FTP_METHOD_ANY)
909     {
910       /* Workaround code:
911        * Various ftp servers aren't setup correctly when behind a NAT. They report
912        * their own IP address (like 10.0.0.4) and not the address in front of the
913        * NAT. But this is likely the same address that we connected to with our
914        * command connetion. So if the address given by PASV fails, we fall back
915        * to the address of the command stream.
916        */
917       addr = g_vfs_ftp_task_create_remote_address (task, port1 << 8 | port2);
918       if (addr == NULL)
919         return G_VFS_FTP_METHOD_ANY;
920       success = g_vfs_ftp_connection_open_data_connection (task->conn,
921                                                            addr,
922                                                            task->cancellable,
923                                                            &task->error);
924       g_object_unref (addr);
925       if (success)
926         return G_VFS_FTP_METHOD_PASV_ADDR;
927     }
928 
929   return G_VFS_FTP_METHOD_ANY;
930 }
931 
932 static GVfsFtpMethod
g_vfs_ftp_task_setup_data_connection_eprt(GVfsFtpTask * task,GVfsFtpMethod unused)933 g_vfs_ftp_task_setup_data_connection_eprt (GVfsFtpTask *task, GVfsFtpMethod unused)
934 {
935   GSocketAddress *addr;
936   guint status, port, family;
937   char *ip_string;
938 
939   /* workaround for the task not having a connection yet */
940   if (task->conn == NULL &&
941       g_vfs_ftp_task_send (task, 0, "NOOP") == 0)
942     return G_VFS_FTP_METHOD_ANY;
943 
944   addr = g_vfs_ftp_connection_listen_data_connection (task->conn, &task->error);
945   if (addr == NULL)
946     return G_VFS_FTP_METHOD_ANY;
947   switch (g_socket_address_get_family (addr))
948     {
949       case G_SOCKET_FAMILY_IPV4:
950         family = 1;
951         break;
952       case G_SOCKET_FAMILY_IPV6:
953         family = 2;
954         break;
955       default:
956         g_object_unref (addr);
957         return G_VFS_FTP_METHOD_ANY;
958     }
959 
960   ip_string = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)));
961   /* if this ever happens (and it must not for IP4 and IP6 addresses),
962    * we need to add support for using a different separator */
963   g_assert (strchr (ip_string, '|') == NULL);
964   port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
965 
966   /* we could handle the 522 response here, (unsupported network family),
967    * but I don't think that will buy us anything */
968   status = g_vfs_ftp_task_send (task, 0, "EPRT |%u|%s|%u|", family, ip_string, port);
969   g_free (ip_string);
970   g_object_unref (addr);
971   if (status == 0)
972     return G_VFS_FTP_METHOD_ANY;
973 
974   return G_VFS_FTP_METHOD_EPRT;
975 }
976 
977 static GVfsFtpMethod
g_vfs_ftp_task_setup_data_connection_port(GVfsFtpTask * task,GVfsFtpMethod unused)978 g_vfs_ftp_task_setup_data_connection_port (GVfsFtpTask *task, GVfsFtpMethod unused)
979 {
980   GSocketAddress *addr;
981   guint status, i, port;
982   char *ip_string;
983 
984   /* workaround for the task not having a connection yet */
985   if (task->conn == NULL &&
986       g_vfs_ftp_task_send (task, 0, "NOOP") == 0)
987     return G_VFS_FTP_METHOD_ANY;
988 
989   addr = g_vfs_ftp_connection_listen_data_connection (task->conn, &task->error);
990   if (addr == NULL)
991     return G_VFS_FTP_METHOD_ANY;
992   /* the PORT command only supports IPv4 */
993   if (g_socket_address_get_family (addr) != G_SOCKET_FAMILY_IPV4)
994     {
995       g_object_unref (addr);
996       return G_VFS_FTP_METHOD_ANY;
997     }
998 
999   ip_string = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)));
1000   for (i = 0; ip_string[i]; i++)
1001     {
1002       if (ip_string[i] == '.')
1003         ip_string[i] = ',';
1004     }
1005   port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1006 
1007   status = g_vfs_ftp_task_send (task, 0, "PORT %s,%u,%u", ip_string, port >> 8, port & 0xFF);
1008   g_free (ip_string);
1009   g_object_unref (addr);
1010   if (status == 0)
1011     return G_VFS_FTP_METHOD_ANY;
1012 
1013   return G_VFS_FTP_METHOD_PORT;
1014 }
1015 
1016 typedef GVfsFtpMethod (* GVfsFtpOpenDataConnectionFunc) (GVfsFtpTask *task, GVfsFtpMethod method);
1017 typedef struct _GVfsFtpOpenDataConnectionMethod GVfsFtpOpenDataConnectionMethod;
1018 struct _GVfsFtpOpenDataConnectionMethod {
1019     GVfsFtpFeature                required_feature;
1020     GSocketFamily                 required_family;
1021     GVfsFtpOpenDataConnectionFunc func;
1022 };
1023 
1024 static gboolean
g_vfs_ftp_task_open_data_connection_method_is_supported(const GVfsFtpOpenDataConnectionMethod * method,GVfsFtpTask * task,GSocketFamily family)1025 g_vfs_ftp_task_open_data_connection_method_is_supported (const GVfsFtpOpenDataConnectionMethod *method,
1026                                                          GVfsFtpTask *                          task,
1027                                                          GSocketFamily                          family)
1028 {
1029   if (method->required_feature &&
1030       !g_vfs_backend_ftp_has_feature (task->backend, method->required_feature))
1031     return FALSE;
1032 
1033   if (method->required_family != G_SOCKET_FAMILY_INVALID &&
1034       method->required_family != family)
1035     return FALSE;
1036 
1037   return TRUE;
1038 }
1039 
1040 static GSocketFamily
g_vfs_ftp_task_get_socket_family(GVfsFtpTask * task)1041 g_vfs_ftp_task_get_socket_family (GVfsFtpTask *task)
1042 {
1043   GSocketAddress *addr;
1044   GSocketFamily family;
1045 
1046   /* workaround for the task not having a connection yet */
1047   if (task->conn == NULL &&
1048       g_vfs_ftp_task_send (task, 0, "NOOP") == 0)
1049     {
1050       g_vfs_ftp_task_clear_error (task);
1051       return G_SOCKET_FAMILY_INVALID;
1052     }
1053 
1054   addr = g_vfs_ftp_connection_get_address (task->conn, NULL);
1055   if (addr == NULL)
1056     return G_SOCKET_FAMILY_INVALID;
1057 
1058   family = g_socket_address_get_family (addr);
1059   g_object_unref (addr);
1060   return family;
1061 }
1062 
1063 static GVfsFtpMethod
g_vfs_ftp_task_setup_data_connection_any(GVfsFtpTask * task,GVfsFtpMethod unused)1064 g_vfs_ftp_task_setup_data_connection_any (GVfsFtpTask *task, GVfsFtpMethod unused)
1065 {
1066   static const GVfsFtpOpenDataConnectionMethod funcs_ordered[] = {
1067     { 0,                      G_SOCKET_FAMILY_IPV4,    g_vfs_ftp_task_setup_data_connection_pasv },
1068     { G_VFS_FTP_FEATURE_EPSV, G_SOCKET_FAMILY_INVALID, g_vfs_ftp_task_setup_data_connection_epsv },
1069     { 0,                      G_SOCKET_FAMILY_IPV4,    g_vfs_ftp_task_setup_data_connection_port },
1070     { G_VFS_FTP_FEATURE_EPRT, G_SOCKET_FAMILY_INVALID, g_vfs_ftp_task_setup_data_connection_eprt }
1071   };
1072   GVfsFtpMethod method;
1073   GSocketFamily family;
1074   guint i;
1075 
1076   family = g_vfs_ftp_task_get_socket_family (task);
1077 
1078   /* first try all advertised features */
1079   for (i = 0; i < G_N_ELEMENTS (funcs_ordered); i++)
1080     {
1081       if (!g_vfs_ftp_task_open_data_connection_method_is_supported (&funcs_ordered[i], task, family))
1082         continue;
1083       method = funcs_ordered[i].func (task, G_VFS_FTP_METHOD_ANY);
1084       if (method != G_VFS_FTP_METHOD_ANY)
1085         return method;
1086 
1087       g_vfs_ftp_task_clear_error (task);
1088     }
1089 
1090   /* then try if the non-advertised features work */
1091   for (i = 0; i < G_N_ELEMENTS (funcs_ordered); i++)
1092     {
1093       if (g_vfs_ftp_task_open_data_connection_method_is_supported (&funcs_ordered[i], task, family))
1094         continue;
1095       method = funcs_ordered[i].func (task, G_VFS_FTP_METHOD_ANY);
1096       if (method != G_VFS_FTP_METHOD_ANY)
1097         return method;
1098 
1099       g_vfs_ftp_task_clear_error (task);
1100     }
1101 
1102   /* finally, just give up */
1103   return G_VFS_FTP_METHOD_ANY;
1104 }
1105 
1106 /**
1107  * g_vfs_ftp_task_setup_data_connection:
1108  * @task: a task not having an open data connection
1109  *
1110  * Sets up a data connection to the ftp server with using the best method for
1111  * this task. If the operation fails, @task will be set into an error state.
1112  * You must call g_vfs_ftp_task_open_data_connection() to finish setup and
1113  * ensure the data connection actually gets opened. Usually, this requires
1114  * sending an FTP command down the stream.
1115  **/
1116 void
g_vfs_ftp_task_setup_data_connection(GVfsFtpTask * task)1117 g_vfs_ftp_task_setup_data_connection (GVfsFtpTask *task)
1118 {
1119   static const GVfsFtpOpenDataConnectionFunc connect_funcs[] = {
1120     [G_VFS_FTP_METHOD_ANY]       = g_vfs_ftp_task_setup_data_connection_any,
1121     [G_VFS_FTP_METHOD_EPSV]      = g_vfs_ftp_task_setup_data_connection_epsv,
1122     [G_VFS_FTP_METHOD_PASV]      = g_vfs_ftp_task_setup_data_connection_pasv,
1123     [G_VFS_FTP_METHOD_PASV_ADDR] = g_vfs_ftp_task_setup_data_connection_pasv,
1124     [G_VFS_FTP_METHOD_EPRT]      = g_vfs_ftp_task_setup_data_connection_eprt,
1125     [G_VFS_FTP_METHOD_PORT]      = g_vfs_ftp_task_setup_data_connection_port
1126   };
1127   GVfsFtpMethod method, result;
1128 
1129   g_return_if_fail (task != NULL);
1130 
1131   task->method = G_VFS_FTP_METHOD_ANY;
1132 
1133   method = g_atomic_int_get (&task->backend->method);
1134   g_assert (method < G_N_ELEMENTS (connect_funcs) && connect_funcs[method]);
1135 
1136   if (g_vfs_ftp_task_is_in_error (task))
1137     return;
1138 
1139   result = connect_funcs[method] (task, method);
1140 
1141   /* be sure to try all possibilities if one failed */
1142   if (result == G_VFS_FTP_METHOD_ANY &&
1143       method != G_VFS_FTP_METHOD_ANY &&
1144       !g_vfs_ftp_task_is_in_error (task))
1145     result = g_vfs_ftp_task_setup_data_connection_any (task, G_VFS_FTP_METHOD_ANY);
1146 
1147   g_assert (result < G_N_ELEMENTS (connect_funcs) && connect_funcs[result]);
1148   if (result != method)
1149     {
1150       static const char *methods[] = {
1151         [G_VFS_FTP_METHOD_ANY] = "any",
1152         [G_VFS_FTP_METHOD_EPSV] = "EPSV",
1153         [G_VFS_FTP_METHOD_PASV] = "PASV",
1154         [G_VFS_FTP_METHOD_PASV_ADDR] = "PASV with workaround",
1155         [G_VFS_FTP_METHOD_EPRT] = "EPRT",
1156         [G_VFS_FTP_METHOD_PORT] = "PORT"
1157       };
1158       g_atomic_int_set (&task->backend->method, result);
1159       g_debug ("# set default data connection method from %s to %s\n",
1160                methods[method], methods[result]);
1161     }
1162   task->method = result;
1163 }
1164 
1165 /**
1166  * g_vfs_ftp_task_open_data_connection:
1167  * @task: a task
1168  *
1169  * Tries to open a data connection to the ftp server and secures it if
1170  * necessary. If the operation fails, @task will be set into an error state.
1171  **/
1172 void
g_vfs_ftp_task_open_data_connection(GVfsFtpTask * task)1173 g_vfs_ftp_task_open_data_connection (GVfsFtpTask *task)
1174 {
1175   g_return_if_fail (task != NULL);
1176 
1177   if (g_vfs_ftp_task_is_in_error (task))
1178     return;
1179 
1180   if (task->method == G_VFS_FTP_METHOD_EPRT ||
1181       task->method == G_VFS_FTP_METHOD_PORT)
1182     g_vfs_ftp_connection_accept_data_connection (task->conn,
1183                                                  task->cancellable,
1184                                                  &task->error);
1185 
1186   if (g_vfs_ftp_task_is_in_error (task))
1187     return;
1188 
1189   if (task->backend->tls_mode != G_VFS_FTP_TLS_MODE_NONE)
1190     g_vfs_ftp_connection_data_connection_enable_tls (task->conn,
1191                                                      task->backend->server_identity,
1192                                                      reconnect_certificate_cb,
1193                                                      task->backend,
1194                                                      task->cancellable,
1195                                                      &task->error);
1196 }
1197 
1198 /**
1199  * g_vfs_ftp_task_initial_handshake:
1200  * @task: a task
1201  * @cb: callback called if there's a verification error
1202  * @user_data: user data passed to @cb
1203  *
1204  * Performs the initial handshake with an FTP server, including reading the
1205  * greeting and activating TLS.  If the operation fails, @task will be set into
1206  * an error state.
1207  **/
1208 gboolean
g_vfs_ftp_task_initial_handshake(GVfsFtpTask * task,CertificateCallback cb,gpointer user_data)1209 g_vfs_ftp_task_initial_handshake (GVfsFtpTask *task,
1210                                   CertificateCallback cb,
1211                                   gpointer user_data)
1212 {
1213   if (g_vfs_ftp_task_is_in_error (task))
1214     return FALSE;
1215 
1216   /* In implicit mode, we do the TLS handshake first, then receive the FTP
1217    * greeting. Explicit mode is practically the opposite.
1218    */
1219 
1220   if (task->backend->tls_mode == G_VFS_FTP_TLS_MODE_IMPLICIT)
1221     {
1222       if (!g_vfs_ftp_connection_enable_tls (task->conn,
1223                                             task->backend->server_identity,
1224                                             TRUE,
1225                                             cb,
1226                                             user_data,
1227                                             task->cancellable,
1228                                             &task->error))
1229         return FALSE;
1230 
1231       if (!g_vfs_ftp_task_receive (task, 0, NULL))
1232         return FALSE;
1233     }
1234   else if (task->backend->tls_mode == G_VFS_FTP_TLS_MODE_EXPLICIT)
1235     {
1236       if (!g_vfs_ftp_task_receive (task, 0, NULL))
1237         return FALSE;
1238 
1239       if (!g_vfs_ftp_task_send (task, 0, "AUTH TLS"))
1240         return FALSE;
1241 
1242       if (!g_vfs_ftp_connection_enable_tls (task->conn,
1243                                             task->backend->server_identity,
1244                                             FALSE,
1245                                             cb,
1246                                             user_data,
1247                                             task->cancellable,
1248                                             &task->error))
1249         return FALSE;
1250     }
1251   else
1252     {
1253       if (!g_vfs_ftp_task_receive (task, 0, NULL))
1254         return FALSE;
1255     }
1256 
1257   /* Request TLS also for the data channels. */
1258 
1259   if (task->backend->tls_mode != G_VFS_FTP_TLS_MODE_NONE)
1260     {
1261       if (!g_vfs_ftp_task_send (task, 0, "PBSZ 0"))
1262         return FALSE;
1263 
1264       if (!g_vfs_ftp_task_send (task, 0, "PROT P"))
1265         return FALSE;
1266     }
1267 
1268   return TRUE;
1269 }
1270