| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- /*
- released as a public domain
- std.denis, 2009
- */
- #include "../../structures.h"
- #include "lastFripper.h"
- #define strdup _strdup
- #define strnicmp _strnicmp
- #define ltoa _ltoa
- #define mkdir _mkdir
- #define PART_SUFFIX "_partial"
- void my_mkdir( char* name )
- {
- char* pdir = name;
-
- while( 1 ) {
- char ch;
- char* pnext = pdir;
- while( *pnext && *pnext != '\\' && *pnext != '/' )
- pnext++;
- if( *pnext == 0 )
- break;
- ch = *pnext;
- *pnext = 0;
- mkdir( name );
- *pnext = ch;
- pdir = pnext + 1;
- }
- mkdir( name );
- }
- __inline void myOutputDebugStringA1( char* str )
- {
- /* char fname[128];
- ltoa( GetCurrentThreadId(), fname, 10 );
- FILE* fp = fopen( fname, "ab" );
- fputs( str, fp );
- fputs( "\r\n", fp );
- fflush( fp );
- fclose( fp );
- */
- }
- __inline void myOutputDebugStringA( void* buf, int len )
- {
- /* char fname[128];
- ltoa( GetCurrentThreadId(), fname, 10 );
- FILE* fp = fopen( fname, "ab" );
- fwrite( buf, len, 1, fp );
- fputs( "\r\n", fp );
- fflush( fp );
- fclose( fp ); */
- }
- #ifndef isnumber
- #define isnumber(i_n_arg) ((i_n_arg>='0')&&(i_n_arg<='9'))
- #endif
- #define sizearr(x) (sizeof(x)/sizeof(x[0]))
- #define xmalloc( type, len ) ((type*)malloc( (len) * sizeof(type) ) )
- #define xcalloc( type, len ) ((type*)calloc( (len), sizeof(type) ) )
- int clean_filename( char* filename )
- {
- int i;
- for( i = 0; filename[i]; i++ )
- switch( filename[i] ) {
- case '*':
- case '?':
- case '<':
- case '>':
- case '\\':
- case '/':
- case ':':
- case '"':
- filename[i] = '_';
- break;
- }
- return i;
- }
- static struct pluginlink * pl;
- static int lfm_loaded = 0;
- static char* g_folder = 0;
- static char* g_format = 0;
- static int lfm_format_filename( struct playlist_item* p_item, char* p_filename, int n_filename )
- {
- int i = 0, j;
- char ch;
- char* fmt;
- char *ff = NULL;
- if( g_folder && *g_folder ) {
- strncpy( p_filename, g_folder, n_filename );
- i = strlen( p_filename );
- if( i < n_filename-1 && p_filename[i-1] != '\\' )
- p_filename[i++] = '\\';
- }
-
- fmt = g_format;
- if( fmt == NULL ) fmt = "%a\\%t.mp3";
- while( ( ch = *fmt++ ) != 0 ) {
- if( ch == '%' ) {
- char* p_sz = NULL;
- char a_sz[32];
- ch = *fmt++;
- switch( ch ) {
- case 0:
- fmt--; break;
- case '%':
- p_sz = "%";
- break;
- case 'n': {
- static unsigned ndx = 0;
- ltoa( ndx++, a_sz, 10 );
- p_sz = a_sz;
- break;
- }
- case 'a':
- p_sz = p_item->artist;
- break;
- case 't':
- p_sz = p_item->title;
- break;
- case 'l':
- p_sz = p_item->album;
- break;
- }
- if( p_sz ) {
- strncpy( p_filename+i, p_sz, n_filename-i );
- p_filename[n_filename-1] = 0;
- i += clean_filename( p_filename+i );
- }
- } else
- if( i < n_filename ) p_filename[i++] = ch;
- }
-
- for( j = i-1; j >= 0; j-- )
- if( p_filename[j] == '\\' || p_filename[j] == '/' ) {
- ff = p_filename + j;
- break;
- }
- if( ff ) {
- char ch = *ff;
- *ff = 0;
- my_mkdir( p_filename );
- *ff = ch;
- }
- return i;
- }
- static void lfm_close_request( struct lfm_client_data* p_client )
- {
- struct lfm_filter_data* p_data;
- if( p_client == NULL ) return;
- p_data = p_client->p_data;
- if( p_data == NULL ) return;
- if( p_client->req_type == LFM_PLAYLIST ) {
- free( p_client->pl_xml );
- } else if( p_client->req_type == LFM_GET && p_client->fp ) {
- struct playlist_item* p_item = p_client->pl_item;
- if( p_item ) {
- char id3v1[128];
- memset( id3v1, 0, 128 );
- strcpy( id3v1, "TAG" );
- strncpy( id3v1+3, p_item->title, 30 );
- strncpy( id3v1+33, p_item->artist, 30 );
- strncpy( id3v1+63, p_item->album, 30 );
- id3v1[127] = -1;
- fwrite( id3v1, 128, 1, p_client->fp );
- }
- fclose( p_client->fp );
- /*
- if( p_item ) {
- char filename_part[512], filename[512];
- int i = lfm_format_filename( p_item, filename_part, sizearr(filename_part) );
- memcpy( filename, filename_part, sizeof(filename) );
- strncpy( filename_part+i, PART_SUFFIX, sizearr(filename_part)-i );
- rename( filename_part, filename );
- }
- */
- pthread_mutex_lock( &p_data->mutex );
- if( p_data->playlist != NULL && p_item != NULL ) {
- if( p_data->playlist == p_item )
- p_data->playlist = p_item->next;
- else {
- struct playlist_item *p_last = p_data->playlist;
- while( p_last->next != NULL && p_last->next != p_item )
- p_last = p_last->next;
- if( p_last->next )
- p_last->next = p_item->next;
- }
- if( p_item->artist ) free( p_item->artist );
- if( p_item->title ) free( p_item->title );
- if( p_item->album ) free( p_item->album );
- if( p_item->url ) free( p_item->url );
- free( p_item );
- }
- pthread_mutex_unlock( &p_data->mutex );
- }
- p_client->req_type = LFM_NONE;
- }
- static void* lfm_filter_open( void * idata, struct srvparam * param ){
- struct lfm_filter_data* pdata = (struct lfm_filter_data*)idata;
- if( pdata ){
- pthread_mutex_lock( &pdata->mutex );
- pdata->refs++;
- pthread_mutex_unlock( &pdata->mutex );
- } else {
- if( ( pdata = xcalloc( struct lfm_filter_data, 1 ) ) != NULL ) {
- pthread_mutex_init( &pdata->mutex, NULL );
- pdata->playlist = NULL;
- pdata->refs++;
- }
- }
- return pdata;
- }
- static FILTER_ACTION lfm_filter_client( void *fo, struct clientparam* param, void** fc )
- {
- struct lfm_filter_data* p_data;
- struct lfm_client_data* p_client;
- *fc = NULL;
- if( fo == NULL ) return PASS;
- p_data = (struct lfm_filter_data*)fo;
- p_client = xcalloc( struct lfm_client_data, 1 );
- if( p_client == NULL ) return PASS;
- p_client->p_data = p_data;
- p_client->req_type = LFM_NONE;
-
- *fc = p_client;
- return CONTINUE;
- }
- static void lfm_filter_clear( void *fc )
- {
- struct lfm_client_data* p_client;
- p_client = (struct lfm_client_data*)fc;
- if( p_client == NULL ) return;
-
- lfm_close_request( p_client );
- free( p_client );
- }
- static void lfm_filter_close(void *fo){
- struct lfm_filter_data* p_data;
- struct playlist_item* p_item;
- p_data = (struct lfm_filter_data*)fo;
- if( p_data == NULL ) return;
- if( --p_data->refs > 0 ) return;
- pthread_mutex_destroy( &p_data->mutex );
- p_item = p_data->playlist;
- while( p_item ) {
- struct playlist_item* p_old = p_item;
- p_item = p_item->next;
- if( p_old->artist ) free( p_old->artist );
- if( p_old->title ) free( p_old->title );
- if( p_old->album ) free( p_old->album );
- if( p_old->url ) free( p_old->url );
- free( p_old );
- }
- free( p_data );
- }
- static FILTER_ACTION lfm_filter_request( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
- {
- char* p_buf = (char*)( *buf_p + offset );
- int n_buf = *length_p - offset;
- struct lfm_client_data* p_client;
- struct lfm_filter_data* p_data;
- if( p_buf == NULL || n_buf < 5 ) return CONTINUE;
- p_client = (struct lfm_client_data*)fc;
- if( p_client == NULL ) return CONTINUE;
- p_data = p_client->p_data;
- if( p_data == NULL ) return CONTINUE;
- pl->conf->filtermaxsize = 0;
- lfm_close_request( p_client );
- p_client->req_type = LFM_NONE;
- p_client->fp = NULL;
-
- if( strncasecmp( p_buf, "GET ", 4 ) != 0 ) return CONTINUE;
-
- p_buf += 4;
- if( strncasecmp( p_buf, "http://ws.audioscrobbler.com/radio/xspf.php?", 44 ) == 0 ) {
- myOutputDebugStringA1( "getting a playlist" );
- p_client->req_type = LFM_PLAYLIST;
- } else {
- char zzz[256];
- int i;
- struct playlist_item* p_item;
- for( i = 0; i < n_buf && i < 256; i++ ) {
- if( p_buf[i] == '\r' ) break;
- zzz[i] = p_buf[i];
- }
- zzz[i] = 0;
- myOutputDebugStringA1( zzz );
- pthread_mutex_lock( &p_data->mutex );
- p_item = p_data->playlist;
- while( p_item ) {
- if( strncasecmp( p_buf, p_item->url, p_item->url_len ) == 0 )
- break;
- p_item = p_item->next;
- }
- pthread_mutex_unlock( &p_data->mutex );
- if( p_item ) {
- myOutputDebugStringA1( "getting a known url: " );
- myOutputDebugStringA1( p_item->title );
- p_client->req_type = LFM_GET;
- p_client->pl_item = p_item;
- }
- }
- return CONTINUE;
- }
- static FILTER_ACTION lfm_filter_header_srv( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
- {
- char* p_buf = (char*)( *buf_p + offset );
- struct lfm_client_data* p_client;
- int n_buf = *length_p - offset;
- struct lfm_filter_data* p_data;
- char zzz[100];
- int code;
- if( p_buf == NULL || n_buf < 9 ) return CONTINUE;
- p_client = (struct lfm_client_data*)fc;
- if( p_client == NULL ) return CONTINUE;
- p_data = p_client->p_data;
- if( p_data == NULL ) return CONTINUE;
- code = atoi( p_buf + 9 );
- sprintf( zzz, "http code: %d", code );
- myOutputDebugStringA1( zzz );
- myOutputDebugStringA( p_buf, n_buf );
- if( p_client->req_type == LFM_GET && ( code > 300 && code < 304 || code == 307 ) ) {
- p_client->req_type = LFM_REDIR;
- do {
- char* p_line = p_buf;
- int n_line;
- for(; n_buf > 0 && *p_buf != '\r'; p_buf++, n_buf-- ) {};
- n_line = p_buf - p_line;
- if( n_line <= 0 ) break;
- if( n_line > 10 && strncasecmp( p_line, "location: ", 10 ) == 0 ) {
- myOutputDebugStringA1( "redir/location: " );
- myOutputDebugStringA( p_line + 10, n_line - 10 );
- if( p_client->pl_item ) {
- char* p_url = p_line + 10;
- int n_url = n_line - 10;
- pthread_mutex_lock( &p_data->mutex );
- if( p_client->pl_item->url ) free( p_client->pl_item->url );
- p_client->pl_item->url = xmalloc( char, n_url + 1 + 1 );
- memcpy( p_client->pl_item->url, p_url, n_url );
- p_client->pl_item->url[n_url] = ' ';
- p_client->pl_item->url[n_url+1] = 0;
- p_client->pl_item->url_len = n_url + 1;
- myOutputDebugStringA1( "got a url: " );
- myOutputDebugStringA1( p_client->pl_item->url );
- pthread_mutex_unlock( &p_data->mutex );
- }
- p_client->req_type = LFM_NONE;
- }
- for(; n_buf > 0 && ( *p_buf == '\n' || *p_buf == '\r' ); p_buf++, n_buf-- ) {};
- } while( n_buf > 0 );
- }
- if( code == 200 && p_client->req_type == LFM_GET ) {
- struct playlist_item* p_item = p_client->pl_item;
- char filename[512];
- int i = lfm_format_filename( p_item, filename, sizearr(filename) );
- /*
- strncpy( filename + i, PART_SUFFIX, sizearr(filename)-i );
- */
- p_client->fp = fopen( filename, "wb" );
- }
- else if( code == 200 && p_client->req_type == LFM_PLAYLIST ) {
- p_client->pl_xml = xcalloc( struct xml_state, 1 );
- }
- return CONTINUE;
- }
- void playlist_analyze( struct lfm_client_data* p_client, const char* p_buf, int len );
- static FILTER_ACTION lfm_filter_data_srv( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
- {
- char* p_buf = (char*)( *buf_p + offset );
- int n_buf = *length_p - offset;
- struct lfm_client_data* p_client;
- struct lfm_filter_data* p_data;
- myOutputDebugStringA1( "filter_data_srv" );
- myOutputDebugStringA( p_buf, n_buf );
- if( p_buf == NULL || n_buf < 1 ) return CONTINUE;
- p_client = (struct lfm_client_data*)fc;
- if( p_client == NULL ) return CONTINUE;
- p_data = p_client->p_data;
- if( p_data == NULL ) return CONTINUE;
- if( p_client->req_type == LFM_PLAYLIST )
- myOutputDebugStringA1( "filter_data_srv: playlist" );
- else if( p_client->req_type == LFM_GET ) {
- myOutputDebugStringA1( "filter_data_srv: retrieving" );
- if( p_client->fp == NULL )
- myOutputDebugStringA1( "but no file allocated" );
- }
- if( p_client->req_type == LFM_PLAYLIST )
- playlist_analyze( p_client, p_buf, n_buf );
- if( p_client->fp )
- fwrite( p_buf, n_buf, 1, p_client->fp );
- return CONTINUE;
- }
- enum XmlState
- {
- XML_TEXT,
- XML_TAGNAME,
- XML_ATTRNAME_W,
- XML_ATTRNAME,
- XML_ATTRVALUE_W,
- XML_ATTRVALUE,
- XML_TAGCLOSE,
- XML_TAGOPENCLOSE,
- };
- int xml_get_token_id( char** table, char* token )
- {
- int i;
- char* psz;
- for( psz = token; *psz; psz++ )
- *psz = tolower( *psz );
- for( i = 0; *table; i++, table++ )
- if( strcmp( *table, token ) == 0 ) return i;
- return -1;
- }
- enum {
- XT_PLAYLIST,
- XT_TRACK_LIST,
- XT_TRACK,
- XT_LOCATION,
- XT_CREATOR,
- XT_ALBUM,
- XT_TITLE
- };
- char* xt_list[] = {
- "playlist",
- "tracklist",
- "track",
- "location",
- "creator",
- "album",
- "title",
- NULL
- };
- void playlist_analyze( struct lfm_client_data* p_client, const char* p_buf, int len )
- {
- struct xml_state* xs = p_client->pl_xml;
- int n_buf = len;
- if( xs == NULL ) return;
- while( n_buf > 0 ) {
- enum {
- XMS_NONE,
- XMS_TAG,
- XMS_ATTR_NAME,
- XMS_ATTR_VALUE,
- XMS_TEXT
- } gotta = XMS_NONE;
- char ch = *p_buf++;
- n_buf--;
- lbl_retry:
- switch( xs->state ) {
- case XML_TAGNAME:
- if( ch == '>' ) {
- xs->state = XML_TEXT;
- gotta = XMS_TAG;
- break;
- } else if( ch == '/' ) {
- if( xs->n_token == 0 )
- xs->closing = 1;
- else {
- xs->state = XML_ATTRNAME_W;
- gotta = XMS_TAG;
- xs->n_token--;
- }
- break;
- } else if( isspace( ch ) ) {
- xs->state = XML_ATTRNAME_W;
- gotta = XMS_TAG;
- break;
- }
- if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
- break;
- case XML_ATTRNAME_W:
- if( ch == '>' ) {
- xs->state = XML_TEXT;
- break;
- } else if( ch == '/' ) {
- xs->closing = 1;
- xs->state = XML_TEXT;
- gotta = XMS_TAG;
- break;
- } else if( !isspace( ch ) ) {
- xs->state = XML_ATTRNAME;
- goto lbl_retry;
- }
- break;
- case XML_ATTRNAME:
- if( ch == '=' ) {
- xs->state = XML_ATTRVALUE_W;
- gotta = XMS_ATTR_NAME;
- break;
- }
- if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
- break;
- case XML_ATTRVALUE_W:
- if( ch == '"' || ch == '\'' ) {
- xs->sep = ch;
- xs->state = XML_ATTRVALUE;
- break;
- }
- break;
- case XML_ATTRVALUE:
- if( ch == xs->sep ) {
- xs->state = XML_ATTRNAME_W;
- gotta = XMS_ATTR_VALUE;
- break;
- }
- if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
- break;
- case XML_TEXT:
- if( ch == '<' ) {
- xs->state = XML_TAGNAME;
- gotta = XMS_TEXT;
- break;
- }
- if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
- break;
- }
- if( gotta != XMS_NONE ) {
- xs->p_token[xs->n_token] = 0;
- switch( gotta ) {
- case XMS_TAG: {
- int id = xml_get_token_id( xt_list, xs->p_token );
- if( !xs->closing ) {
- if( xs->level < sizeof(xs->tree) ) xs->tree[xs->level] = id;
- xs->level++;
- if( id == XT_TRACK && xs->level == 3 && xs->tree[1] == XT_TRACK_LIST )
- xs->p_item = xcalloc( struct playlist_item, 1 );
- } else {
- if( xs->level > 0 ) xs->level--;
- if( id == XT_TRACK && xs->level == 2 && xs->tree[1] == XT_TRACK_LIST ) {
- char zzz[1024];
- _snprintf( zzz, 1024, "artist: <%s>, title: <%s>, url: <%s>",
- xs->p_item->artist, xs->p_item->title, xs->p_item->url );
- myOutputDebugStringA1( zzz );
- pthread_mutex_lock( &p_client->p_data->mutex );
- xs->p_item->next = p_client->p_data->playlist;
- p_client->p_data->playlist = xs->p_item;
- pthread_mutex_unlock( &p_client->p_data->mutex );
- }
- xs->closing = 0;
- }
- break;
- }
- case XMS_ATTR_NAME:
- break;
- case XMS_ATTR_VALUE:
- break;
- case XMS_TEXT:
- if( xs->level == 4 && xs->tree[1] == XT_TRACK_LIST && xs->tree[2] == XT_TRACK ) {
- struct playlist_item* item = xs->p_item;
- if( item )
- switch( xs->tree[3] ) {
- case XT_LOCATION:
- item->url_len = xs->n_token;
- item->url = xmalloc( char, item->url_len + 1 + 1);
- memcpy( item->url, xs->p_token, item->url_len );
- item->url[item->url_len++] = ' ';
- item->url[item->url_len] = 0;
- break;
- case XT_CREATOR:
- item->artist = strdup( xs->p_token );
- break;
- case XT_ALBUM:
- item->album = strdup( xs->p_token );
- break;
- case XT_TITLE:
- item->title = strdup( xs->p_token );
- break;
- }
- }
- break;
- }
- xs->n_token = 0;
- }
- }
- }
- static struct filter lfm_filter = {
- NULL,
- "last.fm spy",
- NULL,
- lfm_filter_open,
- lfm_filter_client,
- lfm_filter_request,
- NULL,
- lfm_filter_header_srv,
- NULL,
- NULL,
- lfm_filter_data_srv,
- lfm_filter_clear,
- lfm_filter_close
- };
- static int h_lfm_folder( int argc, unsigned char **argv )
- {
- if( g_folder ) free( g_folder );
- g_folder = strdup( (char*)argv[1] );
- return 0;
- }
- static int h_lfm_format( int argc, unsigned char **argv )
- {
- if( g_format ) free( g_format );
- g_format = strdup( (char*)argv[1] );
- return 0;
- }
- static struct commands lfm_commandhandlers[] = {
- {lfm_commandhandlers+1, "lfm_folder", h_lfm_folder, 2, 2},
- {NULL, "lfm_format", h_lfm_format, 2, 2}
- };
- #ifdef __cplusplus
- extern "C" {
- #endif
- #ifdef WATCOM
- #pragma aux lfm_plugin "*" parm caller [ ] value struct float struct routine [eax] modify [eax ecx edx]
- #undef PLUGINCALL
- #define PLUGINCALL
- #endif
- PLUGINAPI int PLUGINCALL lfm_plugin( struct pluginlink * pluginlink, int argc, char** argv )
- {
- pl = pluginlink;
- myOutputDebugStringA1( "lfm_plugin" );
- if( !lfm_loaded ) {
- lfm_loaded = 1;
- lfm_filter.next = pl->conf->filters;
- pl->conf->filters = &lfm_filter;
- lfm_commandhandlers[1].next = pl->commandhandlers->next;
- pl->commandhandlers->next = lfm_commandhandlers;
- }
- return 0;
- }
- #ifdef __cplusplus
- }
- #endif
|