예제 #1
0
/* Thread: filescanner */
int
lastfm_login(char *path)
{
  struct keyval *kv;
  char *username;
  char *password;
  int ret;

  DPRINTF(E_DBG, L_LASTFM, "Got LastFM login request\n");

  // Delete any existing session key
  if (lastfm_session_key)
    free(lastfm_session_key);

  lastfm_session_key = NULL;

  db_admin_delete("lastfm_sk");

  // Read the credentials file
  ret = credentials_read(path, &username, &password);
  if (ret < 0)
    return -1;

  // Enable LastFM now that we got a login attempt
  lastfm_disabled = 0;

  kv = keyval_alloc();
  if (!kv)
    {
      free(username);
      free(password);
      return -1;
    }

  ret = ( (keyval_add(kv, "api_key", lastfm_api_key) == 0) &&
          (keyval_add(kv, "username", username) == 0) &&
          (keyval_add(kv, "password", password) == 0) );

  free(username);
  free(password);

  // Send the login request
  ret = request_post("auth.getMobileSession", kv, 1);

  keyval_clear(kv);
  free(kv);

  return ret;
}
예제 #2
0
static int
request_post(char *method, struct keyval *kv, int auth)
{
  struct https_client_ctx ctx;
  char *body;
  int ret;

  ret = keyval_add(kv, "method", method);
  if (ret < 0)
    return -1;

  if (!auth)
    ret = keyval_add(kv, "sk", lastfm_session_key);
  if (ret < 0)
    return -1;

  // API requires that we MD5 sign sorted param (without "format" param)
  keyval_sort(kv);
  ret = param_sign(kv);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LASTFM, "Aborting request, param_sign failed\n");
      return -1;
    }

  ret = body_print(&body, kv);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LASTFM, "Aborting request, body_print failed\n");
      return -1;
    }

  memset(&ctx, 0, sizeof(struct https_client_ctx));
  ctx.url = auth ? auth_url : api_url;
  ctx.body = body;

  ret = https_client_request(&ctx);

  return ret;
}
예제 #3
0
/* Creates an md5 signature of the concatenated parameters and adds it to keyval */
static int
param_sign(struct keyval *kv)
{
  struct onekeyval *okv;

  char hash[33];
  char ebuf[64];
  uint8_t *hash_bytes;
  size_t hash_len;
  gcry_md_hd_t md_hdl;
  gpg_error_t gc_err;
  int ret;
  int i;

  gc_err = gcry_md_open(&md_hdl, GCRY_MD_MD5, 0);
  if (gc_err != GPG_ERR_NO_ERROR)
    {
      gpg_strerror_r(gc_err, ebuf, sizeof(ebuf));
      DPRINTF(E_LOG, L_LASTFM, "Could not open MD5: %s\n", ebuf);
      return -1;
    }

  for (okv = kv->head; okv; okv = okv->next)
    {
      gcry_md_write(md_hdl, okv->name, strlen(okv->name));
      gcry_md_write(md_hdl, okv->value, strlen(okv->value));
    }  

  gcry_md_write(md_hdl, lastfm_secret, strlen(lastfm_secret));

  hash_bytes = gcry_md_read(md_hdl, GCRY_MD_MD5);
  if (!hash_bytes)
    {
      DPRINTF(E_LOG, L_LASTFM, "Could not read MD5 hash\n");
      return -1;
    }

  hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5);

  for (i = 0; i < hash_len; i++)
    sprintf(hash + (2 * i), "%02x", hash_bytes[i]);

  ret = keyval_add(kv, "api_sig", hash);

  gcry_md_close(md_hdl);

  return ret;
}
예제 #4
0
/* Copies headers we are searching for from one keyval struct to another
 *
 */
static void
headers_save(struct keyval *kv, struct evkeyvalq *headers)
{
  const char *value;
  int i;

  if (!kv || !headers)
    return;

  for (i = 0; i < (sizeof(header_list) / sizeof(header_list[0])); i++)
    {
      if ( (value = evhttp_find_header(headers, header_list[i])) )
	keyval_add(kv, header_list[i], value);
    }
  
}
예제 #5
0
static int
scrobble(int id)
{
  struct media_file_info *mfi;
  struct keyval *kv;
  char duration[4];
  char trackNumber[4];
  char timestamp[16];
  int ret;

  mfi = db_file_fetch_byid(id);
  if (!mfi)
    {
      DPRINTF(E_LOG, L_LASTFM, "Scrobble failed, track id %d is unknown\n", id);
      return -1;
    }

  // Don't scrobble songs which are shorter than 30 sec
  if (mfi->song_length < 30000)
    goto noscrobble;

  // Don't scrobble non-music and radio stations
  if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_URL))
    goto noscrobble;

  // Don't scrobble songs with unknown artist
  if (strcmp(mfi->artist, "Unknown artist") == 0)
    goto noscrobble;

  kv = keyval_alloc();
  if (!kv)
    goto noscrobble;

  snprintf(duration, sizeof(duration), "%" PRIu32, mfi->song_length);
  snprintf(trackNumber, sizeof(trackNumber), "%" PRIu32, mfi->track);
  snprintf(timestamp, sizeof(timestamp), "%" PRIi64, (int64_t)time(NULL));

  ret = ( (keyval_add(kv, "api_key", lastfm_api_key) == 0) &&
          (keyval_add(kv, "sk", lastfm_session_key) == 0) &&
          (keyval_add(kv, "artist", mfi->artist) == 0) &&
          (keyval_add(kv, "track", mfi->title) == 0) &&
          (keyval_add(kv, "album", mfi->album) == 0) &&
          (keyval_add(kv, "albumArtist", mfi->album_artist) == 0) &&
          (keyval_add(kv, "duration", duration) == 0) &&
          (keyval_add(kv, "trackNumber", trackNumber) == 0) &&
          (keyval_add(kv, "timestamp", timestamp) == 0)
        );

  free_mfi(mfi, 0);

  if (!ret)
    {
      keyval_clear(kv);
      free(kv);
      return -1;
    }

  DPRINTF(E_INFO, L_LASTFM, "Scrobbling '%s' by '%s'\n", keyval_get(kv, "track"), keyval_get(kv, "artist"));

  ret = request_post("track.scrobble", kv, 0);

  keyval_clear(kv);
  free(kv);

  return ret;

 noscrobble:
  free_mfi(mfi, 0);

  return -1;
}
예제 #6
0
static void
browse_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex intf, AvahiProtocol proto, AvahiResolverEvent event,
			const char *name, const char *type, const char *domain, const char *hostname, const AvahiAddress *addr,
			uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata)
{
  AvahiRecordBrowser *rb;
  struct mdns_browser *mb;
  struct mdns_record_browser *rb_data;
  char *key;
  char *value;
  uint16_t dns_type;
  int family;
  int ret;

  mb = (struct mdns_browser *)userdata;

  if (event != AVAHI_RESOLVER_FOUND)
    {
      if (event == AVAHI_RESOLVER_FAILURE)
	DPRINTF(E_LOG, L_MDNS, "Avahi Resolver failure: service '%s' type '%s' proto %d: %s\n", name, type, proto, MDNSERR);
      else
	DPRINTF(E_LOG, L_MDNS, "Avahi Resolver empty callback\n");

      family = avahi_proto_to_af(proto);
      if (family != AF_UNSPEC)
	mb->cb(name, type, domain, NULL, family, NULL, -1, NULL);

      // We don't clean up resolvers because we want a notification from them if
      // the service reappears (e.g. if device was switched off and then on)

      return;
    }

  DPRINTF(E_DBG, L_MDNS, "Avahi Resolver: resolved service '%s' type '%s' proto %d, host %s\n", name, type, proto, hostname);

  CHECK_NULL(L_MDNS, rb_data = calloc(1, sizeof(struct mdns_record_browser)));

  rb_data->name = strdup(name);
  rb_data->domain = strdup(domain);
  rb_data->mb = mb;
  rb_data->port = port;

  while (txt)
    {
      ret = avahi_string_list_get_pair(txt, &key, &value, NULL);
      txt = avahi_string_list_get_next(txt);

      if (ret < 0)
	continue;

      if (value)
	{
	  keyval_add(&rb_data->txt_kv, key, value);
	  avahi_free(value);
	}

      avahi_free(key);
    }

  if (proto == AVAHI_PROTO_INET6)
    dns_type = AVAHI_DNS_TYPE_AAAA;
  else
    dns_type = AVAHI_DNS_TYPE_A;

  // We need to implement a record browser because the announcement from some
  // devices (e.g. ApEx 1 gen) will include multiple records, and we need to
  // filter out those records that won't work (notably link-local). The value of
  // *addr given by browse_resolve_callback is just the first record.
  rb = avahi_record_browser_new(mdns_client, intf, proto, hostname, AVAHI_DNS_CLASS_IN, dns_type, 0, browse_record_callback, rb_data);
  if (!rb)
    DPRINTF(E_LOG, L_MDNS, "Could not create record browser for host %s: %s\n", hostname, MDNSERR);
}
예제 #7
0
/* Thread: filescanner */
void
lastfm_login(char *path)
{
  struct lastfm_command *cmd;
  struct keyval *kv;
  char *username;
  char *password;
  int ret;

  DPRINTF(E_DBG, L_LASTFM, "Got LastFM login request\n");

  // Delete any existing session key
  if (g_session_key)
    free(g_session_key);

  g_session_key = NULL;

  db_admin_delete("lastfm_sk");

  // Read the credentials file
  ret = credentials_read(path, &username, &password);
  if (ret < 0)
    return;

  // Enable LastFM now that we got a login attempt
  g_disabled = 0;

  kv = keyval_alloc();
  if (!kv)
    {
      free(username);
      free(password);
      return;
    }

  ret = ( (keyval_add(kv, "api_key", g_api_key) == 0) &&
          (keyval_add(kv, "username", username) == 0) &&
          (keyval_add(kv, "password", password) == 0) );

  free(username);
  free(password);

  if (!ret)
    {
      keyval_clear(kv);
      return;
    }

  // Spawn thread
  ret = lastfm_init();
  if (ret < 0)
    {
      g_disabled = 1;
      return;
    }
  g_initialized = 1;

  // Send login command to the thread
  cmd = (struct lastfm_command *)malloc(sizeof(struct lastfm_command));
  if (!cmd)
    {
      DPRINTF(E_LOG, L_LASTFM, "Could not allocate lastfm_command\n");
      return;
    }

  memset(cmd, 0, sizeof(struct lastfm_command));

  cmd->nonblock = 1;
  cmd->func = login;
  cmd->arg.kv = kv;

  nonblock_command(cmd);

  return;
}