コード例 #1
0
ファイル: server.c プロジェクト: Flicki/spotify-api-server
static void put_playlist(sp_playlist *playlist,
                         struct evhttp_request *request,
                         void *userdata) {
  // TODO(liesen): playlist there so that signatures of all handler methods are
  // the same, but do they have to be?
  assert(playlist == NULL);

  sp_session *session = userdata;
  json_error_t loads_error;
  json_t *playlist_json = read_request_body_json(request, &loads_error);

  if (playlist_json == NULL) {
    send_error(request, HTTP_BADREQUEST,
               loads_error.text ? loads_error.text : "Unable to parse JSON");
    return;
  }

  // Parse playlist
  if (!json_is_object(playlist_json)) {
    send_error(request, HTTP_BADREQUEST, "Invalid playlist object");
    return;
  }

  // Get title
  json_t *title_json = json_object_get(playlist_json, "title");

  if (title_json == NULL) {
    json_decref(playlist_json);
    send_error(request, HTTP_BADREQUEST,
               "Invalid playlist: title is missing");
    return;
  }

  if (!json_is_string(title_json)) {
    json_decref(playlist_json);
    send_error(request, HTTP_BADREQUEST,
               "Invalid playlist: title is not a string");
    return;
  }

  char title[kMaxPlaylistTitleLength];
  strncpy(title, json_string_value(title_json), kMaxPlaylistTitleLength);
  json_decref(playlist_json);

  // Add new playlist
  sp_playlistcontainer *pc = sp_session_playlistcontainer(session);
  playlist = sp_playlistcontainer_add_new_playlist(pc, title);

  if (playlist == NULL) {
    send_error(request, HTTP_ERROR, "Unable to create playlist");
  } else {
    register_playlist_callbacks(playlist, request, &get_playlist,
                                &playlist_state_changed_callbacks, NULL);
  }
}
コード例 #2
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
static void get_playlist_subscribers(sp_playlist *playlist,
                                     struct evhttp_request *request,
                                     void *userdata) {
  assert(sp_playlist_is_loaded(playlist));
  sp_session *session = userdata;
  register_playlist_callbacks(playlist, request,
                              &get_playlist_subscribers_callback,
                              &playlist_subscribers_changed_callbacks,
                              userdata);
  sp_playlist_update_subscribers(session, playlist);
}
コード例 #3
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
static void handle_user_request(struct evhttp_request *request,
                                char *action,
                                const char *canonical_username,
                                sp_session *session) {
  if (action == NULL) {
    evhttp_send_error(request, HTTP_BADREQUEST, "Bad Request");
    return;
  }

  int http_method = evhttp_request_get_command(request);

  switch (http_method) {
    case EVHTTP_REQ_GET:
      if (strncmp(action, "playlists", 9) == 0) {
        sp_playlistcontainer *pc = sp_session_publishedcontainer_for_user_create(
            session, canonical_username);

        if (sp_playlistcontainer_is_loaded(pc)) {
          get_user_playlists(pc, request, session);
        } else {
          register_playlistcontainer_callbacks(pc, request,
              &get_user_playlists,
              &playlistcontainer_loaded_callbacks,
              session);
        }
      } else if (strncmp(action, "starred", 7) == 0) {
        sp_playlist *playlist = sp_session_starred_for_user_create(session,
            canonical_username);

        if (sp_playlist_is_loaded(playlist)) {
          get_playlist(playlist, request, session);
        } else {
          register_playlist_callbacks(playlist, request, &get_playlist,
              &playlist_state_changed_callbacks,
              session);
        }
      }
      break;

    case EVHTTP_REQ_PUT:
    case EVHTTP_REQ_POST:
      if (strncmp(action, "inbox", 5) == 0) {
        put_user_inbox(canonical_username, request, session);
      }
      break;

    default:
      evhttp_send_error(request, HTTP_BADREQUEST, "Bad Request");
      break;
  }
}
コード例 #4
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
static void put_playlist_remove_tracks(sp_playlist *playlist,
                                       struct evhttp_request *request,
                                       void *userdata) {
  // sp_session *session = userdata;
  const char *uri = evhttp_request_get_uri(request);
  struct evkeyvalq query_fields;
  evhttp_parse_query(uri, &query_fields);

  // Parse index
  const char *index_field = evhttp_find_header(&query_fields, "index");
  int index;

  if (index_field == NULL ||
      sscanf(index_field, "%d", &index) <= 0 ||
      index < 0) {
    send_error(request, HTTP_BADREQUEST,
               "Bad parameter: index must be numeric");
    return;
  }

  const char *count_field = evhttp_find_header(&query_fields, "count");
  int count;

  if (count_field == NULL ||
      sscanf(count_field, "%d", &count) <= 0 ||
      count < 1) {
    send_error(request, HTTP_BADREQUEST,
               "Bad parameter: count must be numeric and positive");
    return;
  }

  int *tracks = calloc(count, sizeof(int));

  for (int i = 0; i < count; i++)
    tracks[i] = index + i;

  struct playlist_handler *handler = register_playlist_callbacks(
      playlist, request, &get_playlist,
      &playlist_update_in_progress_callbacks, NULL);
  sp_error remove_tracks_error = sp_playlist_remove_tracks(playlist, tracks,
                                                           count);

  if (remove_tracks_error != SP_ERROR_OK) {
    sp_playlist_remove_callbacks(playlist, handler->playlist_callbacks, handler);
    free(handler);
    send_error_sp(request, HTTP_BADREQUEST, remove_tracks_error);
  }

  free(tracks);
}
コード例 #5
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
// Request dispatcher
static void handle_request(struct evhttp_request *request,
                            void *userdata) {
  evhttp_connection_set_timeout(request->evcon, 1);
  evhttp_add_header(evhttp_request_get_output_headers(request),
                    "Server", "[email protected]/spotify-api-server");

  // Check request method
  int http_method = evhttp_request_get_command(request);

  switch (http_method) {
    case EVHTTP_REQ_GET:
    case EVHTTP_REQ_PUT:
    case EVHTTP_REQ_POST:
      break;

    default:
      evhttp_send_error(request, HTTP_NOTIMPL, "Not Implemented");
      return;
  }

  struct state *state = userdata;
  sp_session *session = state->session;
  char *uri = evhttp_decode_uri(evhttp_request_get_uri(request));

  char *entity = strtok(uri, "/");

  if (entity == NULL) {
    evhttp_send_error(request, HTTP_BADREQUEST, "Bad Request");
    free(uri);
    return;
  }

  // Handle requests to /user/<user_name>/inbox
  if (strncmp(entity, "user", 4) == 0) {
    char *username = strtok(NULL, "/");

    if (username == NULL) {
      evhttp_send_error(request, HTTP_BADREQUEST, "Bad Request");
      free(uri);
      return;
    }

    char *action = strtok(NULL, "/");
    handle_user_request(request, action, username, session);
    free(uri);
    return;
  }

  // Handle requests to /playlist/<playlist_uri>/<action>
  if (strncmp(entity, "playlist", 8) != 0) {
    evhttp_send_error(request, HTTP_BADREQUEST, "Bad Request");
    free(uri);
    return;
  }

  char *playlist_uri = strtok(NULL, "/");

  if (playlist_uri == NULL) {
    switch (http_method) {
      case EVHTTP_REQ_PUT:
      case EVHTTP_REQ_POST:
        put_playlist(NULL, request, session);
        break;

      default:
        send_error(request, HTTP_BADREQUEST, "Bad Request");
        break;
    }

    free(uri);
    return;
  }

  sp_link *playlist_link = sp_link_create_from_string(playlist_uri);

  if (playlist_link == NULL) {
    send_error(request, HTTP_NOTFOUND, "Playlist link not found");
    free(uri);
    return;
  }

  if (sp_link_type(playlist_link) != SP_LINKTYPE_PLAYLIST) {
    sp_link_release(playlist_link);
    send_error(request, HTTP_BADREQUEST, "Not a playlist link");
    free(uri);
    return;
  }

  sp_playlist *playlist = sp_playlist_create(session, playlist_link);
  sp_link_release(playlist_link);

  if (playlist == NULL) {
    send_error(request, HTTP_NOTFOUND, "Playlist not found");
    free(uri);
    return;
  }

  sp_playlist_add_ref(playlist);

  // Dispatch request
  char *action = strtok(NULL, "/");

  // Default request handler
  handle_playlist_fn request_callback = &not_implemented;
  void *callback_userdata = session;

  switch (http_method) {
  case EVHTTP_REQ_GET:
    {
      if (action == NULL) {
        // Send entire playlist
        request_callback = &get_playlist;
      } else if (strncmp(action, "collaborative", 13) == 0) {
        request_callback = &get_playlist_collaborative;
      } else if (strncmp(action, "subscribers", 11) == 0) {
        request_callback = &get_playlist_subscribers;
      }
    }
    break;

  case EVHTTP_REQ_PUT:
  case EVHTTP_REQ_POST:
    {
      if (strncmp(action, "add", 3) == 0) {
        request_callback = &put_playlist_add_tracks;
      } else if (strncmp(action, "remove", 6) == 0) {
        request_callback = &put_playlist_remove_tracks;
      } else if (strncmp(action, "patch", 5) == 0) {
        callback_userdata = state;
        request_callback = &put_playlist_patch;
      }
    }
    break;
  }

  if (sp_playlist_is_loaded(playlist)) {
    request_callback(playlist, request, callback_userdata);
  } else {
    // Wait for playlist to load
    register_playlist_callbacks(playlist, request, request_callback,
                                &playlist_state_changed_callbacks,
                                callback_userdata);
  }

  free(uri);
}
コード例 #6
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
static void put_playlist_patch(sp_playlist *playlist,
                               struct evhttp_request *request,
                               void *userdata) {
  struct state *state = userdata;
  struct evbuffer *buf = evhttp_request_get_input_buffer(request);
  size_t buflen = evbuffer_get_length(buf);

  if (buflen == 0) {
    send_error(request, HTTP_BADREQUEST, "No body");
    return;
  }

  // Read request body
  json_error_t loads_error;
  json_t *json = read_request_body_json(request, &loads_error);

  if (json == NULL) {
    send_error(request, HTTP_BADREQUEST,
               loads_error.text ? loads_error.text : "Unable to parse JSON");
    return;
  }

  if (!json_is_array(json)) {
    json_decref(json);
    send_error(request, HTTP_BADREQUEST, "Not valid JSON array");
    return;
  }

  // Handle empty array
  int num_tracks = json_array_size(json);

  if (num_tracks == 0) {
    send_reply(request, HTTP_OK, "OK", NULL);
    return;
  }

  sp_track **tracks = calloc(num_tracks, sizeof (sp_track *));
  int num_valid_tracks = 0;

  for (int i = 0; i < num_tracks; i++) {
    json_t *item = json_array_get(json, i);

    if (!json_is_string(item)) {
      json_decref(item);
      continue;
    }

    char *uri = strdup(json_string_value(item));
    sp_link *track_link = sp_link_create_from_string(uri);
    free(uri);

    if (track_link == NULL)
      continue;

    if (sp_link_type(track_link) != SP_LINKTYPE_TRACK) {
      sp_link_release(track_link);
      continue;
    }

    sp_track *track = sp_link_as_track(track_link);

    if (track == NULL)
      continue;

    tracks[num_valid_tracks++] = track;
  }

  json_decref(json);

  // Bail if no tracks could be read from input
  if (num_valid_tracks == 0) {
    send_error(request, HTTP_BADREQUEST, "No valid tracks");
    free(tracks);
    return;
  }

  tracks = realloc(tracks, num_valid_tracks * sizeof (sp_track *));

  // Apply diff
  apr_pool_t *pool = state->pool;
  svn_diff_t *diff;
  svn_error_t *diff_error = diff_playlist_tracks(&diff, playlist, tracks,
                                                 num_valid_tracks, pool);

  if (diff_error != SVN_NO_ERROR) {
    free(tracks);
    svn_handle_error2(diff_error, stderr, false, "Diff");
    send_error(request, HTTP_BADREQUEST, "Search failed");
    return;
  }

  svn_error_t *apply_error = diff_playlist_tracks_apply(diff, playlist, tracks,
                                                        num_valid_tracks,
                                                        state->session);

  if (apply_error != SVN_NO_ERROR) {
    free(tracks);
    svn_handle_error2(apply_error, stderr, false, "Updating playlist");
    send_error(request, HTTP_BADREQUEST, "Could not apply diff");
    return;
  }

  if (!sp_playlist_has_pending_changes(playlist)) {
    free(tracks);
    get_playlist(playlist, request, NULL);
    return;
  }

  free(tracks);
  register_playlist_callbacks(playlist, request, &get_playlist,
                              &playlist_update_in_progress_callbacks, NULL);
}
コード例 #7
0
ファイル: server.c プロジェクト: ebonical/spotify-api-server
static void put_playlist_add_tracks(sp_playlist *playlist,
                                    struct evhttp_request *request,
                                    void *userdata) {
  sp_session *session = userdata;
  const char *uri = evhttp_request_get_uri(request);
  struct evkeyvalq query_fields;
  evhttp_parse_query(uri, &query_fields);

  // Parse index
  const char *index_field = evhttp_find_header(&query_fields, "index");
  int index;

  if (index_field == NULL || sscanf(index_field, "%d", &index) <= 0) {
    index = sp_playlist_num_tracks(playlist);
  }

  // Parse JSON
  json_error_t loads_error;
  json_t *json = read_request_body_json(request, &loads_error);

  if (json == NULL) {
    send_error(request, HTTP_BADREQUEST,
               loads_error.text ? loads_error.text : "Unable to parse JSON");
    return;
  }

  if (!json_is_array(json)) {
    json_decref(json);
    send_error(request, HTTP_BADREQUEST, "Not valid JSON array");
    return;
  }

  // Handle empty array
  int num_tracks = json_array_size(json);

  if (num_tracks == 0) {
    send_reply(request, HTTP_OK, "OK", NULL);
    return;
  }

  sp_track **tracks = calloc(num_tracks, sizeof (sp_track *));
  int num_valid_tracks = json_to_tracks(json, tracks, num_tracks);
  json_decref(json);

  // Bail if no tracks could be read from input
  if (num_valid_tracks == 0) {
    send_error(request, HTTP_BADREQUEST, "No valid tracks");
    free(tracks);
    return;
  }

  struct playlist_handler *handler = register_playlist_callbacks(
      playlist, request, &get_playlist,
      &playlist_update_in_progress_callbacks, NULL);
  sp_error add_tracks_error = sp_playlist_add_tracks(playlist, tracks,
                                                     num_valid_tracks,
                                                     index, session);

  if (add_tracks_error != SP_ERROR_OK) {
    sp_playlist_remove_callbacks(playlist, handler->playlist_callbacks,
                                 handler);
    free(handler);
    send_error_sp(request, HTTP_BADREQUEST, add_tracks_error);
  }

  free(tracks);
}