Пример #1
0
static int
login(struct lastfm_command *cmd)
{
  request_post("auth.getMobileSession", cmd->arg.kv, 1);

  keyval_clear(cmd->arg.kv);

  return 0;
}
Пример #2
0
static void
browse_record_callback(AvahiRecordBrowser *b, AvahiIfIndex intf, AvahiProtocol proto,
                       AvahiBrowserEvent event, const char *hostname, uint16_t clazz, uint16_t type,
                       const void *rdata, size_t size, AvahiLookupResultFlags flags, void *userdata)
{
  struct mdns_record_browser *rb_data;
  AvahiAddress addr;
  char address[AVAHI_ADDRESS_STR_MAX];
  int family;
  int ret;

  rb_data = (struct mdns_record_browser *)userdata;

  if (event == AVAHI_BROWSER_CACHE_EXHAUSTED)
    DPRINTF(E_DBG, L_MDNS, "Avahi Record Browser (%s, proto %d): no more results (CACHE_EXHAUSTED)\n", hostname, proto);
  else if (event == AVAHI_BROWSER_ALL_FOR_NOW)
    DPRINTF(E_DBG, L_MDNS, "Avahi Record Browser (%s, proto %d): no more results (ALL_FOR_NOW)\n", hostname, proto);
  else if (event == AVAHI_BROWSER_FAILURE)
    DPRINTF(E_LOG, L_MDNS, "Avahi Record Browser (%s, proto %d) failure: %s\n", hostname, proto, MDNSERR);
  else if (event == AVAHI_BROWSER_REMOVE)
    return; // Not handled - record browser lifetime too short for this to happen

  if (event != AVAHI_BROWSER_NEW)
    goto out_free_record_browser;

  ret = avahi_address_make(&addr, proto, rdata, size); // Not an avahi function despite the name
  if (ret < 0)
    return;

  family = avahi_proto_to_af(proto);
  avahi_address_snprint(address, sizeof(address), &addr);

  // Avahi will sometimes give us link-local addresses in 169.254.0.0/16 or
  // fe80::/10, which (most of the time) are useless
  // - see also https://lists.freedesktop.org/archives/avahi/2012-September/002183.html
  if ((proto == AVAHI_PROTO_INET && is_v4ll(&addr.data.ipv4)) || (proto == AVAHI_PROTO_INET6 && is_v6ll(&addr.data.ipv6)))
    {
      DPRINTF(E_WARN, L_MDNS, "Ignoring announcement from %s, address %s is link-local\n", hostname, address);
      return;
    }

  DPRINTF(E_DBG, L_MDNS, "Avahi Record Browser (%s, proto %d): NEW record %s for service type '%s'\n", hostname, proto, address, rb_data->mb->type);

  // Execute callback (mb->cb) with all the data
  rb_data->mb->cb(rb_data->name, rb_data->mb->type, rb_data->domain, hostname, family, address, rb_data->port, &rb_data->txt_kv);

  // Stop record browser
 out_free_record_browser:
  keyval_clear(&rb_data->txt_kv);
  free(rb_data->name);
  free(rb_data->domain);
  free(rb_data);

  avahi_record_browser_free(b);
}
Пример #3
0
static int
mdns_addr_lookup_free(struct mdns_addr_lookup *lu)
{
  if (! lu)
    return -1;

  if(lu->sdref)
    DNSServiceRefDeallocate(lu->sdref);
  keyval_clear(&lu->txt_kv);
  free(lu);

  return -1;
}
Пример #4
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;
}
Пример #5
0
static int
spawn_record_browser(AvahiClient *c, AvahiIfIndex intf, AvahiProtocol proto, const char *hostname, const char *domain,
		     uint16_t type, struct mdns_browser *mb, const char *name, uint16_t port, AvahiStringList *txt)
{
  AvahiRecordBrowser *rb;
  struct mdns_record_browser *rb_data;
  char *key;
  char *value;
  char *ptr;
  size_t len;
  int ret;

  rb_data = (struct mdns_record_browser *)malloc(sizeof(struct mdns_record_browser));
  if (!rb_data)
    {
      DPRINTF(E_LOG, L_MDNS, "Out of memory for record browser data\n");

      return -1;
    }

  memset(rb_data, 0, sizeof(struct mdns_record_browser));

  rb_data->mb = mb;
  rb_data->port = port;

  rb_data->name = strdup(name);
  if (!rb_data->name)
    {
      DPRINTF(E_LOG, L_MDNS, "Out of memory for service name\n");

      goto out_free_rb;
    }

  rb_data->domain = strdup(domain);
  if (!rb_data->domain)
    {
      DPRINTF(E_LOG, L_MDNS, "Out of memory for service domain\n");

      goto out_free_name;
    }

  while (txt)
    {
      len = avahi_string_list_get_size(txt);
      key = (char *)avahi_string_list_get_text(txt);

      ptr = memchr(key, '=', len);
      if (!ptr)
	{
	  value = "";
	  len = 0;
	}
      else
	{
	  *ptr = '\0';
	  value = ptr + 1;

	  len -= strlen(key) + 1;
	}

      ret = keyval_add_size(&rb_data->txt_kv, key, value, len);

      if (ptr)
	*ptr = '=';

      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_MDNS, "Could not build TXT record keyval\n");

	  goto out_free_keyval;
	}

      txt = avahi_string_list_get_next(txt);
    }

  rb = NULL;
  switch (type)
    {
      case AVAHI_DNS_TYPE_A:
	rb = avahi_record_browser_new(c, intf, proto, hostname,
				      AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, 0,
				      browse_record_callback_v4, rb_data);
	if (!rb)
	  DPRINTF(E_LOG, L_MDNS, "Could not create v4 record browser for host %s: %s\n",
		  hostname, avahi_strerror(avahi_client_errno(c)));
	break;

      case AVAHI_DNS_TYPE_AAAA:
	rb = avahi_record_browser_new(c, intf, proto, hostname,
				      AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, 0,
				      browse_record_callback_v6, rb_data);
	if (!rb)
	  DPRINTF(E_LOG, L_MDNS, "Could not create v4 record browser for host %s: %s\n",
		  hostname, avahi_strerror(avahi_client_errno(c)));
	break;
    }

  if (!rb)
    goto out_free_keyval;

  return 0;

 out_free_keyval:
  keyval_clear(&rb_data->txt_kv);
  free(rb_data->domain);
 out_free_name:
  free(rb_data->name);
 out_free_rb:
  free(rb_data);

  return -1;
}
Пример #6
0
static void
browse_record_callback_v6(AvahiRecordBrowser *b, AvahiIfIndex intf, AvahiProtocol proto,
			  AvahiBrowserEvent event, const char *hostname, uint16_t clazz, uint16_t type,
			  const void *rdata, size_t size, AvahiLookupResultFlags flags, void *userdata)
{
  char address[INET6_ADDRSTRLEN + IF_NAMESIZE + 1];
  char ifname[IF_NAMESIZE];
  struct in6_addr addr;
  struct mdns_record_browser *rb_data;
  int ll;
  int len;
  int ret;

  rb_data = (struct mdns_record_browser *)userdata;

  switch (event)
    {
      case AVAHI_BROWSER_NEW:
	if (size != sizeof(addr.s6_addr))
	  {
	    DPRINTF(E_WARN, L_MDNS, "Got RR type AAAA size %ld (should be %ld)\n", (long)size, (long)sizeof(addr.s6_addr));

	    return;
	  }

	memcpy(&addr.s6_addr, rdata, sizeof(addr.s6_addr));

	ll = is_v6ll(&addr);
	if (ll && !(rb_data->mb->flags & MDNS_WANT_V6LL))
	  {
	    DPRINTF(E_DBG, L_MDNS, "Discarding IPv6 LL, not interested (service %s)\n", rb_data->name);
	    return;
	  }
	else if (!ll && !(rb_data->mb->flags & MDNS_WANT_V6))
	  {
	    DPRINTF(E_DBG, L_MDNS, "Discarding IPv6, not interested (service %s)\n", rb_data->name);
	    return;
	  }

	if (!inet_ntop(AF_INET6, &addr.s6_addr, address, sizeof(address)))
	  {
	    DPRINTF(E_LOG, L_MDNS, "Could not print IPv6 address: %s\n", strerror(errno));

	    return;
	  }

	if (ll)
	  {
	    if (!if_indextoname(intf, ifname))
	      {
		DPRINTF(E_LOG, L_MDNS, "Could not map interface index %d to a name\n", intf);

		return;
	      }

	    len = strlen(address);
	    ret = snprintf(address + len, sizeof(address) - len, "%%%s", ifname);
	    if ((ret < 0) || (ret > sizeof(address) - len))
	      {
		DPRINTF(E_LOG, L_MDNS, "Buffer too short for scoped IPv6 LL\n");

		return;
	      }
	  }

	DPRINTF(E_DBG, L_MDNS, "Service %s, hostname %s resolved to %s\n", rb_data->name, hostname, address);

	/* Execute callback (mb->cb) with all the data */
	rb_data->mb->cb(rb_data->name, rb_data->mb->type, rb_data->domain, hostname, AF_INET6, address, rb_data->port, &rb_data->txt_kv);
	/* Got a suitable address, stop record browser */
	break;

      case AVAHI_BROWSER_REMOVE:
	/* Not handled - record browser lifetime too short for this to happen */
	return;

      case AVAHI_BROWSER_CACHE_EXHAUSTED:
      case AVAHI_BROWSER_ALL_FOR_NOW:
	DPRINTF(E_DBG, L_MDNS, "Avahi Record Browser (%s v6): no more results (%s)\n", hostname,
		(event == AVAHI_BROWSER_CACHE_EXHAUSTED) ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");	

	break;

      case AVAHI_BROWSER_FAILURE:
	DPRINTF(E_LOG, L_MDNS, "Avahi Record Browser (%s v6) failure: %s\n", hostname,
		avahi_strerror(avahi_client_errno(avahi_record_browser_get_client(b))));

	break;
    }

  /* Cleanup when done/error */
  keyval_clear(&rb_data->txt_kv);      
  free(rb_data->name);
  free(rb_data->domain);
  free(rb_data);

  avahi_record_browser_free(b);
}
Пример #7
0
static void
browse_record_callback_v4(AvahiRecordBrowser *b, AvahiIfIndex intf, AvahiProtocol proto,
			  AvahiBrowserEvent event, const char *hostname, uint16_t clazz, uint16_t type,
			  const void *rdata, size_t size, AvahiLookupResultFlags flags, void *userdata)
{
  char address[INET_ADDRSTRLEN];
  struct in_addr addr;
  struct mdns_record_browser *rb_data;
  int ll;

  rb_data = (struct mdns_record_browser *)userdata;

  switch (event)
    {
      case AVAHI_BROWSER_NEW:
	if (size != sizeof(addr.s_addr))
	  {
	    DPRINTF(E_WARN, L_MDNS, "Got RR type A size %ld (should be %ld)\n", (long)size, (long)sizeof(addr.s_addr));

	    return;
	  }

	memcpy(&addr.s_addr, rdata, sizeof(addr.s_addr));

	ll = is_v4ll(&addr);
	if (ll && !(rb_data->mb->flags & MDNS_WANT_V4LL))
	  {
	    DPRINTF(E_DBG, L_MDNS, "Discarding IPv4 LL, not interested (service %s)\n", rb_data->name);
	    return;
	  }
	else if (!ll && !(rb_data->mb->flags & MDNS_WANT_V4))
	  {
	    DPRINTF(E_DBG, L_MDNS, "Discarding IPv4, not interested (service %s)\n", rb_data->name);
	    return;
	  }

	if (!inet_ntop(AF_INET, &addr.s_addr, address, sizeof(address)))
	  {
	    DPRINTF(E_LOG, L_MDNS, "Could not print IPv4 address: %s\n", strerror(errno));

	    return;
	  }

	DPRINTF(E_DBG, L_MDNS, "Service %s, hostname %s resolved to %s\n", rb_data->name, hostname, address);

	/* Execute callback (mb->cb) with all the data */
	rb_data->mb->cb(rb_data->name, rb_data->mb->type, rb_data->domain, hostname, AF_INET, address, rb_data->port, &rb_data->txt_kv);
	/* Got a suitable address, stop record browser */
	break;

      case AVAHI_BROWSER_REMOVE:
	/* Not handled - record browser lifetime too short for this to happen */
	return;

      case AVAHI_BROWSER_CACHE_EXHAUSTED:
      case AVAHI_BROWSER_ALL_FOR_NOW:
	DPRINTF(E_DBG, L_MDNS, "Avahi Record Browser (%s v4): no more results (%s)\n", hostname,
		(event == AVAHI_BROWSER_CACHE_EXHAUSTED) ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");	

	break;

      case AVAHI_BROWSER_FAILURE:
	DPRINTF(E_LOG, L_MDNS, "Avahi Record Browser (%s v4) failure: %s\n", hostname,
		avahi_strerror(avahi_client_errno(avahi_record_browser_get_client(b))));

	break;
    }

  keyval_clear(&rb_data->txt_kv);      
  free(rb_data->name);
  free(rb_data->domain);
  free(rb_data);

  avahi_record_browser_free(b);
}
Пример #8
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;
}
Пример #9
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)
{
  char address[AVAHI_ADDRESS_STR_MAX + IF_NAMESIZE + 2];
  char ifname[IF_NAMESIZE];
  struct keyval txt_kv;
  struct mdns_browser *mb;
  char *key;
  char *value;
  size_t len;
  int family;
  int ll;
  int ret;

  mb = (struct mdns_browser *)userdata;

  switch (event)
    {
      case AVAHI_RESOLVER_FAILURE:
	DPRINTF(E_LOG, L_MDNS, "Avahi Resolver failure: service '%s' type '%s': %s\n", name, type,
		avahi_strerror(avahi_client_errno(mdns_client)));
	break;

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

	// KK - if I reject 192.168.* my AirTunes get rejected.
	// How does avahi passes multiple IP addresses?
	// ll = is_link_local(addr);
	ll = 0;

	switch (proto)
	  {
	    case AVAHI_PROTO_INET:
		    avahi_record_browser_new()
	      if (ll)
		{
		  family = AF_UNSPEC;

		  DPRINTF(E_DBG, L_MDNS, "Discarding IPv4 LL address\n");
		  break;
		}

	      family = AF_INET;
	      avahi_address_snprint(address, sizeof(address), addr);
	      break;

	    case AVAHI_PROTO_INET6:
	      avahi_address_snprint(address, sizeof(address), addr);

	      if (ll)
		{
		  DPRINTF(E_DBG, L_MDNS, "Appending interface name to IPv6 LL address\n");

		  if (!if_indextoname(intf, ifname))
		    {
		      DPRINTF(E_LOG, L_MDNS, "Could not map interface index %d to a name\n", intf);

		      family = AF_UNSPEC;
		      break;
		    }

		  len = strlen(address);
		  ret = snprintf(address + len, sizeof(address) - len, "%%%s", ifname);
		  if ((ret < 0) || (ret > sizeof(address) - len))
		    {
		      DPRINTF(E_LOG, L_MDNS, "Buffer too short for scoped IPv6 LL\n");

		      family = AF_UNSPEC;
		      break;
		    }

		  DPRINTF(E_DBG, L_MDNS, "Scoped IPv6 LL: %s\n", address);
		}

	      family = AF_INET6;
	      break;

	    default:
	      DPRINTF(E_INFO, L_MDNS, "Avahi Resolver: unknown protocol %d\n", proto);

	      family = AF_UNSPEC;
	      break;
	  }

	if (family == AF_UNSPEC)
	  break;

	memset(&txt_kv, 0, sizeof(struct keyval));

	while (txt)
	  {
	    len = avahi_string_list_get_size(txt);
	    key = (char *)avahi_string_list_get_text(txt);

	    value = memchr(key, '=', len);
	    if (!value)
	      {
		value = "";
		len = 0;
	      }
	    else
	      {
		*value = '\0';
		value++;

		len -= strlen(key) + 1;
	      }

	    ret = keyval_add_size(&txt_kv, key, value, len);
	    if (ret < 0)
	      {
		DPRINTF(E_LOG, L_MDNS, "Could not build TXT record keyval\n");

		goto out_clear_txt_kv;
	      }

	    txt = avahi_string_list_get_next(txt);
	  }

	/* Execute callback (mb->cb) with all the data */
	mb->cb(name, type, domain, hostname, family, address, port, &txt_kv);

    out_clear_txt_kv:
	keyval_clear(&txt_kv);
	break;
    }

  avahi_service_resolver_free(r);
}
Пример #10
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;
}
Пример #11
0
/* Earlier versions of ffmpeg/libav do not seem to allow access to the http
 * headers, so we must instead open the stream ourselves to get the metadata.
 * Sorry about the extra connections, you radio streaming people!
 *
 * It is not possible to get the packet metadata with these versions of ffmpeg
 */
struct http_icy_metadata *
http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only)
{
  struct http_icy_metadata *metadata;
  struct http_client_ctx ctx;
  struct keyval *kv;
  const char *value;
  int got_header;
  int ret;

  /* Can only get header metadata */
  if (packet_only)
    return NULL;

  kv = keyval_alloc();
  if (!kv)
    return NULL;

  memset(&ctx, 0, sizeof(struct http_client_ctx));
  ctx.url = fmtctx->filename;
  ctx.headers = kv;
  ctx.headers_only = 1;
  ctx.body = NULL;

  ret = http_client_request(&ctx);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_HTTP, "Error fetching %s\n", fmtctx->filename);

      free(kv);
      return NULL;
    }

  metadata = malloc(sizeof(struct http_icy_metadata));
  if (!metadata)
    return NULL;
  memset(metadata, 0, sizeof(struct http_icy_metadata));

  got_header = 0;
  if ( (value = keyval_get(ctx.headers, "icy-name")) )
    {
      metadata->name = strdup(value);
      got_header = 1;
    }
  if ( (value = keyval_get(ctx.headers, "icy-description")) )
    {
      metadata->description = strdup(value);
      got_header = 1;
    }
  if ( (value = keyval_get(ctx.headers, "icy-genre")) )
    {
      metadata->genre = strdup(value);
      got_header = 1;
    }

  keyval_clear(kv);
  free(kv);

  if (!got_header)
   {
     free(metadata);
     return NULL;
   }

/*  DPRINTF(E_DBG, L_HTTP, "Found ICY: N %s, D %s, G %s, T %s, A %s, U %s, I %" PRIu32 "\n",
	metadata->name,
	metadata->description,
	metadata->genre,
	metadata->title,
	metadata->artist,
	metadata->artwork_url,
	metadata->hash
	);*/

  return metadata;
}