~ubuntu-branches/ubuntu/hoary/kdemultimedia/hoary

« back to all changes in this revision

Viewing changes to kscd/cddb.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Martin Schulze
  • Date: 2003-01-22 15:00:51 UTC
  • Revision ID: james.westby@ubuntu.com-20030122150051-uihwkdoxf15mi1tn
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 * kscd -- A simple CD player for the KDE project           
 
4
 *
 
5
 * $Id: cddb.cpp,v 1.40.2.1 2001/11/06 06:07:47 dfoerste Exp $
 
6
 * 
 
7
 * Copyright (C) 1997 Bernd Johannes Wuebben 
 
8
 * wuebben@math.cornell.edu
 
9
 *
 
10
 * 
 
11
 * This library is free software; you can redistribute it and/or
 
12
 * modify it under the terms of the GNU Library General Public
 
13
 * License as published by the Free Software Foundation; either
 
14
 * version 2 of the License, or (at your option) any later version.
 
15
 *
 
16
 * This library is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
19
 * Library General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU Library General Public
 
22
 * License along with this program; if not, write to the Free
 
23
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
24
 *
 
25
 */
 
26
 
 
27
#include <netdb.h>
 
28
#include <pwd.h>
 
29
#include <stdio.h>
 
30
#include <unistd.h>
 
31
#include <stdlib.h>
 
32
#include <errno.h>
 
33
#include <sys/types.h>
 
34
#include <sys/time.h>
 
35
#include <sys/utsname.h>
 
36
 
 
37
#include <qtextstream.h> 
 
38
#include <qfile.h>
 
39
#include <qdir.h>
 
40
#include <qfileinfo.h> 
 
41
#include <qregexp.h> 
 
42
 
 
43
#include <kapp.h>
 
44
#include <kdebug.h>
 
45
 
 
46
#include "cddb.h"
 
47
#include "version.h"
 
48
 
 
49
extern KApplication     *mykapp;
 
50
 
 
51
void cddb_decode(QString& str);
 
52
void cddb_encode(QString& str, QStringList &returnlist);
 
53
bool cddb_playlist_decode(QStringList& playlist,QString&  value);
 
54
void cddb_playlist_encode(QStringList& playlist,QString&  value);
 
55
 
 
56
int Ret;
 
57
extern char cddbbasedirtext[4096];
 
58
 
 
59
CDDB::CDDB(char *host, unsigned short int _port, unsigned short int _timeout)
 
60
{
 
61
    hostname   = host;
 
62
    port       = _port;
 
63
    connected  = false;
 
64
    readonly   = false;
 
65
    timeout    = _timeout;
 
66
    tempbuffer = "";
 
67
 
 
68
    sock = 0L;
 
69
    state = INIT;
 
70
 
 
71
    use_http_proxy=false;
 
72
    protocol_level=1;
 
73
    // for direct connections assuming CDDB protocol level 1
 
74
 
 
75
    // get current user/host name
 
76
    struct utsname uts;
 
77
 
 
78
    uname(&uts);
 
79
    domainname = uts.nodename;
 
80
    
 
81
    
 
82
    if(domainname.isEmpty())
 
83
        domainname = "somemachine.nowhere.org";
 
84
      
 
85
    pw = getpwuid(getuid());
 
86
    if (pw)
 
87
        username = pw->pw_name;
 
88
    else
 
89
        username = "anonymous";
 
90
//printf("cddb info: host[%s] port[%d] connected[%d] readonly[%d] timeout[%d]\n", host, port, connected, readonly, timeout);
 
91
//printf("attemping to connect to cddb...\n");
 
92
//fflush(stdout);
 
93
    //Connect handlers
 
94
    QObject::connect(&starttimer,SIGNAL(timeout()),this,SLOT(cddb_connect_internal()));
 
95
    QObject::connect(&timeouttimer,SIGNAL(timeout()),this,SLOT(cddb_timed_out_slot()));
 
96
 
 
97
}
 
98
 
 
99
CDDB::~CDDB()
 
100
{
 
101
    if(sock)
 
102
      {
 
103
        delete sock;
 
104
        sock = 0L;
 
105
      }
 
106
    timeouttimer.stop();
 
107
    starttimer.stop();
 
108
} // ~CDDB
 
109
 
 
110
void
 
111
CDDB::setTimeout(unsigned short int _timeout)
 
112
{
 
113
  timeout = _timeout;
 
114
} // setTimeout
 
115
 
 
116
unsigned short int 
 
117
CDDB::getTimeout( void )
 
118
{
 
119
  return timeout;
 
120
} // getTimeout
 
121
 
 
122
void 
 
123
CDDB::sighandler(int signum)
 
124
{
 
125
    signum = signum;
 
126
    
 
127
/*      if (signum == SIGALRM && connecting == true ){
 
128
      mykapp->processEvents();
 
129
      mykapp->flushX();
 
130
      signal( SIGALRM , CDDB::sighandler );
 
131
      setalarm();
 
132
*/      fprintf(stderr,"SIGALRM\n");
 
133
/*      }
 
134
    */
 
135
 
 
136
}
 
137
 
 
138
void 
 
139
CDDB::setalarm()
 
140
{
 
141
    struct itimerval  val1;
 
142
    struct timeval  tval1;
 
143
    struct timeval  tval2;
 
144
 
 
145
    tval1.tv_sec = 0;
 
146
    tval1.tv_usec = 100000; // 0.1 secs
 
147
    tval2.tv_sec = 0;
 
148
    tval2.tv_usec = 100000;
 
149
 
 
150
    val1.it_interval = tval1;
 
151
    val1.it_value    = tval2;
 
152
 
 
153
    setitimer(ITIMER_REAL, &val1, 0);
 
154
} // setalarm
 
155
 
 
156
void 
 
157
CDDB::cddbgetServerList(QString& _server)
 
158
{
 
159
    char ser   [CDDB_FIELD_BUFFER_LEN];
 
160
    char por   [CDDB_FIELD_BUFFER_LEN];
 
161
    char proto [CDDB_FIELD_BUFFER_LEN];
 
162
    char extra [CDDB_FIELD_BUFFER_LEN];
 
163
  
 
164
    sscanf(_server.ascii(),"%s %s %s %s",ser,proto,por,extra);
 
165
  
 
166
    hostname  = ser;
 
167
    port      = atoi(por);
 
168
    cgi       = extra;
 
169
 
 
170
    protocol=decodeTransport(proto);
 
171
 
 
172
    kdDebug() << "GETTING SERVERLIST\n" << endl;
 
173
 
 
174
    mode = SERVER_LIST_GET;
 
175
 
 
176
    if(protocol==CDDBHTTP)
 
177
      {
 
178
        cddb_connect_internal();
 
179
        if(connected)
 
180
          {
 
181
            QString cmd="sites";
 
182
            send_http_command(cmd);
 
183
            if(use_http_proxy)
 
184
              {
 
185
                saved_state=SERVER_LIST_WAIT;
 
186
                state=HTTP_REQUEST;
 
187
              } else {
 
188
                state = SERVER_LIST_WAIT;
 
189
              }
 
190
          }
 
191
      } else {
 
192
        starttimer.start(100,TRUE);
 
193
      }
 
194
} // cddbgetServerList
 
195
 
 
196
void 
 
197
CDDB::cddb_connect(QString& _server)
 
198
{
 
199
    char ser[CDDB_FIELD_BUFFER_LEN];
 
200
    char por[CDDB_FIELD_BUFFER_LEN];
 
201
    char proto[CDDB_FIELD_BUFFER_LEN];
 
202
    char extra[CDDB_FIELD_BUFFER_LEN];
 
203
  
 
204
    sscanf(_server.ascii(),"%s %s %s %s",ser,proto,por,extra);
 
205
  
 
206
    hostname  = ser;
 
207
    port      = atoi(por);
 
208
    cgi       = extra;
 
209
    protocol  = decodeTransport(proto);
 
210
  
 
211
    mode = REGULAR;
 
212
    if(protocol==CDDBP)
 
213
      {
 
214
        // --HERE--
 
215
        starttimer.start(100,TRUE);
 
216
      } else {
 
217
        emit cddb_ready();
 
218
      }
 
219
} // cddb_connect
 
220
 
 
221
void 
 
222
CDDB::cddb_connect_internal()
 
223
{
 
224
    starttimer.stop();
 
225
    timeouttimer.start(timeout*1000,TRUE);
 
226
 
 
227
        //    kdDebug() << "cddb_connect_internal_timeout = " << timeout*1000 << "\n" << endl;
 
228
 
 
229
    if(sock) 
 
230
      {
 
231
                delete sock;
 
232
                sock = 0L;
 
233
      }
 
234
 
 
235
    // signal( SIGALRM , CDDB::sighandler );
 
236
    // setalarm();
 
237
 
 
238
    fprintf(stderr, "proto = %d, proxy=%s\n", protocol, use_http_proxy?"true":"false");
 
239
    if(protocol==CDDBHTTP && use_http_proxy)
 
240
      {
 
241
        fprintf(stderr, "PROX\n");
 
242
                kdDebug() << "CONNECTING TO " << proxyhost << ":" << proxyport << " ....\n" << endl;
 
243
                sock = new KSocket(proxyhost.ascii(),proxyport, timeout);
 
244
                kdDebug() << "SOCKET SET" << endl;
 
245
      } else {
 
246
                kdDebug() << "CONNECTING TO " << hostname << ":" << port << " ....\n" << endl;
 
247
                sock = new KSocket(hostname.local8Bit(),port, timeout);
 
248
                kdDebug() << "SOCKET SET" << endl;
 
249
      }
 
250
    
 
251
    //signal( SIGALRM , SIG_DFL );
 
252
        
 
253
    if(sock == 0L || sock->socket() < 0)
 
254
      {
 
255
                timeouttimer.stop();
 
256
                
 
257
                kdDebug() << "CONNECT FAILED\n" << endl;
 
258
                
 
259
                if(mode == REGULAR )
 
260
                  emit cddb_failed();      
 
261
                else // mode == SERVER_LIST_GET
 
262
                  emit get_server_list_failed();
 
263
                
 
264
                connected = false;
 
265
                return;    
 
266
      }
 
267
    
 
268
    connected = true;
 
269
    respbuffer = "";
 
270
    
 
271
    connect(sock,SIGNAL(readEvent(KSocket*)),this,SLOT(cddb_read(KSocket*)));
 
272
    connect(sock,SIGNAL(closeEvent(KSocket*)),this,SLOT(cddb_close(KSocket*)));
 
273
    sock->enableRead(true);
 
274
    
 
275
    if(protocol==CDDBHTTP)
 
276
      {
 
277
                protocol_level=4;
 
278
                state=READY;
 
279
      } else {
 
280
                protocol_level=1;
 
281
                state = INIT;
 
282
      }
 
283
    
 
284
    kdDebug() << "CONNECTED\n" << endl;
 
285
} // cddb_connect_internal
 
286
 
 
287
void 
 
288
CDDB::send_http_command(QString &command)
 
289
{
 
290
    QString request;
 
291
    QString prt;
 
292
    QString prot;
 
293
    QString identification;
 
294
    
 
295
    prot.setNum(protocol_level);
 
296
    identification="&hello="+username+"+"+domainname+"+Kscd+"+KSCDVERSION+"&proto="+prot;
 
297
 
 
298
    prt.setNum(port);
 
299
    QString base  = "http://"+hostname+":"+prt;
 
300
 
 
301
    cddb_http_xlat(command);
 
302
 
 
303
    if(use_http_proxy)
 
304
        request="GET "+base+cgi+"?cmd="+command+identification+" HTTP/1.0\r\n\r\n";
 
305
    else
 
306
        request="GET "+cgi+"?cmd="+command+identification+"\r\n";
 
307
    
 
308
    kdDebug() << "Sending HTTP request: " << request << endl;
 
309
    
 
310
    write(sock->socket(),request.ascii(),request.length());
 
311
    timeouttimer.stop();
 
312
    timeouttimer.start(timeout*1000,TRUE);
 
313
} // send_http_command
 
314
 
 
315
void 
 
316
CDDB::cddb_timed_out_slot()
 
317
{
 
318
    timeouttimer.stop();
 
319
 
 
320
    if(sock) 
 
321
        sock->enableRead(false);
 
322
 
 
323
    if( mode == REGULAR )
 
324
          emit cddb_timed_out();
 
325
    else // mode == SERVER_LIST_GET
 
326
          emit get_server_list_failed();
 
327
        
 
328
    state = CDDB_TIMEDOUT;
 
329
    kdDebug() << "SOCKET CONNECTION TIMED OUT\n" << endl;
 
330
    cddb_close(sock);
 
331
} // cddb_timed_out_slot
 
332
 
 
333
// called externally if we want to close or interrupt the cddb connection
 
334
void 
 
335
CDDB::close_connection()
 
336
{
 
337
    if(sock)
 
338
    {
 
339
        cddb_close(sock);
 
340
        sock = 0L;
 
341
    }
 
342
} // close_connection
 
343
 
 
344
void 
 
345
CDDB::cddb_close(KSocket *socket)
 
346
{
 
347
    timeouttimer.stop();
 
348
    disconnect(socket,SIGNAL(readEvent(KSocket*)),this,SLOT(cddb_read(KSocket*)));
 
349
    disconnect(socket,SIGNAL(closeEvent(KSocket*)),this,SLOT(cddb_close(KSocket*)));
 
350
    socket->enableRead(false);
 
351
    kdDebug() << "SOCKET CONNECTION TERMINATED\n" << endl;
 
352
    connected = false;
 
353
    if(socket)
 
354
      {
 
355
        delete socket;
 
356
        socket = 0L;
 
357
        sock = 0L;
 
358
      }
 
359
} // cddb_close
 
360
 
 
361
#include <sys/types.h>
 
362
#include <sys/stat.h>
 
363
#include <fcntl.h>
 
364
#include <kstddirs.h>
 
365
#include <kglobal.h>
 
366
 
 
367
void 
 
368
CDDB::cddb_read(KSocket *socket)
 
369
{
 
370
    int  n;
 
371
    char buffer[CDDB_READ_BUFFER_LEN];
 
372
    
 
373
    if(socket == 0L || socket->socket() < 0)
 
374
        return;
 
375
 
 
376
    memset(buffer,0,CDDB_READ_BUFFER_LEN);
 
377
    n = read(socket->socket(), buffer, CDDB_READ_BUFFER_LEN-1 );
 
378
    buffer[n] = '\0';
 
379
    tempbuffer += buffer;
 
380
 
 
381
//    kdDebug() << "BUFFER: '" << buffer << "'" << endl;
 
382
 
 
383
    while(next_token())
 
384
        do_state_machine();
 
385
} // cddb_read
 
386
 
 
387
bool 
 
388
CDDB::next_token()
 
389
{
 
390
    int newlinepos = tempbuffer.find('\n');
 
391
    if(newlinepos != -1)
 
392
      {
 
393
        lastline    = tempbuffer.left(newlinepos);
 
394
        tempbuffer  = tempbuffer.right(tempbuffer.length() - newlinepos -1);
 
395
        return true;
 
396
      } else {
 
397
        return false;
 
398
      }
 
399
} // next_token
 
400
 
 
401
void 
 
402
CDDB::queryCD(unsigned long _magicID,QStringList& querylist)
 
403
{
 
404
//    if(state == DO_NOTHING)
 
405
//        return;
 
406
//    state = DO_NOTHING;
 
407
    if((sock == 0L || sock->socket() < 0) && protocol==CDDBP)
 
408
        return;
 
409
 
 
410
    QString str;
 
411
    title = "";
 
412
    category = "";
 
413
    magicID = _magicID;
 
414
 
 
415
    str = str.sprintf("cddb query %08lx %u ",magicID,querylist.count()-1);
 
416
    str += querylist.join(" ") + ' ';
 
417
 
 
418
    if(protocol==CDDBHTTP)
 
419
      {
 
420
        cddb_connect_internal();
 
421
        if(connected)
 
422
          {
 
423
            QString param = str;
 
424
            send_http_command(param);
 
425
            if(use_http_proxy)
 
426
              {
 
427
                saved_state = QUERY;
 
428
                state       = HTTP_REQUEST;
 
429
              } else {
 
430
                state  = QUERY;
 
431
              }
 
432
          }
 
433
      } else {
 
434
        // CDDB
 
435
        timeouttimer.stop();
 
436
        timeouttimer.start(timeout*1000,TRUE);
 
437
        str += "\n";
 
438
        kdDebug() << "strdata: " << str << "\n" << endl;
 
439
        write(sock->socket(),str.ascii(),str.length());
 
440
        state  = QUERY;
 
441
      }
 
442
} // queryCD
 
443
 
 
444
void 
 
445
CDDB::query_exact(QString line)
 
446
{
 
447
  int category_start = 0;
 
448
  int category_end       = 0;
 
449
  int magic_start        = 0;
 
450
  int magic_end          = 0;
 
451
  
 
452
  QString magicstr;
 
453
  
 
454
  category_start = line.find(" ",0,true) + 1;
 
455
  category_end = line.find(" ",category_start,true);
 
456
  category = line.mid(category_start,category_end-category_start);
 
457
  
 
458
  magic_start = category_end + 1;
 
459
  magic_end = line.find(" ",magic_start,true);
 
460
  magicstr = line.mid( magic_start, magic_end - magic_start);
 
461
  
 
462
  title = line.mid(magic_end + 1,line.length());
 
463
  
 
464
  QString readstring;
 
465
  if((sock == 0L || sock ->socket() < 0) && protocol==CDDBP)
 
466
    {
 
467
      kdDebug() << "sock = 0L!!!\n" << endl;
 
468
      return;
 
469
    }
 
470
  
 
471
  if(protocol==CDDBHTTP)
 
472
    {
 
473
      cddb_connect_internal();
 
474
      if(connected)
 
475
        {
 
476
          readstring = QString::fromLatin1("cddb read %1 %2")
 
477
            .arg(category).arg(magicstr);
 
478
          send_http_command(readstring);
 
479
        }
 
480
    } else {
 
481
      // CDDB
 
482
      timeouttimer.stop();
 
483
      timeouttimer.start(timeout*1000,TRUE);
 
484
      //  readstring.sprintf("cddb read %s %lx \n",category.data(),magicID);
 
485
      readstring = QString::fromLatin1("cddb read %1 %2 \n")
 
486
        .arg(category)
 
487
        .arg(magicstr);
 
488
      write(sock->socket(),readstring.ascii(),readstring.length());
 
489
    }
 
490
  
 
491
  state = CDDB_READ;
 
492
  respbuffer = "";
 
493
  sock->enableRead(true);
 
494
} // query_exact
 
495
 
 
496
 
 
497
void 
 
498
CDDB::do_state_machine()
 
499
{
 
500
  static int cddbfh = 0;
 
501
  int cddblinelen;
 
502
  
 
503
  
 
504
  kdDebug() << "STATE MACHINE: State: " << (int)state << " Got: " << lastline << "\n" << endl;
 
505
  
 
506
  switch (state)
 
507
        {
 
508
        case HTTP_HEADER:
 
509
          
 
510
          if(lastline.stripWhiteSpace()==QString(""))
 
511
                {
 
512
                  state=saved_state;
 
513
                  kdDebug() << "HTTP Header is done. Moving on.\n" << endl;
 
514
                }
 
515
          break;
 
516
          
 
517
        case HTTP_REQUEST:
 
518
          //Parse responce and check numeric code.
 
519
        char proto [CDDB_FIELD_BUFFER_LEN];
 
520
        char code  [CDDB_FIELD_BUFFER_LEN];
 
521
        sscanf(lastline.ascii(),"%s %s",proto,code);
 
522
        if(strcmp(code,"200")==0)
 
523
          {
 
524
                if(use_http_proxy)
 
525
              {
 
526
                        state=HTTP_HEADER;
 
527
                        kdDebug() << "HTTP request is OK. Reading HTTP header.\n" << endl;
 
528
              } else {
 
529
                        state=saved_state;
 
530
                        kdDebug() << "HTTP request is OK. Mooving on.\n" << endl;
 
531
              }
 
532
          } else {
 
533
            kdDebug() << "HTTP error: " << lastline << "\n" << endl;
 
534
            if(saved_state==SERVER_LIST_WAIT)
 
535
              {
 
536
                        emit get_server_list_failed();
 
537
              }
 
538
            state=CDDB_DONE; //TODO: some error state
 
539
          }
 
540
        break;
 
541
        
 
542
        case INIT:
 
543
          kdDebug() << "case INIT == true\n" << endl;
 
544
          if((lastline.left(3) == QString("201")) ||(lastline.left(3) == QString("200")) )
 
545
                {
 
546
                  kdDebug() << "next if == true\n" << endl;
 
547
                  QString hellostr;
 
548
                  
 
549
                  // cddb hello username hostname clientname version
 
550
                  hellostr = QString("cddb hello %1 %2 Kscd %3\n")
 
551
                        .arg(username)
 
552
                        .arg(domainname)
 
553
                        .arg(KSCDVERSION);
 
554
                  kdDebug() << "hellostr: " << hellostr << "\n" << endl;
 
555
                  
 
556
                  Ret = write(sock->socket(),hellostr.ascii(),hellostr.length());
 
557
                  kdDebug() << "write() returned: " << Ret << " [" << strerror(errno) << "]\n" << endl;
 
558
                  state = HELLO;
 
559
                } else {
 
560
                  state = ERROR_INIT;   
 
561
                  cddb_close(sock);
 
562
                  kdDebug() << "ERROR_INIT\n" << endl;
 
563
                  emit cddb_failed();
 
564
                }
 
565
          
 
566
          respbuffer = "";
 
567
          break;
 
568
          
 
569
        case HELLO:
 
570
          if(lastline.left(3) == QString("200"))
 
571
                {
 
572
                  // Negotiate protocol level
 
573
                  state = PROTO;
 
574
                  // Let's try to request protocol level 3
 
575
                  // so we'll get list of servers with protocol.
 
576
                  write(sock->socket(),"proto 3\n",8); 
 
577
                } else {
 
578
                  state = ERROR_HELLO;
 
579
                  cddb_close(sock);
 
580
                  kdDebug() << "ERROR_HELLO\n" << endl;
 
581
                  emit cddb_failed();
 
582
                }
 
583
          
 
584
          respbuffer = "";
 
585
          break;
 
586
          
 
587
        case PROTO:
 
588
          if(lastline.left(3) == QString("201"))
 
589
                protocol_level=3;
 
590
          else
 
591
                protocol_level=1;
 
592
          
 
593
          state = READY;
 
594
          if(mode == REGULAR)
 
595
                {
 
596
                  emit cddb_ready();
 
597
                } else {
 
598
                  write(sock->socket(),"sites\n",6);
 
599
                  state = SERVER_LIST_WAIT;
 
600
                }
 
601
          break;
 
602
          
 
603
        case QUERY:
 
604
          if(lastline.left(3) == QString("200"))
 
605
                {
 
606
                  query_exact(lastline);
 
607
                } else {
 
608
                  if(lastline.left(3) == QString("211")) // single or multiple inexact
 
609
                        {
 
610
                          inexact_list.clear();
 
611
                          state = INEX_READ;
 
612
                        } else {
 
613
                          if(lastline.left(3) == QString("202"))
 
614
                                {
 
615
                                  state = CDDB_DONE;
 
616
                                  
 
617
                                  cddb_close(sock);
 
618
                                  emit cddb_no_info();
 
619
                                } else {
 
620
                                  if(lastline.left(3) == QString("210")) // multiple exact
 
621
                                        {
 
622
                                          inexact_list.clear();
 
623
                                          state = MULTEX_READ;
 
624
                                        } else {
 
625
                                          state = ERROR_QUERY;
 
626
                                          cddb_close(sock);
 
627
                                          kdDebug() << "ERROR_QUERY\n" << endl;
 
628
                                          emit cddb_failed();
 
629
                                        }
 
630
                                }
 
631
                        }
 
632
                }
 
633
          break;
 
634
          
 
635
        case INEX_READ:
 
636
          if(lastline.at(0) == '.')
 
637
                {
 
638
                  state = CDDB_DONE;
 
639
                  timeouttimer.stop();
 
640
                  emit cddb_inexact_read();
 
641
                } else {
 
642
                  inexact_list.append(lastline);
 
643
                }
 
644
          break;
 
645
 
 
646
        case MULTEX_READ:
 
647
          if(lastline.at(0) == '.')
 
648
                {
 
649
                  state = CDDB_DONE;
 
650
                  timeouttimer.stop();
 
651
                  emit cddb_inexact_read();
 
652
                } else {
 
653
                  inexact_list.append(lastline);
 
654
                }
 
655
          break;
 
656
          
 
657
        case CDDB_READING:
 
658
          if(lastline.at(0) == '.')
 
659
                {
 
660
                  close(cddbfh);
 
661
                  cddbfh = 0;
 
662
                  if(protocol!=CDDBHTTP)
 
663
                        write(sock->socket(),"quit\n",6);
 
664
                  state = CDDB_DONE;
 
665
                  
 
666
                  cddb_close(sock);
 
667
                  emit cddb_done();
 
668
                } else {
 
669
                  if(!cddbfh)
 
670
                        {
 
671
                          QString file;
 
672
                          file.sprintf("%s/%08lx", category.utf8().data(), magicID);
 
673
                          file = locate("cddb", file);
 
674
                          
 
675
                          kdDebug() << "dir/file path: " << file << "\n" << endl;
 
676
                          cddbfh = open(QFile::encodeName(file), O_CREAT|O_WRONLY|O_TRUNC,
 
677
                                                        S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 
678
                        }
 
679
                  cddblinelen = lastline.length();
 
680
                  write(cddbfh, lastline.ascii(), cddblinelen);
 
681
                  write(cddbfh, "\n", strlen("\n"));
 
682
                  //            kdDebug() << "line written: " << cddblinelen << "\n" << endl;
 
683
                  
 
684
                  respbuffer.prepend("\n");
 
685
                  respbuffer.prepend(lastline);
 
686
                }
 
687
          break;
 
688
          
 
689
        case CDDB_READ:
 
690
          
 
691
          if(lastline.at(0) == '4')
 
692
                {
 
693
                  state = ERROR_CDDB_READ;
 
694
                  kdDebug() << "ERROR_CDDB_READ\n" << endl;
 
695
                  cddb_close(sock);
 
696
                  emit cddb_failed();
 
697
                } else {
 
698
                  respbuffer="";
 
699
                  state = CDDB_READING;
 
700
                }
 
701
          break;
 
702
          
 
703
        case SERVER_LIST_WAIT:
 
704
          
 
705
          if(lastline.left(3) == QString("210"))
 
706
                {
 
707
                  serverlist.clear();
 
708
                  state=GETTING_SERVER_LIST;
 
709
                } else {
 
710
                  state=CDDB_DONE;
 
711
                  emit get_server_list_failed();
 
712
                }
 
713
          break;
 
714
          
 
715
        case GETTING_SERVER_LIST:
 
716
          if(lastline.at(0) == '.')
 
717
                {
 
718
                  kdDebug() << "GOT SERVERLIST\n" << endl;
 
719
                  if(protocol!=CDDBHTTP)
 
720
                        write(sock->socket(),"quit\n",6);
 
721
                  cddb_close(sock);
 
722
                  emit get_server_list_done();
 
723
                  state = CDDB_DONE;
 
724
                } else {
 
725
                  parse_serverlist_entry();
 
726
                }
 
727
          break;
 
728
          
 
729
        default:
 
730
          break;
 
731
        }
 
732
  lastline="";
 
733
} // do_state_machine
 
734
 
 
735
 
 
736
void 
 
737
CDDB::serverList(QStringList& list)
 
738
{
 
739
  list = serverlist;
 
740
}
 
741
 
 
742
void
 
743
CDDB::parse_serverlist_entry()
 
744
{
 
745
    char serv  [CDDB_FIELD_BUFFER_LEN];
 
746
    char po    [CDDB_FIELD_BUFFER_LEN];
 
747
    char proto [CDDB_FIELD_BUFFER_LEN];
 
748
    char extra [CDDB_FIELD_BUFFER_LEN];
 
749
  
 
750
    QCString tempstr;
 
751
 
 
752
    if(protocol_level<3)
 
753
      {
 
754
        sscanf(lastline.ascii(),"%s %s",serv,po);
 
755
        tempstr = tempstr.sprintf("%s cddbp %s -",serv,po);
 
756
        serverlist.append(tempstr);
 
757
      } else {
 
758
        sscanf(lastline.ascii(),"%s %s %s %s",serv,proto,po,extra);
 
759
        tempstr = tempstr.sprintf("%s %s %s %s",serv,proto,po,extra);
 
760
        //         transport tr=decodeTransport(proto);
 
761
        //         if(tr==CDDBP || tr==CDDBHTTP)
 
762
        serverlist.append(tempstr);
 
763
      }
 
764
} // parse_serverlist_entry
 
765
 
 
766
void 
 
767
CDDB::get_inexact_list(QStringList& p_inexact_list)
 
768
{
 
769
    p_inexact_list=inexact_list;
 
770
} // get_inexact_list
 
771
 
 
772
bool 
 
773
CDDB::local_query(
 
774
                  unsigned  long magicID,
 
775
                  QString&  data,
 
776
                  QStringList& titlelist,
 
777
                  QStringList& extlist,
 
778
                  QString&  _category,
 
779
                  QStringList& discidlist,
 
780
                  int&   revision,
 
781
                  QStringList& playlist
 
782
                  )
 
783
{
 
784
  
 
785
  // pre-set revision to 0
 
786
  revision = 0;
 
787
  QStringList pathlist = KGlobal::dirs()->resourceDirs("cddb");
 
788
  
 
789
  if(pathlist.count() == 0)
 
790
    return false;
 
791
  
 
792
  QDir d;
 
793
  
 
794
  for(QStringList::ConstIterator it = pathlist.begin() ; it != pathlist.end(); it++)
 
795
    { 
 
796
      d.setPath(*it);
 
797
      d.setFilter( QDir::Dirs);
 
798
      d.setSorting( QDir::Size);
 
799
      
 
800
      QStringList list = d.entryList();
 
801
      QStringList::Iterator it2;    
 
802
      
 
803
      for(it2 = list.begin(); it2 != list.end(); it2++)
 
804
        {
 
805
          if (*it2 != "." && *it2 != "..")
 
806
            {
 
807
              if(checkDir(magicID, *it + *it2))
 
808
                {
 
809
                  category = *it2;
 
810
                  
 
811
                  getData(data,titlelist,extlist,_category,discidlist,revision,playlist);
 
812
                  return true;
 
813
                }
 
814
            }
 
815
        }
 
816
    }
 
817
  return false;
 
818
} // local_query
 
819
 
 
820
bool 
 
821
CDDB::checkDir(unsigned long magicID, const QString& dir)
 
822
{
 
823
    QString mag;
 
824
    mag.sprintf("%s/%08lx",dir.utf8().data(),magicID);
 
825
 
 
826
    QFileInfo info(mag);
 
827
    if(!info.isReadable())
 
828
        return false;
 
829
 
 
830
    respbuffer = "";
 
831
 
 
832
    QFile file(mag);
 
833
 
 
834
    if( !file.open( IO_ReadOnly )) 
 
835
      {
 
836
        return false;
 
837
      }
 
838
    
 
839
    QTextStream t(&file);
 
840
    
 
841
    while ( !t.eof() ) 
 
842
      {
 
843
        QString s = t.readLine() + "\n";
 
844
        // The following swallowed the last line of the file
 
845
        // no matter if it's just a newline or a whole entry.
 
846
        //      if(!t.eof())    
 
847
          respbuffer += s;
 
848
      }
 
849
 
 
850
    file.close();
 
851
    return true;
 
852
} // checkDir
 
853
 
 
854
// scan the relevant parts of the cddba database entry in the the provied structures
 
855
void 
 
856
CDDB::getData(
 
857
              QString& data,
 
858
              QStringList& titles, 
 
859
              QStringList& extlist,
 
860
              QString& categ,
 
861
              QStringList& discidlist, 
 
862
              int& revision,
 
863
              QStringList& playlist
 
864
              )
 
865
{
 
866
  data = "";
 
867
  titles.clear();
 
868
  extlist.clear();
 
869
  discidlist.clear();
 
870
  categ      = category;
 
871
  data       = respbuffer;
 
872
    
 
873
  
 
874
    int pos1,pos2,pos3,pos4 = 0;
 
875
 
 
876
    int revtmp = data.find("Revision:",0,true);
 
877
    if(revtmp == -1)
 
878
      {
 
879
        revision = 0;  // no Revision -> we are first (ZERO)!
 
880
      } else {
 
881
        // Well, I'll see if there's a simpler way for this.
 
882
        QString revstr;
 
883
        int revtmp2;
 
884
        revtmp2 = data.find("\n",revtmp,true);
 
885
        if(revtmp2 - revtmp - 9 >=0)
 
886
          revstr = data.mid(revtmp +9,revtmp2 - revtmp -9);
 
887
        revstr.stripWhiteSpace();
 
888
        bool ok;
 
889
        revision = revstr.toInt(&ok);
 
890
        if(!ok) // Bogus Revision -> we claim to be first (ZERO)!
 
891
          revision = 0;
 
892
      }
 
893
    
 
894
    // lets get all DISCID's in the data. Remeber there can be many DISCID's on
 
895
    // several lines separated by commas on each line
 
896
    //
 
897
    // DISCID= 47842934,4h48393,47839492
 
898
    // DISCID= 47fd2934,4h48343,47839492,43879074
 
899
    
 
900
    while((pos3 = data.find("DISCID=",pos4,true))!= -1)
 
901
      {
 
902
        pos1 = pos3;
 
903
        pos2 = data.find("\n",pos1,true);
 
904
        
 
905
        QString discidtemp;
 
906
        QString temp3;
 
907
        
 
908
        if( pos2 - pos1 -7 >= 0)
 
909
          {
 
910
            discidtemp = data.mid(pos1 + 7,pos2- pos1 -7);
 
911
          } else {
 
912
            kdDebug() << "ANOMALY 1\n" << endl;
 
913
          }
 
914
        
 
915
        kdDebug() << "DISCDID " << discidtemp << "\n" << endl;
 
916
        
 
917
        pos1 = 0;
 
918
        while((pos2 = discidtemp.find(",",pos1,true)) != -1)
 
919
          {
 
920
            if( pos2 - pos1 >= 0)
 
921
              {
 
922
                temp3 = discidtemp.mid(pos1,pos2-pos1);
 
923
              } else {
 
924
                kdDebug() << "ANOMALY 2\n" << endl;
 
925
              }
 
926
            
 
927
            temp3 = temp3.stripWhiteSpace();
 
928
            
 
929
            if(!temp3.isEmpty())
 
930
              discidlist.append(temp3);
 
931
            pos1 = pos2 + 1;
 
932
          }
 
933
 
 
934
        temp3 = discidtemp.mid(pos1,discidtemp.length());
 
935
        temp3.stripWhiteSpace();
 
936
        
 
937
        if(!temp3.isEmpty())
 
938
          {
 
939
            discidlist.append(temp3);
 
940
          }
 
941
        pos4 = pos3 + 1;
 
942
      }// end get DISCID's
 
943
 
 
944
    kdDebug() << "FOUND " << discidlist.count() << " DISCID's\n" << endl;
 
945
 
 
946
  // Get the DTITLE
 
947
 
 
948
    QString value;
 
949
    QString key;
 
950
    key = "DTITLE=";
 
951
 
 
952
    getValue(key,value,data);
 
953
    titles.append(value);
 
954
 
 
955
 
 
956
    int counter = 0;
 
957
    key = key.sprintf("TTITLE%d=",counter);
 
958
    while(getValue(key,value,data))
 
959
      {
 
960
        titles.append(value);
 
961
        key = key.sprintf("TTITLE%d=",++counter);
 
962
      }
 
963
    
 
964
    key = "EXTD=";
 
965
    getValue(key,value,data);
 
966
    extlist.append(value);
 
967
    
 
968
    counter = 0;
 
969
    key = key.sprintf("EXTT%d=",counter);
 
970
    while(getValue(key,value,data))
 
971
      {
 
972
        extlist.append(value);
 
973
        key = key.sprintf("EXTT%d=",++counter);
 
974
      }
 
975
    
 
976
    key = "PLAYORDER=";
 
977
    getValue(key,value,data);
 
978
    cddb_playlist_decode(playlist, value);
 
979
} // getData
 
980
 
 
981
QString 
 
982
CDDB::getCategoryFromPathName(const QString& pathname){
 
983
  
 
984
    QString path = pathname;
 
985
    path = path.stripWhiteSpace();
 
986
 
 
987
    while(path.right(1).at(1) == '/'){
 
988
        path = path.left(path.length() - 1);
 
989
    }
 
990
 
 
991
    int pos = 0;
 
992
    pos  = path.findRev("/",-1,true);
 
993
    if(pos == -1)
 
994
        return path;
 
995
    else
 
996
        return path.mid(pos+1,path.length());
 
997
 
 
998
} // getCategoryFromPathName
 
999
 
 
1000
bool 
 
1001
CDDB::getValue(QString& key,QString& value, QString& data)
 
1002
{
 
1003
 
 
1004
    bool found_one = false;
 
1005
    int pos1 = 0;
 
1006
    int pos2 = 0;
 
1007
 
 
1008
    value = "";
 
1009
 
 
1010
    while((  pos1 = data.find(key,pos1,true)) != -1)
 
1011
      {
 
1012
        found_one = true;
 
1013
        pos2 = data.find("\n",pos1,true);
 
1014
        if( (pos2 - pos1 - (int)key.length()) >= 0)
 
1015
          {
 
1016
            value += data.mid(pos1 + key.length(), pos2 - pos1 - key.length());
 
1017
          } else {
 
1018
            kdDebug() << "GET VALUE ANOMALY 1\n" << endl;
 
1019
          }
 
1020
        pos1 = pos1 + 1;
 
1021
      }
 
1022
 
 
1023
    if(value.isNull())
 
1024
        value = "";
 
1025
 
 
1026
    cddb_decode(value);
 
1027
    return found_one;
 
1028
} // getValue
 
1029
 
 
1030
void 
 
1031
cddb_playlist_encode(QStringList& list,QString& playstr)
 
1032
{
 
1033
  playstr = list.join(",");
 
1034
} // cddb_playlist_encode
 
1035
 
 
1036
 
 
1037
bool 
 
1038
cddb_playlist_decode(QStringList& list, QString& str)
 
1039
 
1040
  bool isok = true;
 
1041
  int pos1, pos2;
 
1042
  pos1 = 0;
 
1043
  pos2 = 0;
 
1044
  
 
1045
  list.clear();
 
1046
 
 
1047
  list = QStringList::split(',', str);
 
1048
 
 
1049
    QString check;
 
1050
    bool        ok1;
 
1051
    int   num;
 
1052
 
 
1053
    for ( QStringList::Iterator it = list.begin();
 
1054
          it != list.end();
 
1055
          ++it )
 
1056
      {
 
1057
        check = *it;
 
1058
        check = check.stripWhiteSpace();
 
1059
 
 
1060
        if (check.isEmpty())
 
1061
          {
 
1062
            it = list.remove(it);
 
1063
            continue;
 
1064
          }
 
1065
        
 
1066
        num = check.toInt(&ok1);
 
1067
        if(!ok1 || num < 1)
 
1068
          {
 
1069
            it = list.remove(it);
 
1070
            isok = false;
 
1071
            continue;
 
1072
          }
 
1073
    
 
1074
        *it = check;
 
1075
      }
 
1076
 
 
1077
    /*  for(uint i = 0; i < list.count(); i++){
 
1078
        printf("playlist %d=%s\n",i,list.at(i));
 
1079
        }*/
 
1080
    return isok;
 
1081
} // cddb_playlist_decode
 
1082
 
 
1083
void 
 
1084
cddb_decode(QString& str)
 
1085
{
 
1086
    int pos1 = 0;
 
1087
    int pos2 = 0;
 
1088
 
 
1089
    while((pos2 = str.find("\\n",pos1,true)) !=-1  )
 
1090
      {
 
1091
        if(pos2>0)
 
1092
          {
 
1093
            if(str.mid(pos2-1,3) == QString("\\\\n"))
 
1094
              {
 
1095
                pos1 = pos2 + 2;
 
1096
                continue;
 
1097
              }
 
1098
          }
 
1099
        str.replace(pos2 , 2 , "\n");
 
1100
        pos1 = pos2 + 1;
 
1101
      }
 
1102
 
 
1103
    pos1 = 0;
 
1104
    pos2 = 0;
 
1105
 
 
1106
    while((pos2 = str.find("\\t",pos1,true)) !=-1)
 
1107
      {
 
1108
        if( pos2 > 0 )
 
1109
          {
 
1110
            if(str.mid(pos2-1,3) == QString("\\\\t"))
 
1111
              {
 
1112
                pos1 = pos2 + 2;
 
1113
                continue;
 
1114
              }
 
1115
          }
 
1116
        str.replace(pos2 , 2 , "\t");
 
1117
        pos1 = pos2 + 1;
 
1118
      }
 
1119
    
 
1120
    pos1 = 0;
 
1121
    pos2 = 0;
 
1122
    
 
1123
    while((pos2 = str.find("\\\\",pos1,true)) !=-1)
 
1124
      {
 
1125
        str.replace(pos2 , 2 , "\\");
 
1126
        pos1 = pos2 + 1;
 
1127
      }
 
1128
} // cddb_decode
 
1129
 
 
1130
void 
 
1131
cddb_encode(QString& str, QStringList &returnlist)
 
1132
{
 
1133
    returnlist.clear();
 
1134
    
 
1135
    int pos1 = 0;
 
1136
    int pos2 = 0;
 
1137
    
 
1138
    while((pos2 = str.find("\\",pos1,true)) !=-1)
 
1139
      {
 
1140
        str.replace(pos2 , 1 , "\\\\");
 
1141
        pos1 = pos2 + 2;
 
1142
      }
 
1143
 
 
1144
    pos1 = 0;
 
1145
    pos2 = 0;
 
1146
 
 
1147
    while((pos2 = str.find("\n",pos1,true)) !=-1)
 
1148
      {
 
1149
        str.replace(pos2 , 1 , "\\n");
 
1150
        pos1 = pos2 + 2;
 
1151
      }
 
1152
 
 
1153
    pos1 = 0;
 
1154
    pos2 = 0;
 
1155
 
 
1156
    while((pos2 = str.find("\t",pos1,true)) !=-1)
 
1157
      {
 
1158
        str.replace(pos2 , 1 , "\\t");
 
1159
        pos1 = pos2 + 2;
 
1160
      }
 
1161
 
 
1162
    while(str.length() > 70)
 
1163
      {
 
1164
        returnlist.append(str.left(70));
 
1165
        str = str.mid(70,str.length());
 
1166
      }
 
1167
 
 
1168
    returnlist.append(str);
 
1169
} // cddb_encode
 
1170
 
 
1171
// This function converts server list entry from "Server Port" format
 
1172
// To "Server Protocol Port Address".
 
1173
//     The fields are as follows:
 
1174
//         site:
 
1175
//             The Internet address of the remote site.
 
1176
//         protocol:
 
1177
//             The transfer protocol used to access the site.
 
1178
//         port:
 
1179
//             The port at which the server resides on that site.
 
1180
//         address:
 
1181
//             Any additional addressing information needed to access the
 
1182
//             server. For example, for HTTP protocol servers, this would be
 
1183
//             the path to the CDDB server CGI script. This field will be
 
1184
//             "-" if no additional addressing information is needed.
 
1185
//
 
1186
// Returns 'true' if format have been converted.
 
1187
bool 
 
1188
CDDB::normalize_server_list_entry(QString &entry)
 
1189
{
 
1190
    char serv [CDDB_FIELD_BUFFER_LEN];
 
1191
    char proto[CDDB_FIELD_BUFFER_LEN];
 
1192
    char po   [CDDB_FIELD_BUFFER_LEN];
 
1193
    char extra[CDDB_FIELD_BUFFER_LEN];
 
1194
    
 
1195
    if(sscanf(entry.ascii(),"%s %s %s %s",serv,proto,po,extra)==2) 
 
1196
      {
 
1197
        // old format
 
1198
        sprintf(extra,"%s cddbp %s -",serv, proto);
 
1199
        entry=extra;
 
1200
        return true;
 
1201
      } else {
 
1202
        // Otherwise let us leave the item unchanged.
 
1203
        return false;
 
1204
      }
 
1205
} // normalize_server_list_entry
 
1206
 
 
1207
void 
 
1208
CDDB::setHTTPProxy(QString host, unsigned short int port)
 
1209
{
 
1210
  proxyhost=host;
 
1211
  proxyport=port;
 
1212
} // setHTTPProxy
 
1213
 
 
1214
void 
 
1215
CDDB::useHTTPProxy(bool flag)
 
1216
{
 
1217
    use_http_proxy=flag;
 
1218
} // useHTTPProxy(bool)
 
1219
 
 
1220
bool
 
1221
CDDB::useHTTPProxy()
 
1222
{
 
1223
    return use_http_proxy;
 
1224
} // useHTTPProxy
 
1225
 
 
1226
unsigned short 
 
1227
int CDDB::getHTTPProxyPort()
 
1228
{
 
1229
    return proxyport;
 
1230
} // getHTTPProxyPort
 
1231
 
 
1232
QString 
 
1233
CDDB::getHTTPProxyHost()
 
1234
{
 
1235
    return proxyhost;
 
1236
} // getHTTPProxyHost
 
1237
 
 
1238
CDDB::transport 
 
1239
CDDB::decodeTransport(const char *proto)
 
1240
{
 
1241
    if(strcasecmp(proto,"cddbp")==0)
 
1242
        return CDDBP;
 
1243
    else
 
1244
        if(strcasecmp(proto,"http")==0)
 
1245
            return CDDBHTTP;
 
1246
        else
 
1247
            if(strcasecmp(proto,"smtp")==0)
 
1248
                return SMTP;
 
1249
            else
 
1250
                return UNKNOWN;
 
1251
} // decodeTransport
 
1252
 
 
1253
void 
 
1254
CDDB::cddb_http_xlat(QString &s)
 
1255
{
 
1256
    char q[6];
 
1257
 
 
1258
    if(s.isEmpty())
 
1259
        return;
 
1260
 
 
1261
    unsigned int pos=0;
 
1262
    while(pos < s.length()+1)
 
1263
      {
 
1264
        switch (s[pos].latin1()) 
 
1265
          {
 
1266
          case ' ':
 
1267
            s[pos]='+';
 
1268
            pos++;
 
1269
            break;
 
1270
          case '?':
 
1271
          case '=':
 
1272
          case '+':
 
1273
          case '&':
 
1274
          case '%':
 
1275
            (void) sprintf(q, "%%%02X", (char) s[pos].latin1());
 
1276
            s.remove(pos,1);
 
1277
            s.insert(pos+1,q);
 
1278
            pos += 3;
 
1279
            break;
 
1280
          default:
 
1281
            pos++;
 
1282
          }
 
1283
      }
 
1284
} // cddb_http_xlat
 
1285
 
 
1286
 
 
1287
void 
 
1288
CDDB::setPathList(QStringList& _paths)
 
1289
{
 
1290
    pathlist = _paths; // automatically makes deep copies is _paths has deep copies
 
1291
} // setPathList
 
1292
 
 
1293
#include "cddb.moc"
 
1294
 
 
1295
 
 
1296
 
 
1297
 
 
1298