ftp.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * OBEX Server
  5. *
  6. * Copyright (C) 2007-2010 Nokia Corporation
  7. * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
  8. *
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #include <fcntl.h>
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <dirent.h>
  20. #include <sys/stat.h>
  21. #include <sys/types.h>
  22. #include <sys/wait.h>
  23. #include <unistd.h>
  24. #include <inttypes.h>
  25. #include <glib.h>
  26. #include "obexd/src/obexd.h"
  27. #include "obexd/src/plugin.h"
  28. #include "obexd/src/log.h"
  29. #include "obexd/src/obex.h"
  30. #include "obexd/src/manager.h"
  31. #include "obexd/src/mimetype.h"
  32. #include "obexd/src/service.h"
  33. #include "ftp.h"
  34. #include "filesystem.h"
  35. #define LST_TYPE "x-obex/folder-listing"
  36. #define CAP_TYPE "x-obex/capability"
  37. static const uint8_t FTP_TARGET[TARGET_SIZE] = {
  38. 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
  39. 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
  40. struct ftp_session {
  41. struct obex_session *os;
  42. struct obex_transfer *transfer;
  43. char *folder;
  44. };
  45. static void set_folder(struct ftp_session *ftp, const char *new_folder)
  46. {
  47. DBG("%p folder %s", ftp, new_folder);
  48. g_free(ftp->folder);
  49. ftp->folder = new_folder ? g_strdup(new_folder) : NULL;
  50. }
  51. static int get_by_type(struct ftp_session *ftp, const char *type)
  52. {
  53. struct obex_session *os = ftp->os;
  54. const char *capability = obex_option_capability();
  55. const char *name = obex_get_name(os);
  56. char *path;
  57. int err;
  58. DBG("%p name %s type %s", ftp, name, type);
  59. if (type == NULL && name == NULL)
  60. return -EBADR;
  61. if (type != NULL && g_ascii_strcasecmp(type, CAP_TYPE) == 0)
  62. return obex_get_stream_start(os, capability);
  63. if (name != NULL && !is_filename(name))
  64. return -EBADR;
  65. path = g_build_filename(ftp->folder, name, NULL);
  66. err = obex_get_stream_start(os, path);
  67. g_free(path);
  68. return err;
  69. }
  70. void *ftp_connect(struct obex_session *os, int *err)
  71. {
  72. struct ftp_session *ftp;
  73. const char *root_folder;
  74. DBG("");
  75. root_folder = obex_option_root_folder();
  76. manager_register_session(os);
  77. ftp = g_new0(struct ftp_session, 1);
  78. set_folder(ftp, root_folder);
  79. ftp->os = os;
  80. if (err)
  81. *err = 0;
  82. ftp->transfer = manager_register_transfer(os);
  83. DBG("session %p created", ftp);
  84. return ftp;
  85. }
  86. int ftp_get(struct obex_session *os, void *user_data)
  87. {
  88. struct ftp_session *ftp = user_data;
  89. const char *type = obex_get_type(os);
  90. int ret;
  91. DBG("%p", ftp);
  92. if (ftp->folder == NULL)
  93. return -ENOENT;
  94. ret = get_by_type(ftp, type);
  95. if (ret < 0)
  96. return ret;
  97. /* Only track progress of file transfer */
  98. if (type == NULL)
  99. manager_emit_transfer_started(ftp->transfer);
  100. return 0;
  101. }
  102. static int ftp_delete(struct ftp_session *ftp, const char *name)
  103. {
  104. char *path;
  105. int ret = 0;
  106. DBG("%p name %s", ftp, name);
  107. if (!(ftp->folder && name))
  108. return -EINVAL;
  109. path = g_build_filename(ftp->folder, name, NULL);
  110. if (obex_remove(ftp->os, path) < 0)
  111. ret = -errno;
  112. g_free(path);
  113. return ret;
  114. }
  115. int ftp_chkput(struct obex_session *os, void *user_data)
  116. {
  117. struct ftp_session *ftp = user_data;
  118. const char *name = obex_get_name(os);
  119. char *path;
  120. int ret;
  121. DBG("%p name %s", ftp, name);
  122. if (name == NULL)
  123. return -EBADR;
  124. if (!is_filename(name))
  125. return -EBADR;
  126. if (obex_get_size(os) == OBJECT_SIZE_DELETE)
  127. return 0;
  128. path = g_build_filename(ftp->folder, name, NULL);
  129. ret = obex_put_stream_start(os, path);
  130. if (ret == 0)
  131. manager_emit_transfer_started(ftp->transfer);
  132. g_free(path);
  133. return ret;
  134. }
  135. int ftp_put(struct obex_session *os, void *user_data)
  136. {
  137. struct ftp_session *ftp = user_data;
  138. const char *name = obex_get_name(os);
  139. ssize_t size = obex_get_size(os);
  140. DBG("%p name %s size %zd", ftp, name, size);
  141. if (ftp->folder == NULL)
  142. return -EPERM;
  143. if (name == NULL)
  144. return -EBADR;
  145. if (!is_filename(name))
  146. return -EBADR;
  147. if (size == OBJECT_SIZE_DELETE)
  148. return ftp_delete(ftp, name);
  149. return 0;
  150. }
  151. int ftp_setpath(struct obex_session *os, void *user_data)
  152. {
  153. struct ftp_session *ftp = user_data;
  154. const char *root_folder, *name;
  155. const uint8_t *nonhdr;
  156. char *fullname;
  157. struct stat dstat;
  158. gboolean root;
  159. int err;
  160. if (obex_get_non_header_data(os, &nonhdr) != 2) {
  161. error("Set path failed: flag and constants not found!");
  162. return -EBADMSG;
  163. }
  164. name = obex_get_name(os);
  165. root_folder = obex_option_root_folder();
  166. root = g_str_equal(root_folder, ftp->folder);
  167. DBG("%p name %s", ftp, name);
  168. /* Check flag "Backup" */
  169. if ((nonhdr[0] & 0x01) == 0x01) {
  170. DBG("Set to parent path");
  171. if (root)
  172. return -EPERM;
  173. fullname = g_path_get_dirname(ftp->folder);
  174. set_folder(ftp, fullname);
  175. g_free(fullname);
  176. DBG("Set to parent path: %s", ftp->folder);
  177. return 0;
  178. }
  179. if (!name) {
  180. DBG("Set path failed: name missing!");
  181. return -EINVAL;
  182. }
  183. if (strlen(name) == 0) {
  184. DBG("Set to root");
  185. set_folder(ftp, root_folder);
  186. return 0;
  187. }
  188. /* Check and set to name path */
  189. if (!is_filename(name)) {
  190. error("Set path failed: name incorrect!");
  191. return -EPERM;
  192. }
  193. fullname = g_build_filename(ftp->folder, name, NULL);
  194. DBG("Fullname: %s", fullname);
  195. err = verify_path(fullname);
  196. if (err == -ENOENT)
  197. goto not_found;
  198. if (err < 0)
  199. goto done;
  200. err = stat(fullname, &dstat);
  201. if (err < 0) {
  202. err = -errno;
  203. if (err == -ENOENT)
  204. goto not_found;
  205. DBG("stat: %s(%d)", strerror(-err), -err);
  206. goto done;
  207. }
  208. if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & 0400) &&
  209. (dstat.st_mode & 0100)) {
  210. set_folder(ftp, fullname);
  211. goto done;
  212. }
  213. err = -EPERM;
  214. goto done;
  215. not_found:
  216. if (nonhdr[0] != 0) {
  217. err = -ENOENT;
  218. goto done;
  219. }
  220. if (mkdir(fullname, 0755) < 0) {
  221. err = -errno;
  222. DBG("mkdir: %s(%d)", strerror(-err), -err);
  223. goto done;
  224. }
  225. err = 0;
  226. set_folder(ftp, fullname);
  227. done:
  228. g_free(fullname);
  229. return err;
  230. }
  231. static gboolean is_valid_path(const char *path)
  232. {
  233. char **elements, **cur;
  234. int depth = 0;
  235. elements = g_strsplit(path, "/", 0);
  236. for (cur = elements; *cur != NULL; cur++) {
  237. if (**cur == '\0' || strcmp(*cur, ".") == 0)
  238. continue;
  239. if (strcmp(*cur, "..") == 0) {
  240. depth--;
  241. if (depth < 0)
  242. break;
  243. continue;
  244. }
  245. depth++;
  246. }
  247. g_strfreev(elements);
  248. if (depth < 0)
  249. return FALSE;
  250. return TRUE;
  251. }
  252. static char *ftp_build_filename(struct ftp_session *ftp, const char *destname)
  253. {
  254. char *filename;
  255. /* DestName can either be relative or absolute (FTP style) */
  256. if (destname[0] == '/')
  257. filename = g_build_filename(obex_option_root_folder(),
  258. destname, NULL);
  259. else
  260. filename = g_build_filename(ftp->folder, destname, NULL);
  261. if (is_valid_path(filename + strlen(obex_option_root_folder())))
  262. return filename;
  263. g_free(filename);
  264. return NULL;
  265. }
  266. static int ftp_copy(struct ftp_session *ftp, const char *name,
  267. const char *destname)
  268. {
  269. char *source, *destination, *destdir;
  270. int ret;
  271. DBG("%p name %s destination %s", ftp, name, destname);
  272. if (ftp->folder == NULL) {
  273. error("No folder set");
  274. return -ENOENT;
  275. }
  276. if (name == NULL || destname == NULL)
  277. return -EINVAL;
  278. destination = ftp_build_filename(ftp, destname);
  279. if (destination == NULL)
  280. return -EBADR;
  281. destdir = g_path_get_dirname(destination);
  282. ret = verify_path(destdir);
  283. g_free(destdir);
  284. if (ret < 0) {
  285. g_free(destination);
  286. return ret;
  287. }
  288. source = g_build_filename(ftp->folder, name, NULL);
  289. ret = obex_copy(ftp->os, source, destination);
  290. g_free(source);
  291. g_free(destination);
  292. return ret;
  293. }
  294. static int ftp_move(struct ftp_session *ftp, const char *name,
  295. const char *destname)
  296. {
  297. char *source, *destination, *destdir;
  298. int ret;
  299. DBG("%p name %s destname %s", ftp, name, destname);
  300. if (ftp->folder == NULL) {
  301. error("No folder set");
  302. return -ENOENT;
  303. }
  304. if (name == NULL || destname == NULL)
  305. return -EINVAL;
  306. destination = ftp_build_filename(ftp, destname);
  307. if (destination == NULL)
  308. return -EBADR;
  309. destdir = g_path_get_dirname(destination);
  310. ret = verify_path(destdir);
  311. g_free(destdir);
  312. if (ret < 0) {
  313. g_free(destination);
  314. return ret;
  315. }
  316. source = g_build_filename(ftp->folder, name, NULL);
  317. ret = obex_move(ftp->os, source, destination);
  318. g_free(source);
  319. g_free(destination);
  320. return ret;
  321. }
  322. int ftp_action(struct obex_session *os, void *user_data)
  323. {
  324. struct ftp_session *ftp = user_data;
  325. const char *name, *destname;
  326. uint8_t action_id;
  327. name = obex_get_name(os);
  328. if (name == NULL || !is_filename(name))
  329. return -EBADR;
  330. destname = obex_get_destname(os);
  331. action_id = obex_get_action_id(os);
  332. DBG("%p action 0x%x", ftp, action_id);
  333. switch (action_id) {
  334. case 0x00: /* Copy Object */
  335. return ftp_copy(ftp, name, destname);
  336. case 0x01: /* Move/Rename Object */
  337. return ftp_move(ftp, name, destname);
  338. default:
  339. return -ENOSYS;
  340. }
  341. }
  342. void ftp_disconnect(struct obex_session *os, void *user_data)
  343. {
  344. struct ftp_session *ftp = user_data;
  345. DBG("%p", ftp);
  346. manager_unregister_session(os);
  347. manager_unregister_transfer(ftp->transfer);
  348. g_free(ftp->folder);
  349. g_free(ftp);
  350. }
  351. static void ftp_progress(struct obex_session *os, void *user_data)
  352. {
  353. struct ftp_session *ftp = user_data;
  354. manager_emit_transfer_progress(ftp->transfer);
  355. }
  356. static void ftp_reset(struct obex_session *os, void *user_data)
  357. {
  358. struct ftp_session *ftp = user_data;
  359. manager_emit_transfer_completed(ftp->transfer);
  360. }
  361. static struct obex_service_driver ftp = {
  362. .name = "File Transfer server",
  363. .service = OBEX_FTP,
  364. .target = FTP_TARGET,
  365. .target_size = TARGET_SIZE,
  366. .connect = ftp_connect,
  367. .progress = ftp_progress,
  368. .get = ftp_get,
  369. .put = ftp_put,
  370. .chkput = ftp_chkput,
  371. .setpath = ftp_setpath,
  372. .action = ftp_action,
  373. .disconnect = ftp_disconnect,
  374. .reset = ftp_reset
  375. };
  376. static int ftp_init(void)
  377. {
  378. return obex_service_driver_register(&ftp);
  379. }
  380. static void ftp_exit(void)
  381. {
  382. obex_service_driver_unregister(&ftp);
  383. }
  384. OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit)