static void test( struct reader **pp_readers, unsigned int i_readers, const char *psz_md5 ) { #define READ_AT( i_offset, i_read ) \ read_at( pp_readers, i_readers, p_buf, i_offset, i_read, i_size ) #define PEEK_AT( i_offset, i_read ) \ read_at( pp_readers, i_readers, NULL, i_offset, i_read, i_size ) uint8_t p_buf[4096]; ssize_t i_ret = 0; uint64_t i_offset = 0; uint64_t i_size; char *psz_read_md5; struct md5_s md5; /* Compare size between each readers */ i_size = pp_readers[0]->pf_getsize( pp_readers[0] ); assert( i_size > 0 ); log( "stream size: %"PRIu64"\n", i_size ); for( unsigned int i = 1; i < i_readers; ++i ) assert( pp_readers[i]->pf_getsize( pp_readers[i] ) == i_size ); /* Read the whole file and compare between each readers */ if( psz_md5 != NULL ) InitMD5( &md5 ); while( ( i_ret = READ_AT( i_offset, 4096 ) ) > 0 ) { i_offset += i_ret; if( psz_md5 != NULL ) AddMD5( &md5, p_buf, i_ret ); } if( psz_md5 != NULL ) { EndMD5( &md5 ); psz_read_md5 = psz_md5_hash( &md5 ); assert( psz_read_md5 ); assert( strcmp( psz_read_md5, psz_md5 ) == 0 ); free( psz_read_md5 ); } /* Test cache skip */ i_offset = 9 * i_size / 10; while( i_offset < i_size && ( i_ret = READ_AT( i_offset, 4096 ) ) > 0 ) i_offset += i_ret + 1; /* Test seek and peek */ READ_AT( 0, 42 ); READ_AT( i_size - 5, 43 ); READ_AT( 1, 45 ); READ_AT( 2, 45 ); READ_AT( i_size / 2, 45 ); READ_AT( 2, 45 ); READ_AT( 1, 45 ); PEEK_AT( 0, 46 ); PEEK_AT( i_size - 23, 46 ); PEEK_AT( i_size / 2, 46 ); PEEK_AT( 0, 46 ); }
static int Send( sout_stream_t *p_stream, void *_id, block_t *p_buffer ) { sout_stream_sys_t *p_sys = (sout_stream_sys_t *)p_stream->p_sys; sout_stream_id_sys_t *id = (sout_stream_id_sys_t *)_id; struct md5_s hash; block_t *p_block = p_buffer; while ( p_block != NULL ) { InitMD5( &hash ); AddMD5( &hash, p_block->p_buffer, p_block->i_buffer ); AddMD5( &id->hash, p_block->p_buffer, p_block->i_buffer ); EndMD5( &hash ); char *outputhash = psz_md5_hash( &hash ); /* We could just set p_sys->output to stdout and remove user of msg_Dbg * if we don't need ability to output info to gui modules (like qt messages window */ vlc_tick_t dts_difference = VLC_TICK_INVALID; if( likely( id->previous_dts != VLC_TICK_INVALID ) ) dts_difference = p_block->i_dts - id->previous_dts; if( p_sys->output ) { /* Write data in a form that it's easy to plot for example with gnuplot*/ fprintf( p_sys->output, "%s\t%d\t%s\t%"PRIu64"\t%"PRId64"\t%"PRId64"\t%16s\n", p_sys->prefix, id->id, id->type, ++id->segment_number, dts_difference, p_block->i_length, outputhash ); } else { msg_Dbg( p_stream, "%s: track:%d type:%s segment_number:%"PRIu64" dts_difference:%"PRId64" length:%"PRId64" md5:%16s", p_sys->prefix, id->id, id->type, ++id->segment_number, dts_difference, p_block->i_length, outputhash ); } id->track_duration += p_block->i_length ? p_block->i_length : dts_difference; free( outputhash ); id->previous_dts = p_block->i_dts; p_block = p_block->p_next; } if( p_stream->p_next ) return sout_StreamIdSend( p_stream->p_next, id->next_id, p_buffer ); else block_Release( p_buffer ); return VLC_SUCCESS; }
static char* ArtCacheGetDirPath( const char *psz_arturl, const char *psz_artist, const char *psz_album, const char *psz_title ) { char *psz_dir; char *psz_cachedir = config_GetUserDir(VLC_CACHE_DIR); if( !EMPTY_STR(psz_artist) && !EMPTY_STR(psz_album) ) { char *psz_album_sanitized = strdup( psz_album ); filename_sanitize( psz_album_sanitized ); char *psz_artist_sanitized = strdup( psz_artist ); filename_sanitize( psz_artist_sanitized ); if( asprintf( &psz_dir, "%s" DIR_SEP "art" DIR_SEP "artistalbum" DIR_SEP "%s" DIR_SEP "%s", psz_cachedir, psz_artist_sanitized, psz_album_sanitized ) == -1 ) psz_dir = NULL; free( psz_album_sanitized ); free( psz_artist_sanitized ); } else { /* If artist or album are missing, cache by art download URL. * If the URL is an attachment://, add the title to the cache name. * It will be md5 hashed to form a valid cache filename. * We assume that psz_arturl is always the download URL and not the * already hashed filename. * (We should never need to call this function if art has already been * downloaded anyway). */ struct md5_s md5; InitMD5( &md5 ); AddMD5( &md5, psz_arturl, strlen( psz_arturl ) ); if( !strncmp( psz_arturl, "attachment://", 13 ) ) AddMD5( &md5, psz_title, strlen( psz_title ) ); EndMD5( &md5 ); char * psz_arturl_sanitized = psz_md5_hash( &md5 ); if( asprintf( &psz_dir, "%s" DIR_SEP "art" DIR_SEP "arturl" DIR_SEP "%s", psz_cachedir, psz_arturl_sanitized ) == -1 ) psz_dir = NULL; free( psz_arturl_sanitized ); } free( psz_cachedir ); return psz_dir; }
void vlc_rand_bytes (void *buf, size_t len) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static uint64_t counter = 0; uint64_t stamp = NTPtime64 (); while (len > 0) { uint64_t val; struct md5_s mdi, mdo; InitMD5 (&mdi); InitMD5 (&mdo); pthread_mutex_lock (&lock); if (counter == 0) vlc_rand_init (); val = counter++; AddMD5 (&mdi, ikey, sizeof (ikey)); AddMD5 (&mdo, okey, sizeof (okey)); pthread_mutex_unlock (&lock); AddMD5 (&mdi, &stamp, sizeof (stamp)); AddMD5 (&mdi, &val, sizeof (val)); EndMD5 (&mdi); AddMD5 (&mdo, mdi.p_digest, sizeof (mdi.p_digest)); EndMD5 (&mdo); if (len < sizeof (mdo.p_digest)) { memcpy (buf, mdo.p_digest, len); break; } memcpy (buf, mdo.p_digest, sizeof (mdo.p_digest)); len -= sizeof (mdo.p_digest); buf = ((uint8_t *)buf) + sizeof (mdo.p_digest); } }
static char *GenerateCnonce() { char ps_random[32]; struct md5_s md5; vlc_rand_bytes( ps_random, sizeof( ps_random ) ); InitMD5( &md5 ); AddMD5( &md5, ps_random, sizeof( ps_random ) ); EndMD5( &md5 ); return psz_md5_hash( &md5 ); }
static void test_config_StringEscape() { for( int i = 0; md5_samples[i].psz_string; i++ ) { struct md5_s md5; InitMD5( &md5 ); AddMD5( &md5, md5_samples[i].psz_string, strlen( md5_samples[i].psz_string ) ); EndMD5( &md5 ); char * psz_hash = psz_md5_hash( &md5 ); if( strcmp( psz_hash, md5_samples[i].psz_md5 ) ) { printf( "Output: %s\nExpected: %s\n", psz_hash, md5_samples[i].psz_md5 ); abort(); } free( psz_hash ); } }
static int hash( char *string, const char *path ) { static char buffer[BUFFERSIZE]; ssize_t len; struct md5_s md5; int in = rb->open( path, O_RDONLY ); if( in < 0 ) return -1; InitMD5( &md5 ); while( !quit && ( len = rb->read( in, buffer, sizeof(buffer) ) ) > 0 ) { AddMD5( &md5, buffer, len ); if( rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK) == ACTION_STD_CANCEL ) quit = true; } EndMD5( &md5 ); psz_md5_hash( string, &md5 ); rb->close( in ); return 0; }
int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid ) { struct md5_s ctx; int title; int title_sets; int nr_of_files = 0; ifo_handle_t *vmg_ifo; /* Check arguments. */ if( dvd == NULL || discid == NULL ) return 0; vmg_ifo = ifoOpen( dvd, 0 ); if( !vmg_ifo ) { fprintf( stderr, "libdvdread: DVDDiscId, failed to " "open VMG IFO!\n" ); return -1; } title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1; ifoClose( vmg_ifo ); if( title_sets > 10 ) title_sets = 10; /* Go through the first IFO:s, in order, up until the tenth, * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */ InitMD5( &ctx ); for( title = 0; title < title_sets; title++ ) { dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE ); if( dvd_file != NULL ) { ssize_t bytes_read; ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN; char *buffer_base = malloc( file_size + 2048 ); if( buffer_base == NULL ) { DVDCloseFile( dvd_file ); fprintf( stderr, "libdvdread: DVDDiscId, failed to " "allocate memory for file read!\n" ); return -1; } char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048); bytes_read = DVDReadBytes( dvd_file, buffer, file_size ); if( bytes_read != file_size ) { fprintf( stderr, "libdvdread: DVDDiscId read returned %zd bytes" ", wanted %zd\n", bytes_read, file_size ); DVDCloseFile( dvd_file ); free( buffer_base ); return -1; } AddMD5( &ctx, buffer, file_size ); DVDCloseFile( dvd_file ); free( buffer_base ); nr_of_files++; } } EndMD5( &ctx ); memcpy( discid, ctx.buf, 16 ); if(!nr_of_files) return -1; return 0; }
/***************************************************************************** * Handshake : Init audioscrobbler connection *****************************************************************************/ static int Handshake(intf_thread_t *p_this) { char *psz_username, *psz_password; char *psz_scrobbler_url; time_t timestamp; char psz_timestamp[21]; struct md5_s p_struct_md5; stream_t *p_stream; char *psz_handshake_url; uint8_t p_buffer[1024]; char *p_buffer_pos; int i_ret; char *psz_url; intf_thread_t *p_intf = (intf_thread_t*) p_this; intf_sys_t *p_sys = p_this->p_sys; psz_username = var_InheritString(p_this, "lastfm-username"); psz_password = var_InheritString(p_this, "lastfm-password"); /* username or password have not been setup */ if (EMPTY_STR(psz_username) || EMPTY_STR(psz_password)) { free(psz_username); free(psz_password); return VLC_ENOVAR; } time(×tamp); /* generates a md5 hash of the password */ InitMD5(&p_struct_md5); AddMD5(&p_struct_md5, (uint8_t*) psz_password, strlen(psz_password)); EndMD5(&p_struct_md5); free(psz_password); char *psz_password_md5 = psz_md5_hash(&p_struct_md5); if (!psz_password_md5) { free(psz_username); return VLC_ENOMEM; } snprintf(psz_timestamp, sizeof(psz_timestamp), "%"PRIu64, (uint64_t)timestamp); /* generates a md5 hash of : * - md5 hash of the password, plus * - timestamp in clear text */ InitMD5(&p_struct_md5); AddMD5(&p_struct_md5, (uint8_t*) psz_password_md5, 32); AddMD5(&p_struct_md5, (uint8_t*) psz_timestamp, strlen(psz_timestamp)); EndMD5(&p_struct_md5); free(psz_password_md5); char *psz_auth_token = psz_md5_hash(&p_struct_md5); if (!psz_auth_token) { free(psz_username); return VLC_ENOMEM; } psz_scrobbler_url = var_InheritString(p_this, "scrobbler-url"); if (!psz_scrobbler_url) { free(psz_auth_token); free(psz_username); return VLC_ENOMEM; } i_ret = asprintf(&psz_handshake_url, "http://%s/?hs=true&p=1.2&c="CLIENT_NAME"&v="CLIENT_VERSION"&u=%s&t=%s&a=%s" , psz_scrobbler_url, psz_username, psz_timestamp, psz_auth_token); free(psz_auth_token); free(psz_scrobbler_url); free(psz_username); if (i_ret == -1) return VLC_ENOMEM; /* send the http handshake request */ p_stream = stream_UrlNew(p_intf, psz_handshake_url); free(psz_handshake_url); if (!p_stream) return VLC_EGENERIC; /* read answer */ i_ret = stream_Read(p_stream, p_buffer, sizeof(p_buffer) - 1); if (i_ret == 0) { stream_Delete(p_stream); return VLC_EGENERIC; } p_buffer[i_ret] = '\0'; stream_Delete(p_stream); p_buffer_pos = strstr((char*) p_buffer, "FAILED "); if (p_buffer_pos) { /* handshake request failed, sorry */ msg_Err(p_this, "last.fm handshake failed: %s", p_buffer_pos + 7); return VLC_EGENERIC; } if (strstr((char*) p_buffer, "BADAUTH")) { /* authentication failed, bad username/password combination */ dialog_Fatal(p_this, _("last.fm: Authentication failed"), "%s", _("last.fm username or password is incorrect. " "Please verify your settings and relaunch VLC.")); return VLC_AUDIOSCROBBLER_EFATAL; } if (strstr((char*) p_buffer, "BANNED")) { /* oops, our version of vlc has been banned by last.fm servers */ msg_Err(p_intf, "This version of VLC has been banned by last.fm. " "You should upgrade VLC, or disable the last.fm plugin."); return VLC_AUDIOSCROBBLER_EFATAL; } if (strstr((char*) p_buffer, "BADTIME")) { /* The system clock isn't good */ msg_Err(p_intf, "last.fm handshake failed because your clock is too " "much shifted. Please correct it, and relaunch VLC."); return VLC_AUDIOSCROBBLER_EFATAL; } p_buffer_pos = strstr((char*) p_buffer, "OK"); if (!p_buffer_pos) goto proto; p_buffer_pos = strstr(p_buffer_pos, "\n"); if (!p_buffer_pos || strlen(p_buffer_pos) < 33) goto proto; p_buffer_pos++; /* we skip the '\n' */ /* save the session ID */ memcpy(p_sys->psz_auth_token, p_buffer_pos, 32); p_sys->psz_auth_token[32] = '\0'; p_buffer_pos = strstr(p_buffer_pos, "http://"); if (!p_buffer_pos || strlen(p_buffer_pos) == 7) goto proto; /* We need to read the nowplaying url */ p_buffer_pos += 7; /* we skip "http://" */ #if 0 //NOT USED psz_url = strndup(p_buffer_pos, strcspn(p_buffer_pos, "\n")); if (!psz_url) goto oom; switch(ParseURL(psz_url, &p_sys->psz_nowp_host, &p_sys->psz_nowp_file, &p_sys->i_nowp_port)) { case VLC_ENOMEM: goto oom; case VLC_EGENERIC: goto proto; case VLC_SUCCESS: default: break; } #endif p_buffer_pos = strstr(p_buffer_pos, "http://"); if (!p_buffer_pos || strlen(p_buffer_pos) == 7) goto proto; /* We need to read the submission url */ p_buffer_pos += 7; /* we skip "http://" */ psz_url = strndup(p_buffer_pos, strcspn(p_buffer_pos, "\n")); if (!psz_url) goto oom; /* parse the submission url */ vlc_UrlParse(&p_sys->p_submit_url, psz_url, 0); free(psz_url); return VLC_SUCCESS; oom: return VLC_ENOMEM; proto: msg_Err(p_intf, "Handshake: can't recognize server protocol"); return VLC_EGENERIC; }
static char *AuthDigest( vlc_object_t *p_this, http_auth_t *p_auth, const char *psz_method, const char *psz_path, const char *psz_username, const char *psz_password ) { char *psz_HA1 = NULL; char *psz_HA2 = NULL; char *psz_ent = NULL; char *psz_result = NULL; char psz_inonce[9]; struct md5_s md5; struct md5_s ent; if ( p_auth->psz_realm == NULL ) { msg_Warn( p_this, "Digest Authentication: " "Mandatory 'realm' value not available" ); goto error; } /* H(A1) */ if ( p_auth->psz_HA1 ) { psz_HA1 = strdup( p_auth->psz_HA1 ); if ( psz_HA1 == NULL ) goto error; } else { InitMD5( &md5 ); AddMD5( &md5, psz_username, strlen( psz_username ) ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_realm, strlen( p_auth->psz_realm ) ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, psz_password, strlen( psz_password ) ); EndMD5( &md5 ); psz_HA1 = psz_md5_hash( &md5 ); if ( psz_HA1 == NULL ) goto error; if ( p_auth->psz_algorithm && strcmp( p_auth->psz_algorithm, "MD5-sess" ) == 0 ) { InitMD5( &md5 ); AddMD5( &md5, psz_HA1, 32 ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); EndMD5( &md5 ); free( psz_HA1 ); psz_HA1 = psz_md5_hash( &md5 ); if ( psz_HA1 == NULL ) goto error; p_auth->psz_HA1 = strdup( psz_HA1 ); if ( p_auth->psz_HA1 == NULL ) goto error; } } /* H(A2) */ InitMD5( &md5 ); if ( *psz_method ) AddMD5( &md5, psz_method, strlen( psz_method ) ); AddMD5( &md5, ":", 1 ); if ( psz_path ) AddMD5( &md5, psz_path, strlen( psz_path ) ); else AddMD5( &md5, "/", 1 ); if ( p_auth->psz_qop && strcmp( p_auth->psz_qop, "auth-int" ) == 0 ) { InitMD5( &ent ); /* TODO: Support for "qop=auth-int" */ AddMD5( &ent, "", 0 ); EndMD5( &ent ); psz_ent = psz_md5_hash( &ent ); if ( psz_ent == NULL ) goto error; AddMD5( &md5, ":", 1 ); AddMD5( &md5, psz_ent, 32 ); } EndMD5( &md5 ); psz_HA2 = psz_md5_hash( &md5 ); if ( psz_HA2 == NULL ) goto error; /* Request digest */ InitMD5( &md5 ); AddMD5( &md5, psz_HA1, 32 ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); AddMD5( &md5, ":", 1 ); if ( p_auth->psz_qop && ( strcmp( p_auth->psz_qop, "auth" ) == 0 || strcmp( p_auth->psz_qop, "auth-int" ) == 0 ) ) { snprintf( psz_inonce, sizeof( psz_inonce ), "%08x", p_auth->i_nonce ); AddMD5( &md5, psz_inonce, 8 ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); AddMD5( &md5, ":", 1 ); AddMD5( &md5, p_auth->psz_qop, strlen( p_auth->psz_qop ) ); AddMD5( &md5, ":", 1 ); } AddMD5( &md5, psz_HA2, 32 ); EndMD5( &md5 ); psz_result = psz_md5_hash( &md5 ); error: free( psz_HA1 ); free( psz_HA2 ); free( psz_ent ); return psz_result; }
static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; struct md5_s hash; InitMD5(&hash); if (subpicture) { for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) { AddMD5(&hash, &r->i_x, sizeof(r->i_x)); AddMD5(&hash, &r->i_y, sizeof(r->i_y)); AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width)); AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height)); AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset)); AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset)); const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch + r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch; for (int y = 0; y < r->fmt.i_visible_height; y++) AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width); } } EndMD5(&hash); if (!memcmp(hash.buf, sys->hash, 16)) return; memcpy(sys->hash, hash.buf, 16); jobject jsurf = jni_LockAndGetSubtitlesSurface(); if (sys->window && jsurf != sys->jsurf) { sys->native_window.winRelease(sys->window); sys->window = NULL; } sys->jsurf = jsurf; if (!sys->window) { JNIEnv *p_env; jni_attach_thread(&p_env, THREAD_NAME); sys->window = sys->native_window.winFromSurface(p_env, jsurf); jni_detach_thread(); } ANativeWindow_Buffer buf = { 0 }; int32_t err = sys->native_window.winLock(sys->window, &buf, NULL); if (err) { jni_UnlockAndroidSurface(); return; } if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height) { /* Wrap the NativeWindow corresponding to the subtitles surface in a picture_t */ picture_t *picture = sys->subtitles_picture; picture->p[0].p_pixels = (uint8_t*)buf.bits; picture->p[0].i_lines = buf.height; picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * buf.stride; /* Clear the subtitles surface. */ memset(picture->p[0].p_pixels, 0, picture->p[0].i_pitch * picture->p[0].i_lines); if (subpicture) { /* Allocate a blending filter if needed. */ if (unlikely(!sys->p_spu_blend)) sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd), &picture->format); picture_BlendSubpicture(picture, sys->p_spu_blend, subpicture); } } sys->native_window.unlockAndPost(sys->window); jni_UnlockAndroidSurface(); }
static int vlclua_sd_add_item( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "path" ); if( lua_isstring( L, -1 ) ) { const char *psz_path = lua_tostring( L, -1 ); lua_getfield( L, -2, "title" ); const char *psz_title = luaL_checkstring( L, -1 ) ? luaL_checkstring( L, -1 ) : psz_path; /* The table must be at the top of the stack when calling * vlclua_read_options() */ char **ppsz_options = NULL; int i_options = 0; lua_pushvalue( L, -3 ); vlclua_read_options( p_sd, L, &i_options, &ppsz_options ); input_item_t *p_input = input_item_NewExt( psz_path, psz_title, i_options, (const char **)ppsz_options, VLC_INPUT_OPTION_TRUSTED, -1 ); lua_pop( L, 3 ); if( p_input ) { vlclua_read_meta_data( p_sd, L, p_input ); /* This one is to be tested... */ vlclua_read_custom_meta_data( p_sd, L, p_input ); /* The duration is given in seconds, convert to microseconds */ lua_getfield( L, -1, "duration" ); if( lua_isnumber( L, -1 ) ) input_item_SetDuration( p_input, (lua_tonumber( L, -1 )*1e6) ); else if( !lua_isnil( L, -1 ) ) msg_Warn( p_sd, "Item duration should be a number (in seconds)." ); lua_pop( L, 1 ); lua_getfield( L, -1, "category" ); if( lua_isstring( L, -1 ) ) services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) ); else services_discovery_AddItem( p_sd, p_input, NULL ); lua_pop( L, 1 ); /* string to build the input item uid */ lua_getfield( L, -1, "uiddata" ); if( lua_isstring( L, -1 ) ) { char *s = strdup( luaL_checkstring( L, -1 ) ); if ( s ) { struct md5_s md5; InitMD5( &md5 ); AddMD5( &md5, s, strlen( s ) ); EndMD5( &md5 ); free( s ); s = psz_md5_hash( &md5 ); if ( s ) input_item_AddInfo( p_input, "uid", "md5", "%s", s ); free( s ); } } lua_pop( L, 1 ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "input_item_t" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_item_reg ); lua_setfield( L, -2, "__index" ); lua_pushliteral( L, "none of your business" ); lua_setfield( L, -2, "__metatable" ); } lua_setmetatable( L, -2 ); vlc_gc_decref( p_input ); } while( i_options > 0 ) free( ppsz_options[--i_options] ); free( ppsz_options ); } else msg_Err( p_sd, "vlc.sd.add_item: the \"path\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_item arguments" ); return 1; }