| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * OBEX Server
- *
- * Copyright (C) 2007-2010 Nokia Corporation
- * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <fcntl.h>
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <inttypes.h>
- #include <glib.h>
- #include "obexd/src/obexd.h"
- #include "obexd/src/plugin.h"
- #include "obexd/src/log.h"
- #include "obexd/src/obex.h"
- #include "obexd/src/manager.h"
- #include "obexd/src/mimetype.h"
- #include "obexd/src/service.h"
- #include "ftp.h"
- #include "filesystem.h"
- #define LST_TYPE "x-obex/folder-listing"
- #define CAP_TYPE "x-obex/capability"
- static const uint8_t FTP_TARGET[TARGET_SIZE] = {
- 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
- 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
- struct ftp_session {
- struct obex_session *os;
- struct obex_transfer *transfer;
- char *folder;
- };
- static void set_folder(struct ftp_session *ftp, const char *new_folder)
- {
- DBG("%p folder %s", ftp, new_folder);
- g_free(ftp->folder);
- ftp->folder = new_folder ? g_strdup(new_folder) : NULL;
- }
- static int get_by_type(struct ftp_session *ftp, const char *type)
- {
- struct obex_session *os = ftp->os;
- const char *capability = obex_option_capability();
- const char *name = obex_get_name(os);
- char *path;
- int err;
- DBG("%p name %s type %s", ftp, name, type);
- if (type == NULL && name == NULL)
- return -EBADR;
- if (type != NULL && g_ascii_strcasecmp(type, CAP_TYPE) == 0)
- return obex_get_stream_start(os, capability);
- if (name != NULL && !is_filename(name))
- return -EBADR;
- path = g_build_filename(ftp->folder, name, NULL);
- err = obex_get_stream_start(os, path);
- g_free(path);
- return err;
- }
- void *ftp_connect(struct obex_session *os, int *err)
- {
- struct ftp_session *ftp;
- const char *root_folder;
- DBG("");
- root_folder = obex_option_root_folder();
- manager_register_session(os);
- ftp = g_new0(struct ftp_session, 1);
- set_folder(ftp, root_folder);
- ftp->os = os;
- if (err)
- *err = 0;
- ftp->transfer = manager_register_transfer(os);
- DBG("session %p created", ftp);
- return ftp;
- }
- int ftp_get(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- const char *type = obex_get_type(os);
- int ret;
- DBG("%p", ftp);
- if (ftp->folder == NULL)
- return -ENOENT;
- ret = get_by_type(ftp, type);
- if (ret < 0)
- return ret;
- /* Only track progress of file transfer */
- if (type == NULL)
- manager_emit_transfer_started(ftp->transfer);
- return 0;
- }
- static int ftp_delete(struct ftp_session *ftp, const char *name)
- {
- char *path;
- int ret = 0;
- DBG("%p name %s", ftp, name);
- if (!(ftp->folder && name))
- return -EINVAL;
- path = g_build_filename(ftp->folder, name, NULL);
- if (obex_remove(ftp->os, path) < 0)
- ret = -errno;
- g_free(path);
- return ret;
- }
- int ftp_chkput(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- const char *name = obex_get_name(os);
- char *path;
- int ret;
- DBG("%p name %s", ftp, name);
- if (name == NULL)
- return -EBADR;
- if (!is_filename(name))
- return -EBADR;
- if (obex_get_size(os) == OBJECT_SIZE_DELETE)
- return 0;
- path = g_build_filename(ftp->folder, name, NULL);
- ret = obex_put_stream_start(os, path);
- if (ret == 0)
- manager_emit_transfer_started(ftp->transfer);
- g_free(path);
- return ret;
- }
- int ftp_put(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- const char *name = obex_get_name(os);
- ssize_t size = obex_get_size(os);
- DBG("%p name %s size %zd", ftp, name, size);
- if (ftp->folder == NULL)
- return -EPERM;
- if (name == NULL)
- return -EBADR;
- if (!is_filename(name))
- return -EBADR;
- if (size == OBJECT_SIZE_DELETE)
- return ftp_delete(ftp, name);
- return 0;
- }
- int ftp_setpath(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- const char *root_folder, *name;
- const uint8_t *nonhdr;
- char *fullname;
- struct stat dstat;
- gboolean root;
- int err;
- if (obex_get_non_header_data(os, &nonhdr) != 2) {
- error("Set path failed: flag and constants not found!");
- return -EBADMSG;
- }
- name = obex_get_name(os);
- root_folder = obex_option_root_folder();
- root = g_str_equal(root_folder, ftp->folder);
- DBG("%p name %s", ftp, name);
- /* Check flag "Backup" */
- if ((nonhdr[0] & 0x01) == 0x01) {
- DBG("Set to parent path");
- if (root)
- return -EPERM;
- fullname = g_path_get_dirname(ftp->folder);
- set_folder(ftp, fullname);
- g_free(fullname);
- DBG("Set to parent path: %s", ftp->folder);
- return 0;
- }
- if (!name) {
- DBG("Set path failed: name missing!");
- return -EINVAL;
- }
- if (strlen(name) == 0) {
- DBG("Set to root");
- set_folder(ftp, root_folder);
- return 0;
- }
- /* Check and set to name path */
- if (!is_filename(name)) {
- error("Set path failed: name incorrect!");
- return -EPERM;
- }
- fullname = g_build_filename(ftp->folder, name, NULL);
- DBG("Fullname: %s", fullname);
- err = verify_path(fullname);
- if (err == -ENOENT)
- goto not_found;
- if (err < 0)
- goto done;
- err = stat(fullname, &dstat);
- if (err < 0) {
- err = -errno;
- if (err == -ENOENT)
- goto not_found;
- DBG("stat: %s(%d)", strerror(-err), -err);
- goto done;
- }
- if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & 0400) &&
- (dstat.st_mode & 0100)) {
- set_folder(ftp, fullname);
- goto done;
- }
- err = -EPERM;
- goto done;
- not_found:
- if (nonhdr[0] != 0) {
- err = -ENOENT;
- goto done;
- }
- if (mkdir(fullname, 0755) < 0) {
- err = -errno;
- DBG("mkdir: %s(%d)", strerror(-err), -err);
- goto done;
- }
- err = 0;
- set_folder(ftp, fullname);
- done:
- g_free(fullname);
- return err;
- }
- static gboolean is_valid_path(const char *path)
- {
- char **elements, **cur;
- int depth = 0;
- elements = g_strsplit(path, "/", 0);
- for (cur = elements; *cur != NULL; cur++) {
- if (**cur == '\0' || strcmp(*cur, ".") == 0)
- continue;
- if (strcmp(*cur, "..") == 0) {
- depth--;
- if (depth < 0)
- break;
- continue;
- }
- depth++;
- }
- g_strfreev(elements);
- if (depth < 0)
- return FALSE;
- return TRUE;
- }
- static char *ftp_build_filename(struct ftp_session *ftp, const char *destname)
- {
- char *filename;
- /* DestName can either be relative or absolute (FTP style) */
- if (destname[0] == '/')
- filename = g_build_filename(obex_option_root_folder(),
- destname, NULL);
- else
- filename = g_build_filename(ftp->folder, destname, NULL);
- if (is_valid_path(filename + strlen(obex_option_root_folder())))
- return filename;
- g_free(filename);
- return NULL;
- }
- static int ftp_copy(struct ftp_session *ftp, const char *name,
- const char *destname)
- {
- char *source, *destination, *destdir;
- int ret;
- DBG("%p name %s destination %s", ftp, name, destname);
- if (ftp->folder == NULL) {
- error("No folder set");
- return -ENOENT;
- }
- if (name == NULL || destname == NULL)
- return -EINVAL;
- destination = ftp_build_filename(ftp, destname);
- if (destination == NULL)
- return -EBADR;
- destdir = g_path_get_dirname(destination);
- ret = verify_path(destdir);
- g_free(destdir);
- if (ret < 0) {
- g_free(destination);
- return ret;
- }
- source = g_build_filename(ftp->folder, name, NULL);
- ret = obex_copy(ftp->os, source, destination);
- g_free(source);
- g_free(destination);
- return ret;
- }
- static int ftp_move(struct ftp_session *ftp, const char *name,
- const char *destname)
- {
- char *source, *destination, *destdir;
- int ret;
- DBG("%p name %s destname %s", ftp, name, destname);
- if (ftp->folder == NULL) {
- error("No folder set");
- return -ENOENT;
- }
- if (name == NULL || destname == NULL)
- return -EINVAL;
- destination = ftp_build_filename(ftp, destname);
- if (destination == NULL)
- return -EBADR;
- destdir = g_path_get_dirname(destination);
- ret = verify_path(destdir);
- g_free(destdir);
- if (ret < 0) {
- g_free(destination);
- return ret;
- }
- source = g_build_filename(ftp->folder, name, NULL);
- ret = obex_move(ftp->os, source, destination);
- g_free(source);
- g_free(destination);
- return ret;
- }
- int ftp_action(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- const char *name, *destname;
- uint8_t action_id;
- name = obex_get_name(os);
- if (name == NULL || !is_filename(name))
- return -EBADR;
- destname = obex_get_destname(os);
- action_id = obex_get_action_id(os);
- DBG("%p action 0x%x", ftp, action_id);
- switch (action_id) {
- case 0x00: /* Copy Object */
- return ftp_copy(ftp, name, destname);
- case 0x01: /* Move/Rename Object */
- return ftp_move(ftp, name, destname);
- default:
- return -ENOSYS;
- }
- }
- void ftp_disconnect(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- DBG("%p", ftp);
- manager_unregister_session(os);
- manager_unregister_transfer(ftp->transfer);
- g_free(ftp->folder);
- g_free(ftp);
- }
- static void ftp_progress(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- manager_emit_transfer_progress(ftp->transfer);
- }
- static void ftp_reset(struct obex_session *os, void *user_data)
- {
- struct ftp_session *ftp = user_data;
- manager_emit_transfer_completed(ftp->transfer);
- }
- static struct obex_service_driver ftp = {
- .name = "File Transfer server",
- .service = OBEX_FTP,
- .target = FTP_TARGET,
- .target_size = TARGET_SIZE,
- .connect = ftp_connect,
- .progress = ftp_progress,
- .get = ftp_get,
- .put = ftp_put,
- .chkput = ftp_chkput,
- .setpath = ftp_setpath,
- .action = ftp_action,
- .disconnect = ftp_disconnect,
- .reset = ftp_reset
- };
- static int ftp_init(void)
- {
- return obex_service_driver_register(&ftp);
- }
- static void ftp_exit(void)
- {
- obex_service_driver_unregister(&ftp);
- }
- OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit)
|