/* dSfree() assumes links has been decremented and tested by Stringfree() */ void dSfree(String *str, const char *file, int line) { if (str->links < 0) { /* While it would be useful to print str->file, it may have been * clobbered the first time str was freed, so is unsafe. */ internal_error(file, line, "dSfree: links==%d, data=\"%.32b\"", str->links, '\"', str->data); core("dSfree: links==%d", file, line, str->links); } if (str->charattrs) Sfree(str, str->charattrs); if (str->dynamic_data && str->data) Sfree(str, str->data); str->size = -42; /* break lcheck if str is reused without dSinit */ str->len = 0; if (str->dynamic_struct) { if (!str->dynamic_data) /* str and data were alloced together */ Sfree(str, str); #if USE_MMALLOC else if (str->md) Sfree(str, str); #endif else pfree_fl(str, Stringpool, data, file, line); } }
/*=========================================================================*\ Get HTTP log info this includes a corresponding HTTP header returns an allocated string (caller must free) or NULL on error \*=========================================================================*/ char *_ickP2pGetLogFile( ickP2pContext_t *ictx, const char *uri ) { int level = 7; char *logContent; int dlen, hlen; char *message; char header[512]; debug( "_ickP2pGetLogFile (%p): \"%s\"", ictx, uri ); /*------------------------------------------------------------------------*\ Get minimum log level from path \*------------------------------------------------------------------------*/ if( strlen(uri)>strlen(ICK_P2PLOGURI) ) { const char *ptr = uri + strlen(ICK_P2PLOGURI); char *eptr; if( *ptr!='/' ) return strdup( HTTP_404 ); ptr++; level = (int) strtol( ptr, &eptr, 10 ); while( isspace(*eptr) ) eptr++; if( *eptr ) { logwarn("_ickP2pGetLogFile (%p): Requested minimum level is not a number (%s)", ictx, ptr ); return strdup( HTTP_404 ); } } /*------------------------------------------------------------------------*\ Get debug info \*------------------------------------------------------------------------*/ logContent = ickP2pGetLogContent( level ); if( !logContent ) return strdup( HTTP_404 ); /*------------------------------------------------------------------------*\ Construct header \*------------------------------------------------------------------------*/ dlen = strlen( logContent); hlen = sprintf( header, HTTP_200, "text/plain", (long)dlen ); /*------------------------------------------------------------------------*\ Merge header and payload \*------------------------------------------------------------------------*/ message = malloc( hlen+dlen+1 ); if( !message ) { Sfree( logContent ); logerr( "_ickP2pGetLogFile: out of memory" ); return NULL; } strcpy( message, header ); strcpy( message+hlen, logContent ); Sfree( logContent ); /*------------------------------------------------------------------------*\ That's all \*------------------------------------------------------------------------*/ return message; }
/*=========================================================================*\ Get HTTP debug info this includes a corresponding HTTP header will lock the device list returns an allocated string (caller must free) or NULL on error \*=========================================================================*/ char *_ickP2pGetDebugFile( ickP2pContext_t *ictx, const char *uri ) { const char *uuid = NULL; int dlen, hlen; char *debugContent = NULL; char *message; char header[512]; debug( "_ickP2pGetDebugFile (%p): \"%s\"", ictx, uri ); /*------------------------------------------------------------------------*\ Get device info from path \*------------------------------------------------------------------------*/ if( strlen(uri)>strlen(ICK_P2PDEBUGURI) ) { uuid = uri + strlen(ICK_P2PDEBUGURI); if( *uuid!='/' ) return strdup( HTTP_404 ); uuid++; } /*------------------------------------------------------------------------*\ Get debug info \*------------------------------------------------------------------------*/ debugContent = ickP2pGetDebugInfo( ictx, uuid ); /*------------------------------------------------------------------------*\ Not found or error? \*------------------------------------------------------------------------*/ if( !debugContent ) return strdup( HTTP_404 ); /*------------------------------------------------------------------------*\ Construct header \*------------------------------------------------------------------------*/ dlen = strlen( debugContent); hlen = sprintf( header, HTTP_200, "application/json", (long)dlen ); /*------------------------------------------------------------------------*\ Merge header and payload \*------------------------------------------------------------------------*/ message = malloc( hlen+dlen+1 ); if( !message ) { Sfree( debugContent ); logerr( "_ickP2pGetDebugFile: out of memory" ); return NULL; } strcpy( message, header ); strcpy( message+hlen, debugContent ); Sfree( debugContent ); /*------------------------------------------------------------------------*\ That's all \*------------------------------------------------------------------------*/ return message; }
/*=========================================================================*\ Free a message container \*=========================================================================*/ void _ickDeviceFreeMessage( ickMessage_t *message ) { debug( "_ickDeviceFreeMessage: message %p (%ld bytes)", message, message->size ); /*------------------------------------------------------------------------*\ Free payload and descriptor \*------------------------------------------------------------------------*/ Sfree( message->payload ); Sfree( message ); /*------------------------------------------------------------------------*\ That's all \*------------------------------------------------------------------------*/ }
/*=========================================================================*\ Set Filename to store repository An existing repository is not cleared... \*=========================================================================*/ int persistSetFilename( const char *name ) { struct stat buf; DBGMSG( "persistSetFilename: \"%s\"", name ); /*------------------------------------------------------------------------*\ Defensively dump to an existing file name \*------------------------------------------------------------------------*/ if( repositoryFileName && jRepository ) _dumpRepository( repositoryFileName ); /*------------------------------------------------------------------------*\ Try to read content from existing file \*------------------------------------------------------------------------*/ if( !stat(name,&buf) && _readRepository(name) ) return -1; /*------------------------------------------------------------------------*\ Create empty repository if necessary \*------------------------------------------------------------------------*/ if( !jRepository ) jRepository = json_object(); if( !jRepository ) { logerr( "Could not create repository object." ); return -1; } /*------------------------------------------------------------------------*\ Save new name \*------------------------------------------------------------------------*/ Sfree( repositoryFileName ); repositoryFileName = strdup( name ); return 0; }
/*=========================================================================*\ Set device name, uses uuid if name is NULL caller should lock the device \*=========================================================================*/ ickErrcode_t _ickDeviceSetName( ickDevice_t *device, const char *name ) { char *str = name ? strdup(name) : strdup(device->uuid); if( !str ) { logerr( "_ickDeviceSetLocation: out of memory" ); return ICKERR_NOMEM; } Sfree( device->friendlyName ); device->friendlyName = str; return ICKERR_SUCCESS; }
String *dScpy(String *dest, const char *src, const char *file, int line) { dest->len = strlen(src); if (dest->charattrs) { Sfree(dest, dest->charattrs); dest->charattrs = NULL; } lcheck(dest, file, line); memcpy(dest->data, src, dest->len + 1); return dest; }
/*=========================================================================*\ Free memory for a device descriptor this does no unlinking or timer deletion caller should lock device \*=========================================================================*/ void _ickDeviceFree( ickDevice_t *device ) { debug( "_ickDeviceFree: uuid=\"%s\"", device->uuid ); /*------------------------------------------------------------------------*\ Delete mutex \*------------------------------------------------------------------------*/ pthread_mutex_destroy( &device->mutex ); /*------------------------------------------------------------------------*\ Clean up message queues \*------------------------------------------------------------------------*/ _ickDevicePurgeMessages( device ); /*------------------------------------------------------------------------*\ Free memory \*------------------------------------------------------------------------*/ Sfree( device->uuid ); Sfree( device->location ); Sfree( device->friendlyName ); Sfree( device ); }
/*=========================================================================*\ Clean up message queues \*=========================================================================*/ void _ickDevicePurgeMessages( ickDevice_t *device ) { ickMessage_t *msg, *next; int num; debug( "_ickDevicePurgeMessages: uuid=\"%s\"", device->uuid ); /*------------------------------------------------------------------------*\ Delete unsent messages \*------------------------------------------------------------------------*/ if( device->outQueue ) { for( num=0,msg=device->outQueue; msg; msg=next ) { next = msg->next; Sfree( msg->payload ); Sfree( msg ) num++; } device->outQueue = NULL; loginfo( "_ickDevicePurgeMessages: Deleted %d unsent messages in outQueue.", num ); } /*------------------------------------------------------------------------*\ Delete undelivered messages \*------------------------------------------------------------------------*/ if( device->inQueue ) { for( num=0,msg=device->inQueue; msg; msg=next ) { next = msg->next; Sfree( msg->payload ); Sfree( msg ) num++; } device->inQueue = NULL; loginfo( "_ickDevicePurgeMessages: Deleted %d undelivered messages in inQueue.", num ); } /*------------------------------------------------------------------------*\ That's it \*------------------------------------------------------------------------*/ }
/*=========================================================================*\ Set device location string caller should lock the device \*=========================================================================*/ ickErrcode_t _ickDeviceSetLocation( ickDevice_t *device, const char *location ) { char *str = NULL; if( location ) { str = strdup( location ); if( !str ) { logerr( "_ickDeviceSetLocation: out of memory" ); return ICKERR_NOMEM; } } Sfree( device->location ); device->location = str; return ICKERR_SUCCESS; }
String *dSScpy(String *dest, const conString *src, const char *file, int line) { if (dest->charattrs && !src->charattrs) { Sfree(dest, dest->charattrs); dest->charattrs = NULL; } dest->len = src->len; lcheck(dest, file, line); memcpy(dest->data, src->data ? src->data : "", src->len+1); if (src->charattrs) { check_charattrs(dest, 0, 0, file, line); memcpy(dest->charattrs, src->charattrs, sizeof(cattr_t) * (src->len+1)); } dest->attrs = src->attrs; return dest; }
String *dSncpy(String *dest, const char *src, int n, const char *file, int line) { int len = strlen(src); if (n < 0) core("dSncpy: n==%ld", file, line, (long)n); if (n > len) n = len; dest->len = n; if (dest->charattrs) { Sfree(dest, dest->charattrs); dest->charattrs = NULL; } lcheck(dest, file, line); memcpy(dest->data, src, n); dest->data[n] = '\0'; return dest; }
/*=========================================================================*\ Dereference an item URI using the service hints returns an allocated string (called needs to free that) or NULL on error \*=========================================================================*/ char *ickServiceResolveURI( const char* uri, const char* type ) { ServiceListItem *service; char *serviceId; char *urlStub; DBGMSG( "ickServiceResolveURI: \"%s\" type=\"%s\".", uri, type?type:"(no type)" ); /*------------------------------------------------------------------------*\ No service prefix ? \*------------------------------------------------------------------------*/ if( strncasecmp(uri,IckServiceSchemePrefix,strlen(IckServiceSchemePrefix)) ) return strdup( uri ); /*------------------------------------------------------------------------*\ Get service id \*------------------------------------------------------------------------*/ serviceId = strdup( uri+strlen(IckServiceSchemePrefix) ); urlStub = strchr( serviceId, '/' ); if( urlStub ) *(urlStub++) = 0; /*------------------------------------------------------------------------*\ Look up service by id and (optionally) type \*------------------------------------------------------------------------*/ pthread_mutex_lock( &serviceListMutex ); service = _getService( NULL, serviceId, type, 0 ); /*------------------------------------------------------------------------*\ Build result \*------------------------------------------------------------------------*/ char *retval = NULL; if( service && service->serviceUrl ) { retval = malloc( strlen(service->serviceUrl) + strlen(urlStub) + 2 ); strcpy( retval, service->serviceUrl ); strcat( retval, "/" ); strcat( retval, urlStub ); } pthread_mutex_unlock( &serviceListMutex ); /*------------------------------------------------------------------------*\ That's all: clean up \*------------------------------------------------------------------------*/ DBGMSG( "ickServiceResolveURI (%s): \"%s\".", uri, retval ); Sfree( serviceId ); return retval; }
/*=========================================================================*\ Shutdown module, Free all memory \*=========================================================================*/ void persistShutdown( void ) { loginfo( "Shutting down persistency module..." ); /*------------------------------------------------------------------------*\ Dump to file a last time \*------------------------------------------------------------------------*/ if( repositoryFileName && jRepository ) _dumpRepository( repositoryFileName ); /*------------------------------------------------------------------------*\ Free filename \*------------------------------------------------------------------------*/ Sfree( repositoryFileName ); /*------------------------------------------------------------------------*\ Free JSON repository in memory \*------------------------------------------------------------------------*/ _freeRepository(); /*------------------------------------------------------------------------*\ That's all \*------------------------------------------------------------------------*/ }
/*=========================================================================*\ Remove and free a service from list Does not lock the list, so caller needs to set mutex! \*=========================================================================*/ static void _removeService( ServiceListItem *item ) { DBGMSG( "_removeService (%s): (%s:%s).", item->id, item->type, item->name ); /*------------------------------------------------------------------------*\ Search for entry \*------------------------------------------------------------------------*/ ServiceListItem *prevElement = NULL; ServiceListItem *element = serviceList; while( element ) { if( element==item ) break; prevElement = element; element = element->next; } /*------------------------------------------------------------------------*\ Not found \*------------------------------------------------------------------------*/ if( !element ) return; /*------------------------------------------------------------------------*\ Unlink element \*------------------------------------------------------------------------*/ if( !prevElement ) // replace list root serviceList = element->next; else prevElement->next = element->next; /*------------------------------------------------------------------------*\ Free resources \*------------------------------------------------------------------------*/ json_decref( element->jItem ); Sfree( element ); }
/*=========================================================================*\ Add a new service jService is the result part of a getServiceInformation answer \*=========================================================================*/ int ickServiceAdd( json_t *jService, ServiceOrigin origin ) { ServiceListItem *item; json_t *jObj; /*------------------------------------------------------------------------*\ Allocate header \*------------------------------------------------------------------------*/ item = calloc( 1, sizeof(ServiceListItem) ); if( !item ) { logerr( "ickServiceAdd: out of memory!" ); return -1; } item->origin = origin; item->jItem = json_incref( jService ); /*------------------------------------------------------------------------*\ Extract id for quick access (weak ref.) \*------------------------------------------------------------------------*/ jObj = json_object_get( jService, "id" ); if( !jObj || !json_is_string(jObj) ) { logerr( "ickServiceAdd: Missing field \"id\"!" ); Sfree( item ); json_decref( jService ); return -1; } item->id = json_string_value( jObj ); /*------------------------------------------------------------------------*\ Extract name for quick access (weak ref.) \*------------------------------------------------------------------------*/ jObj = json_object_get( jService, "name" ); if( !jObj || !json_is_string(jObj) ) { logerr( "ickServiceAdd (%s): Missing field \"name\"!", item->id ); Sfree( item ); json_decref( jService ); return -1; } item->name = json_string_value( jObj ); /*------------------------------------------------------------------------*\ Extract type for quick access (weak ref.) \*------------------------------------------------------------------------*/ jObj = json_object_get( jService, "type" ); if( !jObj || !json_is_string(jObj) ) { logerr( "ickServiceAdd (%s): Missing field \"type\"!", item->id ); Sfree( item ); json_decref( jService ); return -1; } item->type = json_string_value( jObj ); /*------------------------------------------------------------------------*\ Extract url for quick access (optional, weak ref.) \*------------------------------------------------------------------------*/ jObj = json_object_get( jService, "url" ); if( jObj ) item->url = json_string_value( jObj ); /*------------------------------------------------------------------------*\ Extract service url for quick access (optional, weak ref.) \*------------------------------------------------------------------------*/ jObj = json_object_get( jService, "serviceUrl" ); if( jObj ) item->serviceUrl = json_string_value( jObj ); /*------------------------------------------------------------------------*\ Insert or replace item \*------------------------------------------------------------------------*/ pthread_mutex_lock( &serviceListMutex ); // Check for duplicates ServiceListItem *oldItem = _getService( NULL, item->id, item->type, origin ); if( oldItem ) { DBGMSG( "ickServiceAdd (%s): Replacing service (%s:%s).", item->id, item->type, item->name ); _removeService( oldItem ); } // Insert new item in list item->next = serviceList; serviceList = item; pthread_mutex_unlock( &serviceListMutex ); /*------------------------------------------------------------------------*\ Be verbose \*------------------------------------------------------------------------*/ DBGMSG( "ickServiceAdd (%s): Added (%s:%s), origin %d.", item->id, item->type, item->name, item->origin ); /*------------------------------------------------------------------------*\ That's it \*------------------------------------------------------------------------*/ return 0; }
/*=========================================================================*\ Parse HTTP header, will write all icy-*, ice-* plus some relevant HTTP elements to a JSON object. Type detection is done to identify numbers. Returns NULL on error or an object with key-value pairs for identified fields \*=========================================================================*/ json_t *icyExtractHeaders( const char *httpHeader ) { json_t *jObj; const char *keyPtr; DBGMSG( "icyExtractHeaders: \"%s\".", httpHeader ); /*------------------------------------------------------------------------*\ Create result container \*------------------------------------------------------------------------*/ jObj = json_object(); if( !jObj ) { logerr( "icyExtractHeaders: Out of memory." ); return NULL; } json_object_set( jObj, "timestamp", json_real(srvtime()) ); /*------------------------------------------------------------------------*\ Loop over all lines \*------------------------------------------------------------------------*/ for( keyPtr=httpHeader; keyPtr&&*keyPtr; keyPtr++ ) { const char *linePtr = strpbrk( keyPtr, "\n\r" ); char *key = NULL; const char *valPtr; json_t *jVal = NULL; // Skip white spaces and line feeds if( *keyPtr=='\r' || *keyPtr=='\n' || *keyPtr==' ' || *keyPtr=='\t' ) continue; // Get key name separator within this line, ignore lines without separator valPtr = strchr( keyPtr, ':' ); if( !valPtr || valPtr>linePtr ) { keyPtr = linePtr; continue; } // Only consider header elements of interest if( strcmpprefix(keyPtr,"icy-") && strcmpprefix(keyPtr,"ice-") && strcmpprefix(keyPtr,"Content-Type:") && strcmpprefix(keyPtr,"Server:") ) { keyPtr = linePtr; continue; } key = strndup( keyPtr, valPtr-keyPtr ); // Skip separator and leading white spaces in value do valPtr++; while( *valPtr && (*valPtr==' ' || *valPtr=='\t') ); // Special treatment for ice tag if( !strcmp(key,"ice-audio-info") ) { if( _segmentKeyValList(valPtr,jObj,linePtr)<0 ) { DBGMSG( "icyExtractHeaders: Cannot code entries for key \"%s\": \"%.*s\"", key, linePtr-valPtr, valPtr ); } goto next; } // No value if( !*valPtr || valPtr==linePtr ) jVal = json_null(); // Type detection of value: try integer if( !jVal ) jVal =_getInteger( valPtr, linePtr ); // Try float if( !jVal ) jVal =_getReal( valPtr, linePtr ); // Use string as it is, convert to UTF8 if( !jVal ) jVal = json_mkstring( valPtr, linePtr-valPtr ); // Could not code this? if( !jVal ) { DBGMSG( "icyExtractHeaders: Cannot code entry for key \"%s\": \"%.*s\"", key, linePtr-valPtr, valPtr ); } // Set value in target else { if( jVal && json_object_set(jObj,key,jVal) ) { logerr( "icyExtractHeaders: Cannot insert/set JSON for key \"%s\" in target.", key ); Sfree( key ); json_decref( jObj ); return NULL; } DBGMSG( "icyExtractHeaders: Set entry for key \"%s\": \"%.*s\"", key, linePtr-valPtr, valPtr ); } // Clean up and set pointer to next line separator next: Sfree( key ); keyPtr = linePtr; } /*------------------------------------------------------------------------*\ That's it \*------------------------------------------------------------------------*/ return jObj; }
/*=========================================================================*\ Interpret a string of the form "key1=va11;...;keyn=valn[;]" up to (excluding) pbrk and insert JSON elements to jObj. Type detection is done to identify numbers. Existing elements are overwritten. Returns -1 on error or the number of elements processed \*=========================================================================*/ static int _segmentKeyValList( const char *str, json_t *jObj, const char *pbrk ) { int retval = 0; // Loop over string (range) while( str && *str && (!pbrk || str<pbrk) ) { const char *valPtr; char *key; json_t *jVal = NULL; // Get key name valPtr = strchr( str, '=' ); if( !valPtr ) break; key = strndup( str, valPtr-str ); // Skip separator (equal sign) and leading white spaces in value do valPtr++; while( *valPtr && (*valPtr==' ' || *valPtr=='\t') ); // No value if( !*valPtr || *valPtr== ';' ) { str = *valPtr ? valPtr+1 : valPtr; jVal = json_null(); } // Value is of string type else if( *valPtr=='\'' ) { // there seems to be no escape mechanism for single quotes... str = strchr( ++valPtr, '\'' ); if( !str || (pbrk && str>pbrk) ) { logwarn( "_segmentKeyValList: unterminated string fragment \"%s\".", valPtr ); if( pbrk ) str = pbrk; } if( !str ) str = strchr( valPtr, 0 ); jVal = json_mkstring( valPtr, str-valPtr ); } // O.k. - this should be a number... else { str = strchr( valPtr, ';' ); if( pbrk && (!str || str>pbrk) ) str = pbrk; if( !str ) str = strchr( valPtr, 0 ); jVal = _getInteger( valPtr, str ); if( !jVal ) jVal = _getReal( valPtr, str ); if( !jVal ) logwarn( "_segmentKeyValList: Expected number, but got \"%.*s\" (key: \"%s\"", str-valPtr, valPtr, key ); } // Store JSON element if( jVal && json_object_set(jObj,key,jVal) ) { logerr( "_segmentKeyValList: Cannot insert/set JSON for key \"%s\" in target.", key ); Sfree( key ); return -1; } if( jVal ) { retval++; DBGMSG( "_segmentKeyValList: Set entry for key \"%s\": \"%.*s\"", key, str-valPtr, valPtr ); } else { DBGMSG( "_segmentKeyValList: Cannot code entry for key \"%s\": \"%.*s\"", key, str-valPtr, valPtr ); } // Clean up and skip separator Sfree( key ); while( str && *str==';' ) str++; } // Return number of elements return retval; }
/*=========================================================================*\ Get device info as allocated JSON object string (no unicode escaping of strings) caller must free result and should lock the device returns NULL on error \*=========================================================================*/ static char *_ickDeviceStateJson( ickDevice_t *device, int indent ) { int rc; char *result; char *wsi; char *message; debug( "_ickDeviceStateJson: %s", device->uuid ); indent += JSON_INDENT; /*------------------------------------------------------------------------*\ Empty data? \*------------------------------------------------------------------------*/ if( !device ) return strdup( "null" ); /*------------------------------------------------------------------------*\ Create websocket information (if any) \*------------------------------------------------------------------------*/ /* fixme psd = // no way to get user data from a wsi outside a lws callback scope jWsi = _ickWsiStateJson( psd ); */ wsi = strdup( JSON_BOOL(device->wsi) ); if( !wsi ) { logerr( "_ickDeviceStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Create message information (if any) \*------------------------------------------------------------------------*/ message = _ickMessageStateJson( device->outQueue, indent+JSON_INDENT ); if( !message ) { Sfree( wsi ); logerr( "_ickDeviceStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Compile all debug info \*------------------------------------------------------------------------*/ rc = asprintf( &result, "{\n" "%*s\"name\": \"%s\",\n" "%*s\"tCreation\": %f,\n" "%*s\"UUID\": \"%s\",\n" "%*s\"location\": \"%s\",\n" "%*s\"upnpVersion\": %d,\n" "%*s\"p2pLevel\": %d,\n" "%*s\"lifetime\": %d,\n" "%*s\"services\": %d,\n" "%*s\"getXml\": \"%s\",\n" "%*s\"doConnect\": %s,\n" "%*s\"ssdpState\": \"%s\",\n" "%*s\"bootId\": \"%ld\",\n" "%*s\"configId\": \"%ld\",\n" "%*s\"connectionState\": \"%s\",\n" "%*s\"tXmlComplete\": %f,\n" "%*s\"tConnect\": %f,\n" "%*s\"tDisconnect\": %f,\n" "%*s\"rx\": %d,\n" "%*s\"rxSegmented\": %d,\n" "%*s\"rxPending\": %d,\n" "%*s\"tx\": %d,\n" "%*s\"txPending\": %d,\n" "%*s\"rxLast\": %f,\n" "%*s\"txLast\": %f,\n" "%*s\"wsi\": %s,\n" "%*s\"message\": %s\n" "%*s}", indent, "", JSON_STRING( device->friendlyName ), indent, "", JSON_REAL( device->tCreation ), indent, "", JSON_STRING( device->uuid ), indent, "", JSON_STRING( device->location ), indent, "", JSON_INTEGER( device->ickUpnpVersion ), indent, "", JSON_INTEGER( device->ickP2pLevel ), indent, "", JSON_INTEGER( device->lifetime ), indent, "", JSON_INTEGER( device->services ), indent, "", JSON_STRING( device->wget?_ickWGetUri(device->wget) : NULL ), indent, "", JSON_BOOL( device->doConnect ), indent, "", JSON_STRING( _ickDeviceSsdpState2Str(device->ssdpState) ), indent, "", JSON_LONG( device->ssdpBootId ), indent, "", JSON_LONG( device->ssdpConfigId ), indent, "", JSON_STRING( _ickDeviceConnState2Str(device->connectionState) ), indent, "", JSON_REAL( device->tXmlComplete ), indent, "", JSON_REAL( device->tConnect ), indent, "", JSON_REAL( device->tDisconnect ), indent, "", JSON_INTEGER( device->nRx ), indent, "", JSON_INTEGER( device->nRxSegmented ), indent, "", JSON_INTEGER( _ickDevicePendingInMessages(device) ), indent, "", JSON_INTEGER( device->nTx ), indent, "", JSON_INTEGER( _ickDevicePendingOutMessages(device) ), indent, "", JSON_REAL( device->tLastRx ), indent, "", JSON_REAL( device->tLastTx ), indent, "", JSON_OBJECT( wsi ), indent, "", JSON_OBJECT( message ), indent-JSON_INDENT, "" ); Sfree( wsi ); Sfree( message ); /*------------------------------------------------------------------------*\ Error \*------------------------------------------------------------------------*/ if( rc<0 ) { logerr( "_ickDeviceStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Return result \*------------------------------------------------------------------------*/ return result; }
/*=========================================================================*\ Get context info as allocated JSON object string including device list (no unicode escaping of strings) caller must free result returns NULL on error \*=========================================================================*/ static char *_ickContextStateJson( ickP2pContext_t *ictx, int indent ) { ickDevice_t *device; char *devices = NULL; ickInterface_t *interface; char *interfaces = NULL; int i; int rc; char *result; debug( "_ickContextStateJson (%p): %s", ictx, ictx->deviceUuid ); indent += JSON_INDENT; /*------------------------------------------------------------------------*\ Compile array of interfaces \*------------------------------------------------------------------------*/ _ickLibInterfaceListLock( ictx ); interfaces = strdup( "[" ); for( i=0,interface=ictx->interfaces; interface&&interfaces; i++,interface=interface->next ) { char *interfaceInfo; // Get debug info interfaceInfo = _ickInterfaceStateJson( interface, indent+JSON_INDENT ); if( !interfaceInfo ) { Sfree( interfaces ); break; } // Add to list rc = asprintf( &result, "%s%s\n%*s%s", interfaces, i?",":"", indent+JSON_INDENT, "", interfaceInfo ); Sfree( interfaces ); Sfree( interfaceInfo ); if( rc<0 ) break; else interfaces = result; } _ickLibInterfaceListUnlock( ictx ); // Close list if( interfaces ) { rc = asprintf( &result, "%s\n%*s]", interfaces, indent, "" ); Sfree( interfaces ); if( rc>0 ) interfaces = result; } // Error ? if( !interfaces ) { logerr( "_ickContextStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Compile array of devices \*------------------------------------------------------------------------*/ _ickLibDeviceListLock( ictx ); devices = strdup( "[" ); for( i=0,device=ictx->deviceList; device&&devices; i++,device=device->next ) { char *deviceInfo; // Get debug info deviceInfo = _ickDeviceStateJson( device, indent+JSON_INDENT ); if( !deviceInfo ) { Sfree( devices ); break; } // Add to list rc = asprintf( &result, "%s%s\n%*s%s", devices, i?",":"", indent+JSON_INDENT, "", deviceInfo ); Sfree( devices ); Sfree( deviceInfo ); if( rc<0 ) break; else devices = result; } _ickLibDeviceListUnlock( ictx ); // Close list if( devices ) { rc = asprintf( &result, "%s\n%*s]", devices, indent, "" ); Sfree( devices ); if( rc>0 ) devices = result; } // Error ? if( !devices ) { Sfree( interfaces ); logerr( "_ickContextStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Compile all context debug info \*------------------------------------------------------------------------*/ rc = asprintf( &result, "{\n" "%*s\"pid\": %d,\n" "%*s\"uuid\": \"%s\",\n" "%*s\"name\": \"%s\",\n" "%*s\"services\": %d,\n" "%*s\"upnpListenerPort\": %d,\n" "%*s\"wsPort\": %d,\n" "%*s\"folder\": \"%s\",\n" "%*s\"lifetime\": %d,\n" "%*s\"state\": \"%s\",\n" "%*s\"loopback\": %s,\n" "%*s\"customConnectMatrix\": %s,\n" "%*s\"tCreation\": %f,\n" "%*s\"tResume\": %f,\n" "%*s\"osName\": \"%s\",\n" "%*s\"p2pVersion\": \"%s\",\n" "%*s\"p2pLevel\": %d,\n" "%*s\"lwsVersion\": \"%s\",\n" "%*s\"bootId\": %ld,\n" "%*s\"configId\": %ld,\n" "%*s\"interfaces\": %s\n" "%*s\"devices\": %s\n" "%*s}\n", indent, "", JSON_INTEGER( getpid() ), indent, "", JSON_STRING( ictx->deviceUuid ), indent, "", JSON_STRING( ictx->deviceName ), indent, "", JSON_INTEGER( ictx->ickServices ), indent, "", JSON_INTEGER( ictx->upnpListenerPort ), indent, "", JSON_INTEGER( ictx->lwsPort ), indent, "", JSON_STRING( ictx->upnpFolder), indent, "", JSON_INTEGER( ictx->lifetime ), indent, "", JSON_STRING( ickLibState2Str(ictx->state) ), indent, "", JSON_BOOL( ictx->upnpLoopback ), indent, "", JSON_BOOL( ictx->lwsConnectMatrixCb==ickP2pDefaultConnectMatrixCb ), indent, "", JSON_REAL( ictx->tCreation ), indent, "", JSON_REAL( ictx->tResume ), indent, "", JSON_STRING( ictx->osName ), indent, "", JSON_STRING( ickP2pGetVersion(NULL,NULL) ), indent, "", JSON_INTEGER( ICKP2PLEVEL_SUPPORTED ), indent, "", JSON_STRING( lws_get_library_version() ), indent, "", JSON_LONG( ictx->upnpBootId ), indent, "", JSON_LONG( ictx->upnpConfigId ), indent, "", JSON_OBJECT( interfaces ), indent, "", JSON_OBJECT( devices ), indent-JSON_INDENT, "" ); Sfree( devices ); Sfree( interfaces ); /*------------------------------------------------------------------------*\ Error \*------------------------------------------------------------------------*/ if( rc<0 ) { logerr( "_ickContextStateJson: out of memory" ); return NULL; } /*------------------------------------------------------------------------*\ Return result \*------------------------------------------------------------------------*/ return result; }
/*=========================================================================*\ Get streaming reference for an item from cloud item - the item to be resolved returns a json list containing one element (equivalent to streamingRefs feature of items) returns NULL on error \*=========================================================================*/ json_t *ickServiceGetStreamingRef( PlaylistItem *item ) { const char *token; char *sid; ServiceListItem *service; json_t *jParams; json_t *jResult; json_t *jObj; json_t *jStreamingRefs; DBGMSG( "ickServiceGetStreamingRef: Item \"%s\" (%s)", playlistItemGetText(item), playlistItemGetId(item) ); /*------------------------------------------------------------------------*\ Need token for cloud access... \*------------------------------------------------------------------------*/ token = ickCloudGetAccessToken(); if( !token ) { logwarn( "ickServiceGetStreamingRef: Device not registered (no access token)." ); return NULL; } /*------------------------------------------------------------------------*\ Get service id from item id \*------------------------------------------------------------------------*/ if( !playlistItemGetId(item) ) { playlistItemLock( item ); logwarn( "ickServiceGetStreamingRef (%s): Item contains no id!", playlistItemGetText(item) ); playlistItemUnlock( item ); return NULL; } sid = strdup( playlistItemGetId(item) ); if( !sid ) { logwarn( "ickServiceGetStreamingRef: out of memory!" ); return NULL; } if( !strchr(sid,':') ) { playlistItemLock( item ); logwarn( "ickServiceGetStreamingRef (%s,%s): Malformed id (missing ':')!", playlistItemGetText(item), playlistItemGetId(item) ); playlistItemUnlock( item ); Sfree ( sid ); return NULL; } *strchr( sid, ':' ) = 0; /*------------------------------------------------------------------------*\ Find service descriptor \*------------------------------------------------------------------------*/ service = ickServiceFind( NULL, sid, NULL, ServiceCloud ); if( !service ) { playlistItemLock( item ); logwarn( "ickServiceGetStreamingRef (%s,%s): No such service \"%s\"!", playlistItemGetText(item), playlistItemGetId(item), sid ); playlistItemUnlock( item ); Sfree ( sid ); return NULL; } Sfree ( sid ); DBGMSG( "ickServiceGetStreamingRef (%s,%s): using service \"%s\" (%s)", playlistItemGetText(item), playlistItemGetId(item), service->name, service->type ); /*------------------------------------------------------------------------*\ Collect parameters \*------------------------------------------------------------------------*/ jParams = json_object(); json_object_set_new( jParams, "itemId", json_string(playlistItemGetId(item)) ); //Fixme: collect supported formats /*------------------------------------------------------------------------*\ Interact with cloud \*------------------------------------------------------------------------*/ jResult = ickCloudRequestSync( service->url, token, "getItemStreamingRef", jParams, NULL ); json_decref( jParams ); if( !jResult ) { logwarn( "ickServiceGetStreamingRef: No answer from cloud." ); return NULL; } /*------------------------------------------------------------------------*\ Server indicated error? \*------------------------------------------------------------------------*/ jObj = json_object_get( jResult, "error" ); if( jObj ) { logwarn( "ickServiceGetStreamingRef: Error %s.", json_rpcerrstr(jObj) ); json_decref( jResult ); return NULL; } /*------------------------------------------------------------------------*\ Get result \*------------------------------------------------------------------------*/ jObj = json_object_get( jResult, "result" ); if( !jObj ) { logerr( "ickServiceGetStreamingRef: No \"result\" object in answer." ); json_decref( jResult ); return NULL; } /*------------------------------------------------------------------------*\ Convert to list to be compatible with streamingRefs feature of items \*------------------------------------------------------------------------*/ jStreamingRefs = json_array(); if( !jObj ) { logerr( "ickServiceGetStreamingRef: out of memory!" ); json_decref( jResult ); return NULL; } if( json_array_append(jStreamingRefs,jObj) ) { logerr( "ickServiceGetStreamingRef: json_arry_append() failed." ); json_decref( jResult ); return NULL; } /*------------------------------------------------------------------------*\ That's it \*------------------------------------------------------------------------*/ json_decref( jResult ); return jStreamingRefs; }
/*=========================================================================*\ Get address and network mask of an interface ifname - be interface name or address addr - pointer to addr of interface (might be NULL) netmask - pointer to network mask of interface (might be NULL) name - pointer to name of interface, will be an allocated string (might be NULL) *addr and *netmask are in network byte order return 0 on success or error code \*=========================================================================*/ ickErrcode_t _ickIpGetIfAddr( const char *ifname, in_addr_t *addr, in_addr_t *netmask, char **name ) { char *buffer; struct ifconf ifc; size_t pos; in_addr_t ifaddr; int sd; int rc; ickErrcode_t irc = ICKERR_NOINTERFACE; /*------------------------------------------------------------------------*\ Create virtual socket \*------------------------------------------------------------------------*/ sd = socket( PF_INET, SOCK_DGRAM, 0 ); if( sd<0 ) { logerr( "_ickIpGetIfAddr: could not get socket (%s)", strerror(errno) ); return ICKERR_NOSOCKET; } /*------------------------------------------------------------------------*\ Is this already a valid IP address? \*------------------------------------------------------------------------*/ ifaddr = inet_addr( ifname ); /*------------------------------------------------------------------------*\ Allocate buffer \*------------------------------------------------------------------------*/ buffer = calloc( 1, ICK_IFRBUFFERSIZE ); /*------------------------------------------------------------------------*\ Get all known interfaces \*------------------------------------------------------------------------*/ ifc.ifc_len = ICK_IFRBUFFERSIZE; ifc.ifc_req = (struct ifreq *)buffer; if( ioctl(sd,SIOCGIFCONF,&ifc)<0 ) { close( sd ); logerr( "_ickIpGetIfAddr: ioctl(SIOCGIFCONF) failed (%s)", strerror(errno) ); return ICKERR_NOSOCKET; } /*------------------------------------------------------------------------*\ Loop over interface list \*------------------------------------------------------------------------*/ size_t len; for( pos=0; pos<ifc.ifc_len; pos+=len ) { // Get current interface descriptor struct ifreq *ifr= (struct ifreq *)(buffer + pos ); debug( "_ickIpGetIfAddr: testing interface \"%s\"", ifr->ifr_name ); // Get actual size of interface descriptor #ifdef linux len = sizeof( struct ifreq ); #else len = IFNAMSIZ + ifr->ifr_addr.sa_len; #endif // We are looking for an internet interface if( ifr->ifr_addr.sa_family!=AF_INET ) continue; // If ifname is no address the interface name must match if( ifaddr==INADDR_NONE && strcasecmp(ifname,ifr->ifr_name) ) continue; // Get interface address rc = ioctl( sd, SIOCGIFADDR, ifr ); if( rc<0 ) { logerr( "_ickIpGetIfAddr: ioctl(SIOCGIFADDR) failed (%s)", strerror(errno) ); irc = ICKERR_NOINTERFACE; break; } // If ifname is an address it must match if( ifaddr!=INADDR_NONE && ifaddr!=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr ) continue; // Store address if( addr ) *addr = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr; // Get and store interface network mask if( netmask ) { rc = ioctl( sd, SIOCGIFNETMASK, ifr ); if( rc<0 ) { logerr( "_ickIpGetIfAddr: ioctl(SIOCGIFNETMASK) failed (%s)", strerror(errno) ); irc = ICKERR_NOINTERFACE; break; } *netmask = ((struct sockaddr_in *)(&ifr->ifr_addr))->sin_addr.s_addr; } // Duplicate real interface name if( name ) { *name = strdup( ifr->ifr_name ); if( !*name ) { logerr( "_ickIpGetIfAddr: out of memory" ); irc = ICKERR_NOMEM; break; } } // Found it! debug( "_ickIpGetIfAddr (%s): found interface \"%s\"", ifname, ifr->ifr_name ); irc = ICKERR_SUCCESS; break; } /*------------------------------------------------------------------------*\ Clean up and return result \*------------------------------------------------------------------------*/ Sfree( buffer ); close( sd ); return irc; }