/* * frees members of a playlist item, but not the item * itself, therefore allowing stack allocation if wished */ void hgd_free_playlist_item(struct hgd_playlist_item *i) { if (i->filename != NULL) free(i->filename); if (i->user != NULL) free(i->user); hgd_free_media_tags(&i->tags); }
/* * queue a track * * args: filename|size * reponses * ok... ok and waiting for payload * ok ok and payload accepted * err|size size arg was weird * err|user_not_identified user did not identify * err|internal something else went wrong * * after 'ok...' * client then sends 'size' bytes of the media to queue */ int hgd_cmd_queue(struct hgd_session *sess, char **args) { char *filename_p = args[0], *payload = NULL; size_t bytes = atoi(args[1]); char *unique_fn = NULL; int f = -1, ret = HGD_OK; size_t bytes_recvd = 0, to_write; ssize_t write_ret; char *filename; struct hgd_media_tag tags; if ((flood_limit >= 0) && (hgd_num_tracks_user(sess->user->name) >= flood_limit)) { DPRINTF(HGD_D_WARN, "User '%s' trigger flood protection", sess->user->name); hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|floodprotection"); return (HGD_FAIL); } /* strip path, we don't care about that */ filename = basename(filename_p); if ((bytes == 0) || ((long int) bytes > max_upload_size)) { DPRINTF(HGD_D_WARN, "Incorrect file size"); hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|size"); ret = HGD_FAIL; goto clean; } /* prepare to recieve the media file and stash away */ xasprintf(&unique_fn, "%s/" HGD_UNIQ_FILE_PFX "%s", filestore_path, filename); DPRINTF(HGD_D_DEBUG, "Template for filestore is '%s'", unique_fn); f = mkstemps(unique_fn, strlen(filename) + 1); /* +1 for hyphen */ if (f < 0) { DPRINTF(HGD_D_ERROR, "mkstemp: %s: %s", filestore_path, SERROR); hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|internal"); ret = HGD_FAIL; goto clean; } hgd_sock_send_line(sess->sock_fd, sess->ssl, "ok|..."); DPRINTF(HGD_D_INFO, "Recving %d byte payload '%s' from %s into %s", (int) bytes, filename, sess->user->name, unique_fn); /* recieve bytes in small chunks so that we dont use moar RAM */ while (bytes_recvd != bytes) { if (bytes - bytes_recvd < HGD_BINARY_RECV_SZ) to_write = bytes - bytes_recvd; else to_write = HGD_BINARY_RECV_SZ; payload = NULL; DPRINTF(HGD_D_DEBUG, "Waiting for chunk of length %d bytes", (int) to_write); payload = hgd_sock_recv_bin(sess->sock_fd, sess->ssl, to_write); if (payload == NULL) { DPRINTF(HGD_D_ERROR, "failed to recv binary"); hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|internal"); /* try to clean up a partial upload */ if (fsync(f) < 0) DPRINTF(HGD_D_WARN, "can't sync partial file: %s", SERROR); if (close(f) < 0) DPRINTF(HGD_D_WARN, "can't close partial file: %s", SERROR); f = -1; if (unlink(unique_fn) < 0) { DPRINTF(HGD_D_WARN, "can't unlink partial upload: '%s': %s", unique_fn, SERROR); } ret = HGD_FAIL; goto clean; } write_ret = write(f, payload, to_write); /* XXX what if write returns less than the chunk? */ if (write_ret < 0) { DPRINTF(HGD_D_ERROR, "Failed to write %d bytes: %s", (int) to_write, SERROR); hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|internal"); unlink(unique_fn); /* don't much care if this fails */ ret = HGD_FAIL; goto clean; } bytes_recvd += to_write; DPRINTF(HGD_D_DEBUG, "Recvd binary chunk of length %d bytes", (int) to_write); DPRINTF(HGD_D_DEBUG, "Expecting a further %d bytes", (int) (bytes - bytes_recvd)); free(payload); } payload = NULL; /* * get tag metadata * no error that there is no #ifdef HAVE_TAGLIB */ hgd_get_tag_metadata(unique_fn, &tags); /* insert track into db */ if (hgd_insert_track(basename(unique_fn), &tags, sess->user->name) != HGD_OK) { hgd_sock_send_line(sess->sock_fd, sess->ssl, "err|sql"); goto clean; } hgd_free_media_tags(&tags); hgd_sock_send_line(sess->sock_fd, sess->ssl, "ok"); DPRINTF(HGD_D_INFO, "Transfer of '%s' complete", filename); clean: if (f != -1) close(f); if (payload) free(payload); if (unique_fn) free(unique_fn); if (bytes_recvd != bytes) ret = HGD_FAIL; return (ret); }