transfer.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * OBEX Client
  5. *
  6. * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
  7. * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved.
  8. *
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <fcntl.h>
  18. #include <unistd.h>
  19. #include <string.h>
  20. #include <sys/stat.h>
  21. #include <inttypes.h>
  22. #include <glib.h>
  23. #include "gdbus/gdbus.h"
  24. #include "gobex/gobex.h"
  25. #include "obexd/src/log.h"
  26. #include "transfer.h"
  27. #define TRANSFER_INTERFACE "org.bluez.obex.Transfer1"
  28. #define ERROR_INTERFACE "org.bluez.obex.Error"
  29. #define OBC_TRANSFER_ERROR obc_transfer_error_quark()
  30. #define FIRST_PACKET_TIMEOUT 60
  31. static guint64 counter = 0;
  32. struct transfer_callback {
  33. transfer_callback_t func;
  34. void *data;
  35. };
  36. enum {
  37. TRANSFER_STATUS_QUEUED = 0,
  38. TRANSFER_STATUS_ACTIVE,
  39. TRANSFER_STATUS_SUSPENDED,
  40. TRANSFER_STATUS_COMPLETE,
  41. TRANSFER_STATUS_SUSPENDED_QUEUED,
  42. TRANSFER_STATUS_ERROR
  43. };
  44. struct obc_transfer {
  45. GObex *obex;
  46. uint8_t status;
  47. GObexApparam *apparam;
  48. guint8 op;
  49. struct transfer_callback *callback;
  50. DBusConnection *conn;
  51. DBusMessage *msg;
  52. char *session; /* Session path */
  53. char *owner; /* Transfer initiator */
  54. char *path; /* Transfer path */
  55. char *filename; /* Transfer file location */
  56. char *name; /* Transfer object name */
  57. char *type; /* Transfer object type */
  58. int fd;
  59. guint req;
  60. guint xfer;
  61. gint64 size;
  62. gint64 transferred;
  63. gint64 progress;
  64. guint progress_id;
  65. };
  66. static GQuark obc_transfer_error_quark(void)
  67. {
  68. return g_quark_from_static_string("obc-transfer-error-quark");
  69. }
  70. DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer,
  71. DBusMessage *message)
  72. {
  73. DBusMessage *reply;
  74. DBusMessageIter iter;
  75. reply = dbus_message_new_method_return(message);
  76. if (reply == NULL)
  77. return NULL;
  78. dbus_message_iter_init_append(reply, &iter);
  79. dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
  80. &transfer->path);
  81. g_dbus_get_properties(transfer->conn, transfer->path,
  82. TRANSFER_INTERFACE, &iter);
  83. return reply;
  84. }
  85. static void abort_complete(GObex *obex, GError *err, gpointer user_data)
  86. {
  87. struct obc_transfer *transfer = user_data;
  88. struct transfer_callback *callback = transfer->callback;
  89. DBusMessage *reply;
  90. transfer->xfer = 0;
  91. reply = dbus_message_new_method_return(transfer->msg);
  92. if (reply)
  93. g_dbus_send_message(transfer->conn, reply);
  94. dbus_message_unref(transfer->msg);
  95. transfer->msg = NULL;
  96. if (callback == NULL)
  97. return;
  98. if (err) {
  99. callback->func(transfer, err, callback->data);
  100. } else {
  101. GError *abort_err;
  102. abort_err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
  103. "Transfer cancelled by user");
  104. callback->func(transfer, abort_err, callback->data);
  105. g_error_free(abort_err);
  106. }
  107. }
  108. static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
  109. DBusMessage *message, void *user_data)
  110. {
  111. struct obc_transfer *transfer = user_data;
  112. const char *sender;
  113. sender = dbus_message_get_sender(message);
  114. if (g_strcmp0(transfer->owner, sender) != 0)
  115. return g_dbus_create_error(message,
  116. ERROR_INTERFACE ".NotAuthorized",
  117. "Not Authorized");
  118. if (transfer->msg != NULL)
  119. return g_dbus_create_error(message,
  120. ERROR_INTERFACE ".InProgress",
  121. "Cancellation already in progress");
  122. if (transfer->status == TRANSFER_STATUS_SUSPENDED)
  123. g_obex_resume(transfer->obex);
  124. if (transfer->req > 0) {
  125. if (!g_obex_cancel_req(transfer->obex, transfer->req, TRUE))
  126. return g_dbus_create_error(message,
  127. ERROR_INTERFACE ".Failed",
  128. "Failed");
  129. transfer->req = 0;
  130. }
  131. if (transfer->xfer == 0) {
  132. struct transfer_callback *callback = transfer->callback;
  133. if (callback != NULL) {
  134. GError *err;
  135. err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
  136. "Transfer cancelled by user");
  137. callback->func(transfer, err, callback->data);
  138. g_error_free(err);
  139. }
  140. return dbus_message_new_method_return(message);
  141. }
  142. if (transfer->progress_id != 0) {
  143. g_source_remove(transfer->progress_id);
  144. transfer->progress_id = 0;
  145. }
  146. if (!g_obex_cancel_transfer(transfer->xfer, abort_complete, transfer))
  147. return g_dbus_create_error(message,
  148. ERROR_INTERFACE ".Failed",
  149. "Failed");
  150. transfer->msg = dbus_message_ref(message);
  151. return NULL;
  152. }
  153. static void transfer_set_status(struct obc_transfer *transfer, uint8_t status)
  154. {
  155. if (transfer->status == status)
  156. return;
  157. transfer->status = status;
  158. g_dbus_emit_property_changed(transfer->conn, transfer->path,
  159. TRANSFER_INTERFACE, "Status");
  160. }
  161. static DBusMessage *obc_transfer_suspend(DBusConnection *connection,
  162. DBusMessage *message, void *user_data)
  163. {
  164. struct obc_transfer *transfer = user_data;
  165. const char *sender;
  166. uint8_t status;
  167. sender = dbus_message_get_sender(message);
  168. if (g_strcmp0(transfer->owner, sender) != 0)
  169. return g_dbus_create_error(message,
  170. ERROR_INTERFACE ".NotAuthorized",
  171. "Not Authorized");
  172. switch (transfer->status) {
  173. case TRANSFER_STATUS_QUEUED:
  174. status = TRANSFER_STATUS_SUSPENDED_QUEUED;
  175. break;
  176. case TRANSFER_STATUS_ACTIVE:
  177. if (transfer->xfer)
  178. g_obex_suspend(transfer->obex);
  179. status = TRANSFER_STATUS_SUSPENDED;
  180. break;
  181. default:
  182. return g_dbus_create_error(message,
  183. ERROR_INTERFACE ".NotInProgress",
  184. "Not in progress");
  185. }
  186. transfer_set_status(transfer, status);
  187. return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
  188. }
  189. static DBusMessage *obc_transfer_resume(DBusConnection *connection,
  190. DBusMessage *message, void *user_data)
  191. {
  192. struct obc_transfer *transfer = user_data;
  193. const char *sender;
  194. uint8_t status;
  195. sender = dbus_message_get_sender(message);
  196. if (g_strcmp0(transfer->owner, sender) != 0)
  197. return g_dbus_create_error(message,
  198. ERROR_INTERFACE ".NotAuthorized",
  199. "Not Authorized");
  200. switch (transfer->status) {
  201. case TRANSFER_STATUS_SUSPENDED_QUEUED:
  202. status = TRANSFER_STATUS_QUEUED;
  203. break;
  204. case TRANSFER_STATUS_SUSPENDED:
  205. if (transfer->xfer)
  206. g_obex_resume(transfer->obex);
  207. else
  208. obc_transfer_start(transfer, NULL, NULL);
  209. status = TRANSFER_STATUS_ACTIVE;
  210. break;
  211. default:
  212. return g_dbus_create_error(message,
  213. ERROR_INTERFACE ".NotInProgress",
  214. "Not in progress");
  215. }
  216. transfer_set_status(transfer, status);
  217. return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
  218. }
  219. static gboolean name_exists(const GDBusPropertyTable *property, void *data)
  220. {
  221. struct obc_transfer *transfer = data;
  222. return transfer->name != NULL;
  223. }
  224. static gboolean get_name(const GDBusPropertyTable *property,
  225. DBusMessageIter *iter, void *data)
  226. {
  227. struct obc_transfer *transfer = data;
  228. if (transfer->name == NULL)
  229. return FALSE;
  230. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
  231. &transfer->name);
  232. return TRUE;
  233. }
  234. static gboolean get_size(const GDBusPropertyTable *property,
  235. DBusMessageIter *iter, void *data)
  236. {
  237. struct obc_transfer *transfer = data;
  238. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
  239. &transfer->size);
  240. return TRUE;
  241. }
  242. static gboolean filename_exists(const GDBusPropertyTable *property, void *data)
  243. {
  244. struct obc_transfer *transfer = data;
  245. return transfer->filename != NULL;
  246. }
  247. static gboolean get_filename(const GDBusPropertyTable *property,
  248. DBusMessageIter *iter, void *data)
  249. {
  250. struct obc_transfer *transfer = data;
  251. if (transfer->filename == NULL)
  252. return FALSE;
  253. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
  254. &transfer->filename);
  255. return TRUE;
  256. }
  257. static gboolean transferred_exists(const GDBusPropertyTable *property,
  258. void *data)
  259. {
  260. struct obc_transfer *transfer = data;
  261. return transfer->obex != NULL;
  262. }
  263. static gboolean get_transferred(const GDBusPropertyTable *property,
  264. DBusMessageIter *iter, void *data)
  265. {
  266. struct obc_transfer *transfer = data;
  267. if (transfer->obex == NULL)
  268. return FALSE;
  269. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
  270. &transfer->progress);
  271. return TRUE;
  272. }
  273. static const char *status2str(uint8_t status)
  274. {
  275. switch (status) {
  276. case TRANSFER_STATUS_QUEUED:
  277. return "queued";
  278. case TRANSFER_STATUS_ACTIVE:
  279. return "active";
  280. case TRANSFER_STATUS_SUSPENDED_QUEUED:
  281. case TRANSFER_STATUS_SUSPENDED:
  282. return "suspended";
  283. case TRANSFER_STATUS_COMPLETE:
  284. return "complete";
  285. case TRANSFER_STATUS_ERROR:
  286. default:
  287. return "error";
  288. }
  289. }
  290. static gboolean get_status(const GDBusPropertyTable *property,
  291. DBusMessageIter *iter, void *data)
  292. {
  293. struct obc_transfer *transfer = data;
  294. const char *status = status2str(transfer->status);
  295. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
  296. return TRUE;
  297. }
  298. static gboolean get_session(const GDBusPropertyTable *property,
  299. DBusMessageIter *iter, void *data)
  300. {
  301. struct obc_transfer *transfer = data;
  302. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
  303. &transfer->session);
  304. return TRUE;
  305. }
  306. static const GDBusMethodTable obc_transfer_methods[] = {
  307. { GDBUS_METHOD("Suspend", NULL, NULL, obc_transfer_suspend) },
  308. { GDBUS_METHOD("Resume", NULL, NULL, obc_transfer_resume) },
  309. { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL,
  310. obc_transfer_cancel) },
  311. { }
  312. };
  313. static const GDBusPropertyTable obc_transfer_properties[] = {
  314. { "Status", "s", get_status },
  315. { "Name", "s", get_name, NULL, name_exists },
  316. { "Size", "t", get_size },
  317. { "Filename", "s", get_filename, NULL, filename_exists },
  318. { "Transferred", "t", get_transferred, NULL, transferred_exists },
  319. { "Session", "o", get_session },
  320. { }
  321. };
  322. static void obc_transfer_free(struct obc_transfer *transfer)
  323. {
  324. DBG("%p", transfer);
  325. if (transfer->status == TRANSFER_STATUS_SUSPENDED)
  326. g_obex_resume(transfer->obex);
  327. if (transfer->req > 0)
  328. g_obex_cancel_req(transfer->obex, transfer->req, TRUE);
  329. if (transfer->xfer)
  330. g_obex_cancel_transfer(transfer->xfer, NULL, NULL);
  331. if (transfer->progress_id != 0) {
  332. g_source_remove(transfer->progress_id);
  333. transfer->progress_id = 0;
  334. }
  335. if (transfer->op == G_OBEX_OP_GET &&
  336. transfer->status != TRANSFER_STATUS_COMPLETE &&
  337. transfer->filename)
  338. remove(transfer->filename);
  339. if (transfer->fd > 0)
  340. close(transfer->fd);
  341. if (transfer->apparam != NULL)
  342. g_obex_apparam_free(transfer->apparam);
  343. if (transfer->conn)
  344. dbus_connection_unref(transfer->conn);
  345. if (transfer->msg)
  346. dbus_message_unref(transfer->msg);
  347. if (transfer->obex)
  348. g_obex_unref(transfer->obex);
  349. g_free(transfer->callback);
  350. g_free(transfer->owner);
  351. g_free(transfer->filename);
  352. g_free(transfer->name);
  353. g_free(transfer->type);
  354. g_free(transfer->session);
  355. g_free(transfer->path);
  356. g_free(transfer);
  357. }
  358. static struct obc_transfer *obc_transfer_create(guint8 op,
  359. const char *filename,
  360. const char *name,
  361. const char *type)
  362. {
  363. struct obc_transfer *transfer;
  364. transfer = g_new0(struct obc_transfer, 1);
  365. transfer->op = op;
  366. transfer->filename = g_strdup(filename);
  367. transfer->name = g_strdup(name);
  368. transfer->type = g_strdup(type);
  369. return transfer;
  370. }
  371. gboolean obc_transfer_register(struct obc_transfer *transfer,
  372. DBusConnection *conn,
  373. const char *session,
  374. const char *owner,
  375. GError **err)
  376. {
  377. transfer->owner = g_strdup(owner);
  378. transfer->session = g_strdup(session);
  379. transfer->path = g_strdup_printf("%s/transfer%ju", session, counter++);
  380. transfer->conn = dbus_connection_ref(conn);
  381. if (transfer->conn == NULL) {
  382. g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
  383. "Unable to connect to D-Bus");
  384. return FALSE;
  385. }
  386. if (g_dbus_register_interface(transfer->conn, transfer->path,
  387. TRANSFER_INTERFACE,
  388. obc_transfer_methods, NULL,
  389. obc_transfer_properties, transfer,
  390. NULL) == FALSE) {
  391. g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
  392. "Unable to register to D-Bus");
  393. return FALSE;
  394. }
  395. DBG("%p registered %s", transfer, transfer->path);
  396. return TRUE;
  397. }
  398. static gboolean transfer_open(struct obc_transfer *transfer, int flags,
  399. mode_t mode, GError **err)
  400. {
  401. int fd;
  402. char *filename;
  403. if (transfer->filename != NULL && strcmp(transfer->filename, "") != 0) {
  404. fd = open(transfer->filename, flags, mode);
  405. if (fd < 0) {
  406. error("open(): %s(%d)", strerror(errno), errno);
  407. g_set_error(err, OBC_TRANSFER_ERROR, -errno,
  408. "Unable to open file");
  409. return FALSE;
  410. }
  411. goto done;
  412. }
  413. fd = g_file_open_tmp("obex-clientXXXXXX", &filename, err);
  414. if (fd < 0) {
  415. error("g_file_open_tmp(): %s", (*err)->message);
  416. return FALSE;
  417. }
  418. if (transfer->filename == NULL) {
  419. remove(filename); /* remove always only if NULL was given */
  420. g_free(filename);
  421. } else {
  422. g_free(transfer->filename);
  423. transfer->filename = filename;
  424. }
  425. done:
  426. transfer->fd = fd;
  427. return TRUE;
  428. }
  429. struct obc_transfer *obc_transfer_get(const char *type, const char *name,
  430. const char *filename, GError **err)
  431. {
  432. struct obc_transfer *transfer;
  433. int perr;
  434. transfer = obc_transfer_create(G_OBEX_OP_GET, filename, name, type);
  435. perr = transfer_open(transfer, O_WRONLY | O_CREAT | O_TRUNC, 0600, err);
  436. if (perr < 0) {
  437. obc_transfer_free(transfer);
  438. return NULL;
  439. }
  440. return transfer;
  441. }
  442. struct obc_transfer *obc_transfer_put(const char *type, const char *name,
  443. const char *filename,
  444. const void *contents, size_t size,
  445. GError **err)
  446. {
  447. struct obc_transfer *transfer;
  448. struct stat st;
  449. int perr;
  450. if ((filename == NULL || strcmp(filename, "") == 0) &&
  451. contents == NULL) {
  452. g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL,
  453. "Invalid filename given");
  454. return NULL;
  455. }
  456. transfer = obc_transfer_create(G_OBEX_OP_PUT, filename, name, type);
  457. if (contents != NULL) {
  458. ssize_t w;
  459. if (!transfer_open(transfer, O_RDWR, 0, err))
  460. goto fail;
  461. w = write(transfer->fd, contents, size);
  462. if (w < 0) {
  463. perr = errno;
  464. error("write(): %s(%d)", strerror(perr), perr);
  465. g_set_error(err, OBC_TRANSFER_ERROR, -perr,
  466. "Writing to file failed");
  467. goto fail;
  468. } else if ((size_t) w != size) {
  469. error("Unable to write all contents to file");
  470. g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
  471. "Writing all contents to file failed");
  472. goto fail;
  473. }
  474. lseek(transfer->fd, 0, SEEK_SET);
  475. } else {
  476. if (!transfer_open(transfer, O_RDONLY, 0, err))
  477. goto fail;
  478. }
  479. if (fstat(transfer->fd, &st) < 0) {
  480. perr = errno;
  481. error("fstat(): %s(%d)", strerror(perr), perr);
  482. g_set_error(err, OBC_TRANSFER_ERROR, -perr,
  483. "Unable to get file status");
  484. goto fail;
  485. }
  486. transfer->size = st.st_size;
  487. return transfer;
  488. fail:
  489. obc_transfer_free(transfer);
  490. return NULL;
  491. }
  492. void obc_transfer_unregister(struct obc_transfer *transfer)
  493. {
  494. if (transfer->path) {
  495. g_dbus_unregister_interface(transfer->conn,
  496. transfer->path, TRANSFER_INTERFACE);
  497. }
  498. DBG("%p unregistered %s", transfer, transfer->path);
  499. obc_transfer_free(transfer);
  500. }
  501. static gboolean get_xfer_progress(const void *buf, gsize len,
  502. gpointer user_data)
  503. {
  504. struct obc_transfer *transfer = user_data;
  505. if (transfer->fd > 0) {
  506. int w;
  507. w = write(transfer->fd, buf, len);
  508. if (w < 0)
  509. return FALSE;
  510. transfer->transferred += w;
  511. }
  512. return TRUE;
  513. }
  514. static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
  515. {
  516. struct obc_transfer *transfer = user_data;
  517. struct transfer_callback *callback = transfer->callback;
  518. transfer->xfer = 0;
  519. if (transfer->progress_id != 0) {
  520. g_source_remove(transfer->progress_id);
  521. transfer->progress_id = 0;
  522. }
  523. if (transfer->status == TRANSFER_STATUS_SUSPENDED)
  524. g_obex_resume(transfer->obex);
  525. if (err)
  526. transfer_set_status(transfer, TRANSFER_STATUS_ERROR);
  527. else
  528. transfer_set_status(transfer, TRANSFER_STATUS_COMPLETE);
  529. if (callback)
  530. callback->func(transfer, err, callback->data);
  531. }
  532. static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp,
  533. gpointer user_data)
  534. {
  535. struct obc_transfer *transfer = user_data;
  536. GObexPacket *req;
  537. GObexHeader *hdr;
  538. GObexApparam *apparam;
  539. const guint8 *buf;
  540. gsize len;
  541. guint8 rspcode;
  542. gboolean final;
  543. if (err != NULL) {
  544. xfer_complete(obex, err, transfer);
  545. return;
  546. }
  547. rspcode = g_obex_packet_get_operation(rsp, &final);
  548. if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
  549. err = g_error_new(OBC_TRANSFER_ERROR, rspcode, "%s",
  550. g_obex_strerror(rspcode));
  551. xfer_complete(obex, err, transfer);
  552. g_error_free(err);
  553. return;
  554. }
  555. hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_LENGTH);
  556. if (hdr) {
  557. uint32_t len;
  558. if (g_obex_header_get_uint32(hdr, &len)) {
  559. transfer->size = len;
  560. g_dbus_emit_property_changed(transfer->conn,
  561. transfer->path,
  562. TRANSFER_INTERFACE, "Size");
  563. }
  564. }
  565. hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
  566. if (hdr) {
  567. apparam = g_obex_header_get_apparam(hdr);
  568. if (apparam != NULL)
  569. obc_transfer_set_apparam(transfer, apparam);
  570. }
  571. hdr = g_obex_packet_get_body(rsp);
  572. if (hdr) {
  573. g_obex_header_get_bytes(hdr, &buf, &len);
  574. if (len != 0)
  575. get_xfer_progress(buf, len, transfer);
  576. }
  577. if (rspcode == G_OBEX_RSP_SUCCESS) {
  578. transfer->req = 0;
  579. xfer_complete(obex, err, transfer);
  580. return;
  581. }
  582. if (g_obex_srm_active(obex) ||
  583. transfer->status == TRANSFER_STATUS_SUSPENDED)
  584. return;
  585. transfer->req = 0;
  586. req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
  587. transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
  588. xfer_complete, transfer,
  589. &err);
  590. }
  591. static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
  592. {
  593. struct obc_transfer *transfer = user_data;
  594. gssize size;
  595. size = read(transfer->fd, buf, len);
  596. if (size <= 0)
  597. return size;
  598. transfer->transferred += size;
  599. return size;
  600. }
  601. gboolean obc_transfer_set_callback(struct obc_transfer *transfer,
  602. transfer_callback_t func,
  603. void *user_data)
  604. {
  605. struct transfer_callback *callback;
  606. if (transfer->callback != NULL)
  607. return FALSE;
  608. callback = g_new0(struct transfer_callback, 1);
  609. callback->func = func;
  610. callback->data = user_data;
  611. transfer->callback = callback;
  612. return TRUE;
  613. }
  614. static gboolean report_progress(gpointer data)
  615. {
  616. struct obc_transfer *transfer = data;
  617. if (transfer->transferred == transfer->progress)
  618. return TRUE;
  619. transfer->progress = transfer->transferred;
  620. if (transfer->transferred == transfer->size) {
  621. transfer->progress_id = 0;
  622. return FALSE;
  623. }
  624. if (transfer->status != TRANSFER_STATUS_ACTIVE &&
  625. transfer->status != TRANSFER_STATUS_SUSPENDED)
  626. transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
  627. g_dbus_emit_property_changed(transfer->conn, transfer->path,
  628. TRANSFER_INTERFACE, "Transferred");
  629. return TRUE;
  630. }
  631. static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
  632. {
  633. GObexPacket *req;
  634. GObexHeader *hdr;
  635. if (transfer->xfer > 0) {
  636. g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
  637. "Transfer already started");
  638. return FALSE;
  639. }
  640. req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
  641. if (transfer->name != NULL)
  642. g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
  643. transfer->name);
  644. if (transfer->type != NULL)
  645. g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
  646. strlen(transfer->type) + 1);
  647. if (transfer->apparam != NULL) {
  648. hdr = g_obex_header_new_apparam(transfer->apparam);
  649. g_obex_packet_add_header(req, hdr);
  650. }
  651. transfer->req = g_obex_send_req(transfer->obex, req,
  652. FIRST_PACKET_TIMEOUT,
  653. get_xfer_progress_first,
  654. transfer, err);
  655. if (transfer->req == 0)
  656. return FALSE;
  657. if (transfer->path == NULL)
  658. return TRUE;
  659. transfer->progress_id = g_timeout_add_seconds(1, report_progress,
  660. transfer);
  661. return TRUE;
  662. }
  663. static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
  664. {
  665. GObexPacket *req;
  666. GObexHeader *hdr;
  667. if (transfer->xfer > 0) {
  668. g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
  669. "Transfer already started");
  670. return FALSE;
  671. }
  672. req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
  673. if (transfer->name != NULL)
  674. g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
  675. transfer->name);
  676. if (transfer->type != NULL)
  677. g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
  678. strlen(transfer->type) + 1);
  679. if (transfer->size < UINT32_MAX)
  680. g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
  681. if (transfer->apparam != NULL) {
  682. hdr = g_obex_header_new_apparam(transfer->apparam);
  683. g_obex_packet_add_header(req, hdr);
  684. }
  685. transfer->xfer = g_obex_put_req_pkt(transfer->obex, req,
  686. put_xfer_progress, xfer_complete,
  687. transfer, err);
  688. if (transfer->xfer == 0)
  689. return FALSE;
  690. if (transfer->path == NULL)
  691. return TRUE;
  692. transfer->progress_id = g_timeout_add_seconds(1, report_progress,
  693. transfer);
  694. return TRUE;
  695. }
  696. gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex,
  697. GError **err)
  698. {
  699. if (!transfer->obex)
  700. transfer->obex = g_obex_ref(obex);
  701. if (transfer->status == TRANSFER_STATUS_SUSPENDED_QUEUED) {
  702. /* Reset status so the transfer can be resumed */
  703. transfer->status = TRANSFER_STATUS_SUSPENDED;
  704. return TRUE;
  705. }
  706. switch (transfer->op) {
  707. case G_OBEX_OP_GET:
  708. return transfer_start_get(transfer, err);
  709. case G_OBEX_OP_PUT:
  710. return transfer_start_put(transfer, err);
  711. }
  712. g_set_error(err, OBC_TRANSFER_ERROR, -ENOTSUP, "Not supported");
  713. return FALSE;
  714. }
  715. guint8 obc_transfer_get_operation(struct obc_transfer *transfer)
  716. {
  717. return transfer->op;
  718. }
  719. void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data)
  720. {
  721. if (transfer->apparam != NULL)
  722. g_obex_apparam_free(transfer->apparam);
  723. if (data == NULL)
  724. return;
  725. transfer->apparam = data;
  726. }
  727. void *obc_transfer_get_apparam(struct obc_transfer *transfer)
  728. {
  729. return transfer->apparam;
  730. }
  731. int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents,
  732. size_t *size)
  733. {
  734. struct stat st;
  735. ssize_t ret;
  736. if (contents == NULL)
  737. return -EINVAL;
  738. if (fstat(transfer->fd, &st) < 0) {
  739. error("fstat(): %s(%d)", strerror(errno), errno);
  740. return -errno;
  741. }
  742. if (lseek(transfer->fd, 0, SEEK_SET) < 0) {
  743. error("lseek(): %s(%d)", strerror(errno), errno);
  744. return -errno;
  745. }
  746. *contents = g_malloc(st.st_size + 1);
  747. ret = read(transfer->fd, *contents, st.st_size);
  748. if (ret < 0) {
  749. error("read(): %s(%d)", strerror(errno), errno);
  750. g_free(*contents);
  751. return -errno;
  752. }
  753. (*contents)[ret] = '\0';
  754. if (size)
  755. *size = ret;
  756. return 0;
  757. }
  758. const char *obc_transfer_get_path(struct obc_transfer *transfer)
  759. {
  760. return transfer->path;
  761. }
  762. gint64 obc_transfer_get_size(struct obc_transfer *transfer)
  763. {
  764. return transfer->size;
  765. }