upgrade obexd to 0.47
[profile/ivi/obexd.git] / plugins / ftp.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2007-2010  Nokia Corporation
6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include <inttypes.h>
40
41 #include <glib.h>
42
43 #include "obexd.h"
44 #include "plugin.h"
45 #include "log.h"
46 #include "obex.h"
47 #include "manager.h"
48 #include "mimetype.h"
49 #include "service.h"
50 #include "ftp.h"
51 #include "filesystem.h"
52
53 #define LST_TYPE "x-obex/folder-listing"
54 #define CAP_TYPE "x-obex/capability"
55
56 #define FTP_CHANNEL 10
57 #define FTP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>         \
58 <record>                                                                \
59   <attribute id=\"0x0001\">                                             \
60     <sequence>                                                          \
61       <uuid value=\"0x1106\"/>                                          \
62     </sequence>                                                         \
63   </attribute>                                                          \
64                                                                         \
65   <attribute id=\"0x0004\">                                             \
66     <sequence>                                                          \
67       <sequence>                                                        \
68         <uuid value=\"0x0100\"/>                                        \
69       </sequence>                                                       \
70       <sequence>                                                        \
71         <uuid value=\"0x0003\"/>                                        \
72         <uint8 value=\"%u\" name=\"channel\"/>                          \
73       </sequence>                                                       \
74       <sequence>                                                        \
75         <uuid value=\"0x0008\"/>                                        \
76       </sequence>                                                       \
77     </sequence>                                                         \
78   </attribute>                                                          \
79                                                                         \
80   <attribute id=\"0x0009\">                                             \
81     <sequence>                                                          \
82       <sequence>                                                        \
83         <uuid value=\"0x1106\"/>                                        \
84         <uint16 value=\"0x0102\" name=\"version\"/>                     \
85       </sequence>                                                       \
86     </sequence>                                                         \
87   </attribute>                                                          \
88                                                                         \
89   <attribute id=\"0x0100\">                                             \
90     <text value=\"%s\" name=\"name\"/>                                  \
91   </attribute>                                                          \
92   <attribute id=\"0x0200\">                                             \
93     <uint16 value=\"%u\" name=\"psm\"/>                                 \
94   </attribute>                                                          \
95 </record>"
96
97 static const uint8_t FTP_TARGET[TARGET_SIZE] = {
98                         0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
99                         0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
100
101 struct ftp_session {
102         struct obex_session *os;
103         char *folder;
104 };
105
106 static void set_folder(struct ftp_session *ftp, const char *new_folder)
107 {
108         DBG("%p folder %s", ftp, new_folder);
109
110         g_free(ftp->folder);
111
112         ftp->folder = new_folder ? g_strdup(new_folder) : NULL;
113 }
114
115 static int get_by_type(struct ftp_session *ftp, const char *type)
116 {
117         struct obex_session *os = ftp->os;
118         const char *capability = obex_option_capability();
119         const char *name = obex_get_name(os);
120         char *path;
121         int err;
122
123         DBG("%p name %s type %s", ftp, name, type);
124
125         if (type == NULL && name == NULL)
126                 return -EBADR;
127
128         if (type != NULL && g_ascii_strcasecmp(type, CAP_TYPE) == 0)
129                 return obex_get_stream_start(os, capability);
130
131         if (name != NULL && !is_filename(name))
132                 return -EBADR;
133
134         path = g_build_filename(ftp->folder, name, NULL);
135         err = obex_get_stream_start(os, path);
136
137         g_free(path);
138
139         return err;
140 }
141
142 void *ftp_connect(struct obex_session *os, int *err)
143 {
144         struct ftp_session *ftp;
145         const char *root_folder;
146
147         DBG("");
148
149         root_folder = obex_option_root_folder();
150
151         manager_register_session(os);
152
153         ftp = g_new0(struct ftp_session, 1);
154         set_folder(ftp, root_folder);
155         ftp->os = os;
156
157         if (err)
158                 *err = 0;
159
160         DBG("session %p created", ftp);
161
162         return ftp;
163 }
164
165 int ftp_get(struct obex_session *os, void *user_data)
166 {
167         struct ftp_session *ftp = user_data;
168         const char *type = obex_get_type(os);
169         int ret;
170
171         DBG("%p", ftp);
172
173         if (ftp->folder == NULL)
174                 return -ENOENT;
175
176         ret = get_by_type(ftp, type);
177         if (ret < 0)
178                 return ret;
179
180         return 0;
181 }
182
183 static int ftp_delete(struct ftp_session *ftp, const char *name)
184 {
185         char *path;
186         int ret = 0;
187
188         DBG("%p name %s", ftp, name);
189
190         if (!(ftp->folder && name))
191                 return -EINVAL;
192
193         path = g_build_filename(ftp->folder, name, NULL);
194
195         if (obex_remove(ftp->os, path) < 0)
196                 ret = -errno;
197
198         g_free(path);
199
200         return ret;
201 }
202
203 int ftp_chkput(struct obex_session *os, void *user_data)
204 {
205         struct ftp_session *ftp = user_data;
206         const char *name = obex_get_name(os);
207         char *path;
208         int ret;
209
210         DBG("%p name %s", ftp, name);
211
212         if (name == NULL)
213                 return -EBADR;
214
215         if (!is_filename(name))
216                 return -EBADR;
217
218         if (obex_get_size(os) == OBJECT_SIZE_DELETE)
219                 return 0;
220
221         path = g_build_filename(ftp->folder, name, NULL);
222
223         ret = obex_put_stream_start(os, path);
224
225         g_free(path);
226
227         return ret;
228 }
229
230 int ftp_put(struct obex_session *os, void *user_data)
231 {
232         struct ftp_session *ftp = user_data;
233         const char *name = obex_get_name(os);
234         ssize_t size = obex_get_size(os);
235
236         DBG("%p name %s size %zd", ftp, name, size);
237
238         if (ftp->folder == NULL)
239                 return -EPERM;
240
241         if (name == NULL)
242                 return -EBADR;
243
244         if (!is_filename(name))
245                 return -EBADR;
246
247         if (size == OBJECT_SIZE_DELETE)
248                 return ftp_delete(ftp, name);
249
250         return 0;
251 }
252
253 int ftp_setpath(struct obex_session *os, void *user_data)
254 {
255         struct ftp_session *ftp = user_data;
256         const char *root_folder, *name;
257         const uint8_t *nonhdr;
258         char *fullname;
259         struct stat dstat;
260         gboolean root;
261         int err;
262
263         if (obex_get_non_header_data(os, &nonhdr) != 2) {
264                 error("Set path failed: flag and constants not found!");
265                 return -EBADMSG;
266         }
267
268         name = obex_get_name(os);
269         root_folder = obex_option_root_folder();
270         root = g_str_equal(root_folder, ftp->folder);
271
272         DBG("%p name %s", ftp, name);
273
274         /* Check flag "Backup" */
275         if ((nonhdr[0] & 0x01) == 0x01) {
276                 DBG("Set to parent path");
277
278                 if (root)
279                         return -EPERM;
280
281                 fullname = g_path_get_dirname(ftp->folder);
282                 set_folder(ftp, fullname);
283                 g_free(fullname);
284
285                 DBG("Set to parent path: %s", ftp->folder);
286
287                 return 0;
288         }
289
290         if (!name) {
291                 DBG("Set path failed: name missing!");
292                 return -EINVAL;
293         }
294
295         if (strlen(name) == 0) {
296                 DBG("Set to root");
297                 set_folder(ftp, root_folder);
298                 return 0;
299         }
300
301         /* Check and set to name path */
302         if (!is_filename(name)) {
303                 error("Set path failed: name incorrect!");
304                 return -EPERM;
305         }
306
307         fullname = g_build_filename(ftp->folder, name, NULL);
308
309         DBG("Fullname: %s", fullname);
310
311         err = verify_path(fullname);
312
313         if (err < 0)
314                 goto done;
315
316         err = stat(fullname, &dstat);
317
318         if (err < 0) {
319                 err = -errno;
320
321                 if (err == -ENOENT)
322                         goto not_found;
323
324                 DBG("stat: %s(%d)", strerror(-err), -err);
325
326                 goto done;
327         }
328
329         if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & S_IRUSR) &&
330                                                 (dstat.st_mode & S_IXUSR)) {
331                 set_folder(ftp, fullname);
332                 goto done;
333         }
334
335         err = -EPERM;
336         goto done;
337
338 not_found:
339         if (nonhdr[0] != 0) {
340                 err = -ENOENT;
341                 goto done;
342         }
343
344         if (mkdir(fullname, 0755) <  0) {
345                 err = -errno;
346                 DBG("mkdir: %s(%d)", strerror(-err), -err);
347                 goto done;
348         }
349
350         err = 0;
351         set_folder(ftp, fullname);
352
353 done:
354         g_free(fullname);
355         return err;
356 }
357
358 static gboolean is_valid_path(const char *path)
359 {
360         gchar **elements, **cur;
361         int depth = 0;
362
363         elements = g_strsplit(path, "/", 0);
364
365         for (cur = elements; *cur != NULL; cur++) {
366                 if (**cur == '\0' || strcmp(*cur, ".") == 0)
367                         continue;
368
369                 if (strcmp(*cur, "..") == 0) {
370                         depth--;
371                         if (depth < 0)
372                                 break;
373                         continue;
374                 }
375
376                 depth++;
377         }
378
379         g_strfreev(elements);
380
381         if (depth < 0)
382                 return FALSE;
383
384         return TRUE;
385 }
386
387 static char *ftp_build_filename(struct ftp_session *ftp, const char *destname)
388 {
389         char *filename;
390
391         /* DestName can either be relative or absolute (FTP style) */
392         if (destname[0] == '/')
393                 filename = g_build_filename(obex_option_root_folder(),
394                                                                 destname, NULL);
395         else
396                 filename = g_build_filename(ftp->folder, destname, NULL);
397
398         if (is_valid_path(filename + strlen(obex_option_root_folder())))
399                 return filename;
400
401         g_free(filename);
402
403         return NULL;
404 }
405
406 static int ftp_copy(struct ftp_session *ftp, const char *name,
407                                                         const char *destname)
408 {
409         char *source, *destination, *destdir;
410         int ret;
411
412         DBG("%p name %s destination %s", ftp, name, destname);
413
414         if (ftp->folder == NULL) {
415                 error("No folder set");
416                 return -ENOENT;
417         }
418
419         if (name == NULL || destname == NULL)
420                 return -EINVAL;
421
422         destination = ftp_build_filename(ftp, destname);
423
424         if (destination == NULL)
425                 return -EBADR;
426
427         destdir = g_path_get_dirname(destination);
428         ret = verify_path(destdir);
429         g_free(destdir);
430
431         if (ret < 0)
432                 return ret;
433
434         source = g_build_filename(ftp->folder, name, NULL);
435
436         ret = obex_copy(ftp->os, source, destination);
437
438         g_free(source);
439         g_free(destination);
440
441         return ret;
442 }
443
444 static int ftp_move(struct ftp_session *ftp, const char *name,
445                                                         const char *destname)
446 {
447         char *source, *destination, *destdir;
448         int ret;
449
450         DBG("%p name %s destname %s", ftp, name, destname);
451
452         if (ftp->folder == NULL) {
453                 error("No folder set");
454                 return -ENOENT;
455         }
456
457         if (name == NULL || destname == NULL)
458                 return -EINVAL;
459
460         destination = ftp_build_filename(ftp, destname);
461
462         if (destination == NULL)
463                 return -EBADR;
464
465         destdir = g_path_get_dirname(destination);
466         ret = verify_path(destdir);
467         g_free(destdir);
468
469         if (ret < 0)
470                 return ret;
471
472         source = g_build_filename(ftp->folder, name, NULL);
473
474         ret = obex_move(ftp->os, source, destination);
475
476         g_free(source);
477         g_free(destination);
478
479         return ret;
480 }
481
482 int ftp_action(struct obex_session *os, void *user_data)
483 {
484         struct ftp_session *ftp = user_data;
485         const char *name, *destname;
486         uint8_t action_id;
487
488         name = obex_get_name(os);
489         if (name == NULL || !is_filename(name))
490                 return -EBADR;
491
492         destname = obex_get_destname(os);
493         action_id = obex_get_action_id(os);
494
495         DBG("%p action 0x%x", ftp, action_id);
496
497         switch (action_id) {
498         case 0x00: /* Copy Object */
499                 return ftp_copy(ftp, name, destname);
500         case 0x01: /* Move/Rename Object */
501                 return ftp_move(ftp, name, destname);
502         default:
503                 return -ENOSYS;
504         }
505 }
506
507 void ftp_disconnect(struct obex_session *os, void *user_data)
508 {
509         struct ftp_session *ftp = user_data;
510
511         DBG("%p", ftp);
512
513         manager_unregister_session(os);
514
515         g_free(ftp->folder);
516         g_free(ftp);
517 }
518
519 static struct obex_service_driver ftp = {
520         .name = "File Transfer server",
521         .service = OBEX_FTP,
522         .channel = FTP_CHANNEL,
523         .port = OBEX_PORT_RANDOM,
524         .secure = TRUE,
525         .record = FTP_RECORD,
526         .target = FTP_TARGET,
527         .target_size = TARGET_SIZE,
528         .connect = ftp_connect,
529         .get = ftp_get,
530         .put = ftp_put,
531         .chkput = ftp_chkput,
532         .setpath = ftp_setpath,
533         .action = ftp_action,
534         .disconnect = ftp_disconnect
535 };
536
537 static int ftp_init(void)
538 {
539         return obex_service_driver_register(&ftp);
540 }
541
542 static void ftp_exit(void)
543 {
544         obex_service_driver_unregister(&ftp);
545 }
546
547 OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit)