| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Documentation and Copyright exist after __END__ |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package CDDB; |
|
4
|
|
|
|
|
|
|
require 5.001; |
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
863
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
33
|
|
|
7
|
1
|
|
|
1
|
|
4
|
use vars qw($VERSION); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
64
|
|
|
8
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
114
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
$VERSION = '1.220'; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
BEGIN { |
|
13
|
1
|
50
|
|
1
|
|
6
|
if ($^O eq 'MSWin32') { |
|
14
|
0
|
|
|
|
|
0
|
eval 'sub USING_WINDOWS () { 1 }'; |
|
15
|
|
|
|
|
|
|
} |
|
16
|
|
|
|
|
|
|
else { |
|
17
|
1
|
|
|
|
|
59
|
eval 'sub USING_WINDOWS () { 0 }'; |
|
18
|
|
|
|
|
|
|
} |
|
19
|
|
|
|
|
|
|
} |
|
20
|
|
|
|
|
|
|
|
|
21
|
1
|
|
|
1
|
|
945
|
use IO::Socket; |
|
|
1
|
|
|
|
|
25200
|
|
|
|
1
|
|
|
|
|
5
|
|
|
22
|
1
|
|
|
1
|
|
1308
|
use Sys::Hostname; |
|
|
1
|
|
|
|
|
1253
|
|
|
|
1
|
|
|
|
|
4960
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# A list of known freedb servers. I've stopped using Gracenote's CDDB |
|
25
|
|
|
|
|
|
|
# because they never return my e-mail about becoming a developer. To |
|
26
|
|
|
|
|
|
|
# top it off, they've started denying CDDB.pm users. |
|
27
|
|
|
|
|
|
|
# TODO: Fetch the list from freedb.freedb.org, which is a round-robin |
|
28
|
|
|
|
|
|
|
# for all the others anyway. |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $cddbp_host_selector = 0; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my @cddbp_hosts = ( |
|
33
|
|
|
|
|
|
|
[ 'localhost' => 8880 ], |
|
34
|
|
|
|
|
|
|
[ 'freedb.freedb.org' => 8880 ], |
|
35
|
|
|
|
|
|
|
[ 'us.freedb.org', => 8880 ], |
|
36
|
|
|
|
|
|
|
[ 'ca.freedb.org', => 8880 ], |
|
37
|
|
|
|
|
|
|
[ 'ca2.freedb.org', => 8880 ], |
|
38
|
|
|
|
|
|
|
[ 'uk.freedb.org' => 8880 ], |
|
39
|
|
|
|
|
|
|
[ 'no.freedb.org' => 8880 ], |
|
40
|
|
|
|
|
|
|
[ 'de.freedb.org' => 8880 ], |
|
41
|
|
|
|
|
|
|
[ 'at.freedb.org' => 8880 ], |
|
42
|
|
|
|
|
|
|
[ 'freedb.freedb.de' => 8880 ], |
|
43
|
|
|
|
|
|
|
); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
46
|
|
|
|
|
|
|
# Determine whether we can submit changes by e-mail. |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $imported_mail = 0; |
|
49
|
|
|
|
|
|
|
eval { |
|
50
|
|
|
|
|
|
|
require Mail::Internet; |
|
51
|
|
|
|
|
|
|
require Mail::Header; |
|
52
|
|
|
|
|
|
|
require MIME::QuotedPrint; |
|
53
|
|
|
|
|
|
|
$imported_mail = 1; |
|
54
|
|
|
|
|
|
|
}; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
57
|
|
|
|
|
|
|
# Determine whether we can use HTTP for requests and submissions. |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my $imported_http = 0; |
|
60
|
|
|
|
|
|
|
eval { |
|
61
|
|
|
|
|
|
|
require LWP; |
|
62
|
|
|
|
|
|
|
require HTTP::Request; |
|
63
|
|
|
|
|
|
|
$imported_http = 1; |
|
64
|
|
|
|
|
|
|
}; |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
67
|
|
|
|
|
|
|
# Send a command. If we're not connected, try to connect first. |
|
68
|
|
|
|
|
|
|
# Returns 1 if the command is sent ok; 0 if there was a problem. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub command { |
|
71
|
16
|
|
|
16
|
0
|
41
|
my $self = shift; |
|
72
|
16
|
|
|
|
|
76
|
my $str = join(' ', @_); |
|
73
|
|
|
|
|
|
|
|
|
74
|
16
|
100
|
|
|
|
86
|
unless ($self->{handle}) { |
|
75
|
3
|
50
|
|
|
|
16
|
$self->connect() or return 0; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
16
|
|
|
|
|
67
|
$self->debug_print(0, '>>> ', $str); |
|
79
|
|
|
|
|
|
|
|
|
80
|
16
|
|
|
|
|
42
|
my $len = length($str .= "\x0D\x0A"); |
|
81
|
|
|
|
|
|
|
|
|
82
|
16
|
50
|
|
|
|
497
|
local $SIG{PIPE} = 'IGNORE' unless ($^O eq 'MacOS'); |
|
83
|
16
|
50
|
|
|
|
3291
|
return 0 unless(syswrite($self->{handle}, $str, $len) == $len); |
|
84
|
16
|
|
|
|
|
202
|
return 1; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
88
|
|
|
|
|
|
|
# Retrieve a line from the server. Uses a buffer to allow for |
|
89
|
|
|
|
|
|
|
# ungetting lines. Returns the next line or undef if there is a |
|
90
|
|
|
|
|
|
|
# problem. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub getline { |
|
93
|
129
|
|
|
129
|
0
|
202
|
my $self = shift; |
|
94
|
|
|
|
|
|
|
|
|
95
|
129
|
100
|
|
|
|
160
|
if (@{$self->{lines}}) { |
|
|
129
|
|
|
|
|
715
|
|
|
96
|
106
|
|
|
|
|
136
|
my $line = shift @{$self->{lines}}; |
|
|
106
|
|
|
|
|
309
|
|
|
97
|
106
|
|
|
|
|
279
|
$self->debug_print(0, '<<< ', $line); |
|
98
|
106
|
|
|
|
|
453
|
return $line; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
23
|
|
|
|
|
59
|
my $socket = $self->{handle}; |
|
102
|
23
|
50
|
|
|
|
70
|
return unless defined $socket; |
|
103
|
|
|
|
|
|
|
|
|
104
|
23
|
|
|
|
|
56
|
my $fd = fileno($socket); |
|
105
|
23
|
50
|
|
|
|
60
|
return unless defined $fd; |
|
106
|
|
|
|
|
|
|
|
|
107
|
23
|
|
|
|
|
147
|
vec(my $rin = '', $fd, 1) = 1; |
|
108
|
23
|
|
50
|
|
|
197
|
my $timeout = $self->{timeout} || undef; |
|
109
|
23
|
|
|
|
|
55
|
my $frame = $self->{frame}; |
|
110
|
|
|
|
|
|
|
|
|
111
|
23
|
|
|
|
|
32
|
until (@{$self->{lines}}) { |
|
|
46
|
|
|
|
|
2490
|
|
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Fail if the socket is inactive for the timeout period. Fail |
|
114
|
|
|
|
|
|
|
# also if sysread returns nothing. |
|
115
|
|
|
|
|
|
|
|
|
116
|
23
|
50
|
|
|
|
1050311
|
return unless select(my $rout=$rin, undef, undef, $timeout); |
|
117
|
23
|
50
|
|
|
|
972
|
return unless defined sysread($socket, my $buf='', 1024); |
|
118
|
|
|
|
|
|
|
|
|
119
|
23
|
|
|
|
|
178
|
$frame .= $buf; |
|
120
|
23
|
|
|
|
|
707
|
my @lines = split(/\x0D?\x0A/, $frame); |
|
121
|
23
|
100
|
66
|
|
|
439
|
$frame = ( |
|
122
|
|
|
|
|
|
|
(length($buf) == 0 || substr($buf, -1, 1) eq "\x0A") |
|
123
|
|
|
|
|
|
|
? '' |
|
124
|
|
|
|
|
|
|
: pop(@lines) |
|
125
|
|
|
|
|
|
|
); |
|
126
|
23
|
|
|
|
|
103
|
push @{$self->{lines}}, map { decode('utf8', $_) } @lines; |
|
|
23
|
|
|
|
|
173
|
|
|
|
129
|
|
|
|
|
14280
|
|
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
23
|
|
|
|
|
82
|
$self->{frame} = $frame; |
|
130
|
|
|
|
|
|
|
|
|
131
|
23
|
|
|
|
|
41
|
my $line = shift @{$self->{lines}}; |
|
|
23
|
|
|
|
|
78
|
|
|
132
|
23
|
|
|
|
|
147
|
$self->debug_print(0, '<<< ', $line); |
|
133
|
23
|
|
|
|
|
128
|
return $line; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
137
|
|
|
|
|
|
|
# Receive a server response, and parse it into its numeric code and |
|
138
|
|
|
|
|
|
|
# text message. Return the code's first character, which usually |
|
139
|
|
|
|
|
|
|
# indicates the response class (ok, error, information, warning, |
|
140
|
|
|
|
|
|
|
# etc.). Returns undef on failure. |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub response { |
|
143
|
19
|
|
|
19
|
0
|
47
|
my $self = shift; |
|
144
|
19
|
|
|
|
|
31
|
my ($code, $text); |
|
145
|
|
|
|
|
|
|
|
|
146
|
19
|
|
|
|
|
71
|
my $str = $self->getline(); |
|
147
|
|
|
|
|
|
|
|
|
148
|
19
|
50
|
|
|
|
59
|
return unless defined($str); |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# Fail if the line we get isn't the proper format. |
|
151
|
19
|
50
|
|
|
|
431
|
return unless ( ($code, $text) = ($str =~ /^(\d+)\s*(.*?)\s*$/) ); |
|
152
|
|
|
|
|
|
|
|
|
153
|
19
|
|
|
|
|
72
|
$self->{response_code} = $code; |
|
154
|
19
|
|
|
|
|
63
|
$self->{response_text} = $text; |
|
155
|
19
|
|
|
|
|
165
|
substr($code, 0, 1); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
159
|
|
|
|
|
|
|
# Accessors to retrieve the last response() call's code and text |
|
160
|
|
|
|
|
|
|
# separately. |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub code { |
|
163
|
20
|
|
|
20
|
0
|
29
|
my $self = shift; |
|
164
|
20
|
|
|
|
|
117
|
$self->{response_code}; |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub text { |
|
168
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
|
169
|
1
|
|
|
|
|
15
|
$self->{response_text}; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
173
|
|
|
|
|
|
|
# Helper to print stuff for debugging. |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub debug_print { |
|
176
|
154
|
|
|
154
|
0
|
344
|
my $self = shift; |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Don't bother if not debugging. |
|
179
|
154
|
50
|
|
|
|
607
|
return unless $self->{debug}; |
|
180
|
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
0
|
my $level = shift; |
|
182
|
0
|
|
|
|
|
0
|
my $text = join('', @_); |
|
183
|
0
|
|
|
|
|
0
|
print STDERR $text, "\n"; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
187
|
|
|
|
|
|
|
# Read data until it's terminated by a single dot on its own line. |
|
188
|
|
|
|
|
|
|
# Two dots at the start of a line are replaced by one. Returns an |
|
189
|
|
|
|
|
|
|
# ARRAY reference containing the lines received, or undef on error. |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub read_until_dot { |
|
192
|
6
|
|
|
6
|
0
|
14
|
my $self = shift; |
|
193
|
6
|
|
|
|
|
15
|
my @lines; |
|
194
|
|
|
|
|
|
|
|
|
195
|
6
|
|
|
|
|
11
|
while ('true') { |
|
196
|
110
|
50
|
|
|
|
263
|
my $line = $self->getline() or return; |
|
197
|
110
|
100
|
|
|
|
418
|
last if ($line =~ /^\.$/); |
|
198
|
104
|
|
|
|
|
573
|
$line =~ s/^\.\././; |
|
199
|
104
|
|
|
|
|
192
|
push @lines, $line; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
6
|
|
|
|
|
35
|
\@lines; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
206
|
|
|
|
|
|
|
# Create an object to represent one or more cddbp sessions. |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub new { |
|
209
|
1
|
|
|
1
|
1
|
16
|
my $type = shift; |
|
210
|
1
|
|
|
|
|
8
|
my %param = @_; |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# Attempt to suss our hostname. |
|
213
|
1
|
|
|
|
|
6
|
my $hostname = &hostname(); |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Attempt to suss our login ID. |
|
216
|
1
|
|
33
|
|
|
23
|
my $login = $param{Login} || $ENV{LOGNAME} || $ENV{USER}; |
|
217
|
1
|
50
|
|
|
|
7
|
if (not defined $login) { |
|
218
|
1
|
|
|
|
|
1
|
if (USING_WINDOWS) { |
|
219
|
|
|
|
|
|
|
carp( |
|
220
|
|
|
|
|
|
|
"Can't get login ID. Use Login parameter or " . |
|
221
|
|
|
|
|
|
|
"set LOGNAME or USER environment variable. Using default login " . |
|
222
|
|
|
|
|
|
|
"ID 'win32usr'" |
|
223
|
|
|
|
|
|
|
); |
|
224
|
|
|
|
|
|
|
$login = 'win32usr'; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
else { |
|
227
|
1
|
50
|
|
|
|
933
|
$login = getpwuid($>) |
|
228
|
|
|
|
|
|
|
or croak( |
|
229
|
|
|
|
|
|
|
"Can't get login ID. " . |
|
230
|
|
|
|
|
|
|
"Set LOGNAME or USER environment variable and try again: $!" |
|
231
|
|
|
|
|
|
|
); |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Debugging flag. |
|
236
|
1
|
|
|
|
|
4
|
my $debug = $param{Debug}; |
|
237
|
1
|
50
|
|
|
|
4
|
$debug = 0 unless defined $debug; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Choose a particular cddbp host. |
|
240
|
1
|
|
|
|
|
3
|
my $host = $param{Host}; |
|
241
|
1
|
50
|
|
|
|
2
|
$host = '' unless defined $host; |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Choose a particular cddbp port. |
|
244
|
1
|
|
|
|
|
3
|
my $port = $param{Port}; |
|
245
|
1
|
50
|
|
|
|
2
|
$port = 8880 unless $port; |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Choose a particular cddbp submission address. |
|
248
|
1
|
|
|
|
|
9
|
my $submit_to = $param{Submit_Address}; |
|
249
|
1
|
50
|
|
|
|
4
|
$submit_to = 'freedb-submit@freedb.org' unless defined $submit_to; |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Change the cddbp client name. |
|
252
|
1
|
|
|
|
|
3
|
my $client_name = $param{Client_Name}; |
|
253
|
1
|
50
|
|
|
|
3
|
$client_name = 'CDDB.pm' unless defined $client_name; |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Change the cddbp client version. |
|
256
|
1
|
|
|
|
|
2
|
my $client_version = $param{Client_Version}; |
|
257
|
1
|
50
|
|
|
|
4
|
$client_version = $VERSION unless defined $client_version; |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# Whether to use utf-8 for submission |
|
260
|
1
|
|
|
|
|
1
|
my $utf8 = $param{Utf8}; |
|
261
|
1
|
50
|
|
|
|
4
|
$utf8 = 1 unless defined $utf8; |
|
262
|
1
|
50
|
|
|
|
4
|
if ($utf8) { |
|
263
|
1
|
|
|
|
|
2
|
eval { |
|
264
|
1
|
|
|
|
|
1072
|
require Encode; |
|
265
|
1
|
|
|
|
|
11160
|
import Encode; |
|
266
|
|
|
|
|
|
|
}; |
|
267
|
1
|
50
|
|
|
|
32
|
if ( $@ ) { |
|
268
|
0
|
|
|
|
|
0
|
carp 'Unable to load the Encode module, falling back to ascii'; |
|
269
|
0
|
|
|
|
|
0
|
$utf8 = 0; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
|
|
273
|
1
|
50
|
|
|
|
4
|
eval 'sub encode { $_[1] };sub decode { $_[1] }' unless $utf8; |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Change the cddbp protocol level. |
|
276
|
1
|
|
|
|
|
2
|
my $cddb_protocol = $param{Protocol_Version}; |
|
277
|
1
|
50
|
|
|
|
7
|
$cddb_protocol = ($utf8 ? 6 : 1) unless defined $cddb_protocol; |
|
|
|
50
|
|
|
|
|
|
|
278
|
1
|
50
|
33
|
|
|
8
|
carp <
|
|
279
|
|
|
|
|
|
|
You have requested protocol level $cddb_protocol. However, |
|
280
|
|
|
|
|
|
|
utf-8 support is only available starting from level 6 |
|
281
|
|
|
|
|
|
|
EOF |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# Mac Freaks Got Spaces! Augh! |
|
284
|
1
|
|
|
|
|
3
|
$login =~ s/\s+/_/g; |
|
285
|
|
|
|
|
|
|
|
|
286
|
1
|
|
|
|
|
19
|
my $self = bless { |
|
287
|
|
|
|
|
|
|
hostname => $hostname, |
|
288
|
|
|
|
|
|
|
login => $login, |
|
289
|
|
|
|
|
|
|
mail_from => undef, |
|
290
|
|
|
|
|
|
|
mail_host => undef, |
|
291
|
|
|
|
|
|
|
libname => $client_name, |
|
292
|
|
|
|
|
|
|
libver => $client_version, |
|
293
|
|
|
|
|
|
|
cddbmail => $submit_to, |
|
294
|
|
|
|
|
|
|
debug => $debug, |
|
295
|
|
|
|
|
|
|
host => $host, |
|
296
|
|
|
|
|
|
|
port => $port, |
|
297
|
|
|
|
|
|
|
cddb_protocol => $cddb_protocol, |
|
298
|
|
|
|
|
|
|
utf8 => $utf8, |
|
299
|
|
|
|
|
|
|
lines => [], |
|
300
|
|
|
|
|
|
|
frame => '', |
|
301
|
|
|
|
|
|
|
response_code => '000', |
|
302
|
|
|
|
|
|
|
response_text => '', |
|
303
|
|
|
|
|
|
|
}, $type; |
|
304
|
|
|
|
|
|
|
|
|
305
|
1
|
|
|
|
|
8
|
$self; |
|
306
|
|
|
|
|
|
|
} |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
309
|
|
|
|
|
|
|
# Disconnect from a cddbp server. This is needed sometimes when a |
|
310
|
|
|
|
|
|
|
# server decides a session has performed enough requests. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub disconnect { |
|
313
|
3
|
|
|
3
|
0
|
3165
|
my $self = shift; |
|
314
|
3
|
50
|
|
|
|
21
|
if ($self->{handle}) { |
|
315
|
3
|
|
|
|
|
11
|
$self->command('quit'); # quit |
|
316
|
3
|
|
|
|
|
14
|
$self->response(); # wait for any response |
|
317
|
3
|
|
|
|
|
6299
|
delete $self->{handle}; # close the socket |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
else { |
|
320
|
0
|
|
|
|
|
0
|
$self->debug_print( 0, '--- disconnect on unconnected handle' ); |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
325
|
|
|
|
|
|
|
# Connect to a cddbp server. Connecting and disconnecting are done |
|
326
|
|
|
|
|
|
|
# transparently and are performed on the basis of need. Furthermore, |
|
327
|
|
|
|
|
|
|
# this routine will cycle through servers until one connects or it has |
|
328
|
|
|
|
|
|
|
# exhausted all its possibilities. Returns true if successful, or |
|
329
|
|
|
|
|
|
|
# false if failed. |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub connect { |
|
332
|
3
|
|
|
3
|
0
|
6
|
my $self = shift; |
|
333
|
3
|
|
|
|
|
4
|
my $cddbp_host; |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Try to get our hostname yet again, in case it failed during the |
|
336
|
|
|
|
|
|
|
# constructor call. |
|
337
|
3
|
50
|
|
|
|
23
|
unless (defined $self->{hostname}) { |
|
338
|
0
|
0
|
|
|
|
0
|
$self->{hostname} = &hostname() or croak "can't get hostname: $!"; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# The handshake loop tries to complete an entire connection |
|
342
|
|
|
|
|
|
|
# negociation. It loops until success, or until HOST returns |
|
343
|
|
|
|
|
|
|
# because all the hosts have failed us. |
|
344
|
|
|
|
|
|
|
|
|
345
|
3
|
|
|
|
|
6
|
HANDSHAKE: while ('true') { |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# Loop through the CDDB protocol hosts list up to twice in order |
|
348
|
|
|
|
|
|
|
# to find a server that will respond. This implements a 2x retry. |
|
349
|
|
|
|
|
|
|
|
|
350
|
3
|
|
|
|
|
17
|
HOST: for (1..(@cddbp_hosts * 2)) { |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# Hard disconnect here to prevent recursion. |
|
353
|
4
|
|
|
|
|
11
|
delete $self->{handle}; |
|
354
|
|
|
|
|
|
|
|
|
355
|
4
|
|
|
|
|
6
|
($self->{host}, $self->{port}) = @{$cddbp_hosts[$cddbp_host_selector]}; |
|
|
4
|
|
|
|
|
27
|
|
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# Assign the host we selected, and attempt a connection. |
|
358
|
4
|
|
|
|
|
30
|
$self->debug_print( |
|
359
|
|
|
|
|
|
|
0, |
|
360
|
|
|
|
|
|
|
"=== connecting to $self->{host} port $self->{port}" |
|
361
|
|
|
|
|
|
|
); |
|
362
|
4
|
|
|
|
|
54
|
$self->{handle} = new IO::Socket::INET( |
|
363
|
|
|
|
|
|
|
PeerAddr => $self->{host}, |
|
364
|
|
|
|
|
|
|
PeerPort => $self->{port}, |
|
365
|
|
|
|
|
|
|
Proto => 'tcp', |
|
366
|
|
|
|
|
|
|
Timeout => 30, |
|
367
|
|
|
|
|
|
|
); |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# The host did not answer. Clean up after the failed attempt |
|
370
|
|
|
|
|
|
|
# and cycle to the next host. |
|
371
|
4
|
100
|
|
|
|
129340
|
unless (defined $self->{handle}) { |
|
372
|
1
|
|
|
|
|
15
|
$self->debug_print( |
|
373
|
|
|
|
|
|
|
0, |
|
374
|
|
|
|
|
|
|
"--- error connecting to $self->{host} port $self->{port}: $!" |
|
375
|
|
|
|
|
|
|
); |
|
376
|
|
|
|
|
|
|
|
|
377
|
1
|
|
|
|
|
4
|
delete $self->{handle}; |
|
378
|
1
|
|
|
|
|
4
|
$self->{host} = $self->{port} = ''; |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Try the next host in the list. Wrap if necessary. |
|
381
|
1
|
50
|
|
|
|
6
|
$cddbp_host_selector = 0 if ++$cddbp_host_selector > @cddbp_hosts; |
|
382
|
|
|
|
|
|
|
|
|
383
|
1
|
|
|
|
|
4
|
next HOST; |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# The host accepted our connection. We'll push it back on the |
|
387
|
|
|
|
|
|
|
# list of known cddbp hosts so it can be tried later. And we're |
|
388
|
|
|
|
|
|
|
# done with the host list cycle for now. |
|
389
|
|
|
|
|
|
|
$self->debug_print( |
|
390
|
3
|
|
|
|
|
39
|
0, |
|
391
|
|
|
|
|
|
|
"+++ successfully connected to $self->{host} port $self->{port}" |
|
392
|
|
|
|
|
|
|
); |
|
393
|
|
|
|
|
|
|
|
|
394
|
3
|
|
|
|
|
14
|
last HOST; |
|
395
|
|
|
|
|
|
|
} |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# Tried the whole list twice without success? Time to give up. |
|
398
|
3
|
50
|
|
|
|
16
|
unless (defined $self->{handle}) { |
|
399
|
0
|
|
|
|
|
0
|
$self->debug_print( 0, "--- all cddbp servers failed to answer" ); |
|
400
|
0
|
0
|
|
|
|
0
|
warn "No cddb protocol servers answer. Is your network OK?\n" |
|
401
|
|
|
|
|
|
|
unless $self->{debug}; |
|
402
|
0
|
|
|
|
|
0
|
return; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Turn off buffering on the socket handle. |
|
406
|
3
|
|
|
|
|
42
|
select((select($self->{handle}), $|=1)[0]); |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# Get the server's banner message. Try reconnecting if it's bad. |
|
409
|
3
|
|
|
|
|
16
|
my $code = $self->response(); |
|
410
|
3
|
50
|
|
|
|
20
|
if ($code != 2) { |
|
411
|
0
|
|
|
|
|
0
|
$self->debug_print( |
|
412
|
|
|
|
|
|
|
0, "--- bad cddbp response: ", |
|
413
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
414
|
|
|
|
|
|
|
); |
|
415
|
0
|
|
|
|
|
0
|
next HANDSHAKE; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# Say hello, and wait for a response. |
|
419
|
|
|
|
|
|
|
$self->command( |
|
420
|
3
|
|
|
|
|
28
|
'cddb hello', |
|
421
|
|
|
|
|
|
|
$self->{login}, $self->{hostname}, |
|
422
|
|
|
|
|
|
|
$self->{libname}, $self->{libver} |
|
423
|
|
|
|
|
|
|
); |
|
424
|
3
|
|
|
|
|
16
|
$code = $self->response(); |
|
425
|
3
|
50
|
|
|
|
28
|
if ($code == 4) { |
|
426
|
0
|
|
|
|
|
0
|
$self->debug_print( |
|
427
|
|
|
|
|
|
|
0, "--- the server denies us: ", |
|
428
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
429
|
|
|
|
|
|
|
); |
|
430
|
0
|
|
|
|
|
0
|
return; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
3
|
50
|
|
|
|
13
|
if ($code != 2) { |
|
433
|
0
|
|
|
|
|
0
|
$self->debug_print( |
|
434
|
|
|
|
|
|
|
0, "--- the server didn't handshake: ", |
|
435
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
436
|
|
|
|
|
|
|
); |
|
437
|
0
|
|
|
|
|
0
|
next HANDSHAKE; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# Set the protocol level. |
|
441
|
3
|
50
|
|
|
|
39
|
if ($self->{cddb_protocol} != 1) { |
|
442
|
3
|
|
|
|
|
19
|
$self->command( 'proto', $self->{cddb_protocol} ); |
|
443
|
3
|
|
|
|
|
15
|
$code = $self->response(); |
|
444
|
3
|
50
|
|
|
|
46
|
if ($code != 2) { |
|
445
|
0
|
|
|
|
|
0
|
$self->debug_print( |
|
446
|
|
|
|
|
|
|
0, "--- can't set protocol level ", |
|
447
|
|
|
|
|
|
|
$self->{cddb_protocol}, ' ', |
|
448
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
449
|
|
|
|
|
|
|
); |
|
450
|
0
|
|
|
|
|
0
|
return; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
# If we get here, everything succeeded. |
|
455
|
3
|
|
|
|
|
19
|
return 1; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Destroying the cddbp object disconnects from the server. |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub DESTROY { |
|
462
|
1
|
|
|
1
|
|
1312
|
my $self = shift; |
|
463
|
1
|
|
|
|
|
6
|
$self->disconnect(); |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
############################################################################### |
|
467
|
|
|
|
|
|
|
# High-level cddbp functions. |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
470
|
|
|
|
|
|
|
# Get a list of available genres. Returns an array of genre names, or |
|
471
|
|
|
|
|
|
|
# undef on failure. |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub get_genres { |
|
474
|
1
|
|
|
1
|
1
|
833
|
my $self = shift; |
|
475
|
1
|
|
|
|
|
2
|
my @genres; |
|
476
|
|
|
|
|
|
|
|
|
477
|
1
|
|
|
|
|
5
|
$self->command('cddb lscat'); |
|
478
|
1
|
|
|
|
|
8
|
my $code = $self->response(); |
|
479
|
1
|
50
|
|
|
|
11
|
return unless $code; |
|
480
|
|
|
|
|
|
|
|
|
481
|
1
|
50
|
|
|
|
7
|
if ($code == 2) { |
|
482
|
1
|
|
|
|
|
4
|
my $genres = $self->read_until_dot(); |
|
483
|
1
|
50
|
|
|
|
17
|
return @$genres if defined $genres; |
|
484
|
0
|
|
|
|
|
0
|
return; |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
$self->debug_print( |
|
488
|
0
|
|
|
|
|
0
|
0, '--- error listing categories: ', |
|
489
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
490
|
|
|
|
|
|
|
); |
|
491
|
0
|
|
|
|
|
0
|
return; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
495
|
|
|
|
|
|
|
# Calculate a cddbp ID based on a text table of contents. The text |
|
496
|
|
|
|
|
|
|
# format was chosen because it was straightforward and easy to |
|
497
|
|
|
|
|
|
|
# generate. In a scalar context, this returns just the cddbp ID. In |
|
498
|
|
|
|
|
|
|
# a list context it returns several things: a listref of track |
|
499
|
|
|
|
|
|
|
# numbers, a listref of track lengths (MM:SS format), a listref of |
|
500
|
|
|
|
|
|
|
# track offsets (in seconds), and the disc's total playing time in |
|
501
|
|
|
|
|
|
|
# seconds. In either context it returns undef on failure. |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub calculate_id { |
|
504
|
2
|
|
|
2
|
1
|
1717
|
my $self = shift; |
|
505
|
2
|
|
|
|
|
9
|
my @toc = @_; |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
my ( |
|
508
|
2
|
|
|
|
|
12
|
$seconds_previous, $seconds_first, $seconds_last, $cddbp_sum, |
|
509
|
|
|
|
|
|
|
@track_numbers, @track_lengths, @track_offsets, |
|
510
|
|
|
|
|
|
|
); |
|
511
|
|
|
|
|
|
|
|
|
512
|
2
|
|
|
|
|
7
|
foreach my $line (@toc) { |
|
513
|
4
|
|
|
|
|
27
|
my ($track, $mm_begin, $ss_begin, $ff_begin) = split(/\s+/, $line, 4); |
|
514
|
4
|
|
|
|
|
14
|
my $frame_offset = (($mm_begin * 60 + $ss_begin) * 75) + $ff_begin; |
|
515
|
4
|
|
|
|
|
9
|
my $seconds_begin = int($frame_offset / 75); |
|
516
|
|
|
|
|
|
|
|
|
517
|
4
|
100
|
|
|
|
12
|
if (defined $seconds_previous) { |
|
518
|
2
|
|
|
|
|
289
|
my $elapsed = $seconds_begin - $seconds_previous; |
|
519
|
2
|
|
|
|
|
15
|
push( |
|
520
|
|
|
|
|
|
|
@track_lengths, |
|
521
|
|
|
|
|
|
|
sprintf("%02d:%02d", int($elapsed / 60), $elapsed % 60) |
|
522
|
|
|
|
|
|
|
); |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
else { |
|
525
|
2
|
|
|
|
|
4
|
$seconds_first = $seconds_begin; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# Track 999 was chosen for the lead-out information. |
|
529
|
4
|
100
|
|
|
|
14
|
if ($track == 999) { |
|
530
|
2
|
|
|
|
|
3
|
$seconds_last = $seconds_begin; |
|
531
|
2
|
|
|
|
|
6
|
last; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# Track 1000 was chosen for error information. |
|
535
|
2
|
50
|
|
|
|
6
|
if ($track == 1000) { |
|
536
|
0
|
|
|
|
|
0
|
$self->debug_print( 0, "error in TOC: $ff_begin" ); |
|
537
|
0
|
|
|
|
|
0
|
return; |
|
538
|
|
|
|
|
|
|
} |
|
539
|
|
|
|
|
|
|
|
|
540
|
2
|
|
|
|
|
6
|
map { $cddbp_sum += $_; } split(//, $seconds_begin); |
|
|
2
|
|
|
|
|
5
|
|
|
541
|
2
|
|
|
|
|
5
|
push @track_offsets, $frame_offset; |
|
542
|
2
|
|
|
|
|
14
|
push @track_numbers, sprintf("%03d", $track); |
|
543
|
2
|
|
|
|
|
3
|
$seconds_previous = $seconds_begin; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# Calculate the ID. Whee! |
|
547
|
2
|
|
|
|
|
9
|
my $id = sprintf( |
|
548
|
|
|
|
|
|
|
"%02x%04x%02x", |
|
549
|
|
|
|
|
|
|
($cddbp_sum % 255), |
|
550
|
|
|
|
|
|
|
$seconds_last - $seconds_first, |
|
551
|
|
|
|
|
|
|
scalar(@track_offsets) |
|
552
|
|
|
|
|
|
|
); |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# In list context, we return several things. Some of them are |
|
555
|
|
|
|
|
|
|
# useful for generating filenames or playlists (the padded track |
|
556
|
|
|
|
|
|
|
# numbers). Others are needed for cddbp queries. |
|
557
|
|
|
|
|
|
|
return ( |
|
558
|
2
|
50
|
|
|
|
22
|
$id, \@track_numbers, \@track_lengths, \@track_offsets, $seconds_last |
|
559
|
|
|
|
|
|
|
) if wantarray(); |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# Just return the cddbp ID in scalar context. |
|
562
|
0
|
|
|
|
|
0
|
return $id; |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
566
|
|
|
|
|
|
|
# Parse cdinfo's output so calculate_id() can eat it. |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
sub parse_cdinfo { |
|
569
|
0
|
|
|
0
|
1
|
0
|
my ($self, $command) = @_; |
|
570
|
0
|
0
|
|
|
|
0
|
open(FH, $command) or croak "could not open `$command': $!"; |
|
571
|
|
|
|
|
|
|
|
|
572
|
0
|
|
|
|
|
0
|
my @toc; |
|
573
|
0
|
|
|
|
|
0
|
while () { |
|
574
|
0
|
0
|
|
|
|
0
|
if (/(\d+):\s+(\d+):(\d+):(\d+)/) { |
|
575
|
0
|
|
|
|
|
0
|
my @track = ($1,$2,$3,$4); |
|
576
|
0
|
0
|
|
|
|
0
|
$track[0] = 999 if /leadout/; |
|
577
|
0
|
|
|
|
|
0
|
push @toc, "@track"; |
|
578
|
|
|
|
|
|
|
} |
|
579
|
|
|
|
|
|
|
} |
|
580
|
0
|
|
|
|
|
0
|
close FH; |
|
581
|
0
|
|
|
|
|
0
|
return @toc; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
585
|
|
|
|
|
|
|
# Get a list of discs that match a particular CD's table of contents. |
|
586
|
|
|
|
|
|
|
# This accepts the TOC information as returned by calculate_id(). It |
|
587
|
|
|
|
|
|
|
# will also accept information in mp3 format, but I forget what that |
|
588
|
|
|
|
|
|
|
# is. Pudge asked for it, so he'd know. |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
sub get_discs { |
|
591
|
5
|
|
|
5
|
1
|
10431
|
my $self = shift; |
|
592
|
5
|
|
|
|
|
134
|
my ($id, $offsets, $total_seconds) = @_; |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# Accept the TOC in CDDB.pm format. |
|
595
|
5
|
|
|
|
|
10
|
my ($track_count, $offsets_string); |
|
596
|
5
|
50
|
|
|
|
126
|
if (ref($offsets) eq 'ARRAY') { |
|
597
|
5
|
|
|
|
|
11
|
$track_count = scalar(@$offsets); |
|
598
|
5
|
|
|
|
|
20
|
$offsets_string = join ' ', @$offsets; |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# Accept the TOC in mp3 format, for pudge. |
|
602
|
|
|
|
|
|
|
else { |
|
603
|
0
|
|
|
|
|
0
|
$offsets =~ /^(\d+?)\s+(.*)$/; |
|
604
|
0
|
|
|
|
|
0
|
$track_count = $1; |
|
605
|
0
|
|
|
|
|
0
|
$offsets_string = $2; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# Make repeated attempts to query the server. I do this to drive |
|
609
|
|
|
|
|
|
|
# the hidden server cycling. |
|
610
|
5
|
|
|
|
|
8
|
my $code; |
|
611
|
|
|
|
|
|
|
|
|
612
|
5
|
|
|
|
|
10
|
ATTEMPT: while ('true') { |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# Send a cddbp query command. |
|
615
|
5
|
50
|
|
|
|
89
|
$self->command( |
|
616
|
|
|
|
|
|
|
'cddb query', $id, $track_count, |
|
617
|
|
|
|
|
|
|
$offsets_string, $total_seconds |
|
618
|
|
|
|
|
|
|
) or return; |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# Get the response. Try again if the server is temporarly |
|
621
|
|
|
|
|
|
|
# unavailable. |
|
622
|
5
|
|
|
|
|
26
|
$code = $self->response(); |
|
623
|
5
|
50
|
|
|
|
33
|
next ATTEMPT if $self->code() == 417; |
|
624
|
5
|
|
|
|
|
19
|
last ATTEMPT; |
|
625
|
|
|
|
|
|
|
} |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
# Return undef if there's a problem. |
|
628
|
5
|
50
|
33
|
|
|
60
|
return unless defined $code and $code == 2; |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# Single matching disc. |
|
631
|
5
|
100
|
|
|
|
14
|
if ($self->code() == 200) { |
|
632
|
1
|
|
|
|
|
7
|
my ($genre, $cddbp_id, $title) = ( |
|
633
|
|
|
|
|
|
|
$self->text() =~ /^(\S+)\s*(\S+)\s*(.*?)\s*$/ |
|
634
|
|
|
|
|
|
|
); |
|
635
|
1
|
|
|
|
|
10
|
return [ $genre, $cddbp_id, $title ]; |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# No matching discs. |
|
639
|
4
|
50
|
|
|
|
14
|
return if $self->code() == 202; |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# Multiple matching discs. |
|
642
|
|
|
|
|
|
|
# 210 Found exact matches, list follows (...) [proto>=4] |
|
643
|
|
|
|
|
|
|
# 211 Found inexact matches, list follows (...) [proto>=1] |
|
644
|
4
|
50
|
66
|
|
|
14
|
if ($self->code() == 210 or $self->code() == 211) { |
|
645
|
4
|
|
|
|
|
15
|
my $discs = $self->read_until_dot(); |
|
646
|
4
|
50
|
|
|
|
15
|
return unless defined $discs; |
|
647
|
|
|
|
|
|
|
|
|
648
|
4
|
|
|
|
|
7
|
my @matches; |
|
649
|
4
|
|
|
|
|
16
|
foreach my $disc (@$discs) { |
|
650
|
74
|
|
|
|
|
955
|
my ($genre, $cddbp_id, $title) = ($disc =~ /^(\S+)\s*(\S+)\s*(.*?)\s*$/); |
|
651
|
74
|
|
|
|
|
289
|
push(@matches, [ $genre, $cddbp_id, $title ]); |
|
652
|
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
4
|
|
|
|
|
81
|
return @matches; |
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
# What the heck? |
|
658
|
|
|
|
|
|
|
$self->debug_print( |
|
659
|
0
|
|
|
|
|
0
|
0, "--- unknown cddbp response: ", |
|
660
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
661
|
|
|
|
|
|
|
); |
|
662
|
0
|
|
|
|
|
0
|
return; |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
666
|
|
|
|
|
|
|
# A little helper to combine list-context calculate_id() with |
|
667
|
|
|
|
|
|
|
# get_discs(). |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
sub get_discs_by_toc { |
|
670
|
1
|
|
|
1
|
1
|
10
|
my $self = shift; |
|
671
|
1
|
|
|
|
|
3
|
my (@info, @discs); |
|
672
|
1
|
50
|
|
|
|
6
|
if (@info = $self->calculate_id(@_)) { |
|
673
|
1
|
|
|
|
|
8
|
@discs = $self->get_discs(@info[0, 3, 4]); |
|
674
|
|
|
|
|
|
|
} |
|
675
|
1
|
|
|
|
|
11
|
@discs; |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
679
|
|
|
|
|
|
|
# A little helper to get discs from an existing query string. |
|
680
|
|
|
|
|
|
|
# Contributed by Ron Grabowski. |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
sub get_discs_by_query { |
|
683
|
1
|
|
|
1
|
1
|
791
|
my ($self, $query) = @_; |
|
684
|
1
|
|
|
|
|
10
|
my (undef, undef, $cddbp_id, $tracks, @offsets) = split /\s+/, $query; |
|
685
|
1
|
|
|
|
|
4
|
my $total_seconds = pop @offsets; |
|
686
|
1
|
|
|
|
|
6
|
my @discs = $self->get_discs($cddbp_id, \@offsets, $total_seconds); |
|
687
|
1
|
|
|
|
|
37
|
return @discs; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
691
|
|
|
|
|
|
|
# Retrieve the database record for a particular genre/id combination. |
|
692
|
|
|
|
|
|
|
# Returns a moderately complex hashref representing the cddbp record, |
|
693
|
|
|
|
|
|
|
# or undef on failure. |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub get_disc_details { |
|
696
|
1
|
|
|
1
|
1
|
21
|
my $self = shift; |
|
697
|
1
|
|
|
|
|
9
|
my ($genre, $id) = @_; |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
# Because cddbp only allows one detail query per connection, we |
|
700
|
|
|
|
|
|
|
# force a disconnect/reconnect here if we already did one. |
|
701
|
1
|
50
|
|
|
|
11
|
if (exists $self->{'got tracks before'}) { |
|
702
|
0
|
|
|
|
|
0
|
$self->disconnect(); |
|
703
|
0
|
0
|
|
|
|
0
|
$self->connect() or return; |
|
704
|
|
|
|
|
|
|
} |
|
705
|
1
|
|
|
|
|
6
|
$self->{'got tracks before'} = 'yes'; |
|
706
|
|
|
|
|
|
|
|
|
707
|
1
|
|
|
|
|
8
|
$self->command('cddb read', $genre, $id); |
|
708
|
1
|
|
|
|
|
7
|
my $code = $self->response(); |
|
709
|
1
|
50
|
|
|
|
34
|
if ($code != 2) { |
|
710
|
0
|
|
|
|
|
0
|
$self->debug_print( |
|
711
|
|
|
|
|
|
|
0, "--- cddbp host could not read the disc record: ", |
|
712
|
|
|
|
|
|
|
$self->code(), ' ', $self->text() |
|
713
|
|
|
|
|
|
|
); |
|
714
|
0
|
|
|
|
|
0
|
return; |
|
715
|
|
|
|
|
|
|
} |
|
716
|
|
|
|
|
|
|
|
|
717
|
1
|
|
|
|
|
3
|
my $track_file; |
|
718
|
1
|
50
|
|
|
|
5
|
unless (defined($track_file = $self->read_until_dot())) { |
|
719
|
0
|
|
|
|
|
0
|
$self->debug_print( 0, "--- cddbp disc record interrupted" ); |
|
720
|
0
|
|
|
|
|
0
|
return; |
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# Parse that puppy. |
|
724
|
1
|
|
|
|
|
8
|
return parse_xmcd_file($track_file, $genre); |
|
725
|
|
|
|
|
|
|
} |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
# Arf! |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
sub parse_xmcd_file { |
|
730
|
1
|
|
|
1
|
1
|
5
|
my ($track_file, $genre) = @_; |
|
731
|
|
|
|
|
|
|
|
|
732
|
1
|
|
|
|
|
8
|
my %details = ( |
|
733
|
|
|
|
|
|
|
offsets => [ ], |
|
734
|
|
|
|
|
|
|
seconds => [ ], |
|
735
|
|
|
|
|
|
|
); |
|
736
|
1
|
|
|
|
|
3
|
my $state = 'beginning'; |
|
737
|
1
|
|
|
|
|
5
|
foreach my $line (@$track_file) { |
|
738
|
|
|
|
|
|
|
# Keep returned so-called xmcd record... |
|
739
|
19
|
|
|
|
|
47
|
$details{xmcd_record} .= $line . "\n"; |
|
740
|
|
|
|
|
|
|
|
|
741
|
19
|
100
|
|
|
|
39
|
if ($state eq 'beginning') { |
|
742
|
3
|
100
|
|
|
|
38
|
if ($line =~ /track\s*frame\s*off/i) { |
|
743
|
1
|
|
|
|
|
3
|
$state = 'offsets'; |
|
744
|
|
|
|
|
|
|
} |
|
745
|
3
|
|
|
|
|
6
|
next; |
|
746
|
|
|
|
|
|
|
} |
|
747
|
|
|
|
|
|
|
|
|
748
|
16
|
100
|
|
|
|
34
|
if ($state eq 'offsets') { |
|
749
|
2
|
100
|
|
|
|
12
|
if ($line =~ /^\#\s*(\d+)/) { |
|
750
|
1
|
|
|
|
|
3
|
push @{$details{offsets}}, $1; |
|
|
1
|
|
|
|
|
6
|
|
|
751
|
1
|
|
|
|
|
3
|
next; |
|
752
|
|
|
|
|
|
|
} |
|
753
|
1
|
|
|
|
|
2
|
$state = 'headers'; |
|
754
|
|
|
|
|
|
|
# This passes through on purpose. |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# This is not an elsif on purpose. |
|
758
|
15
|
100
|
|
|
|
29
|
if ($state eq 'headers') { |
|
759
|
8
|
100
|
|
|
|
23
|
if ($line =~ /^\#/) { |
|
760
|
7
|
|
|
|
|
37
|
$line =~ s/\s+/ /g; |
|
761
|
7
|
100
|
|
|
|
54
|
if (my ($header, $value) = ($line =~ /^\#\s*(.*?)\:\s*(.*?)\s*$/)) { |
|
762
|
4
|
|
|
|
|
28
|
$details{lc($header)} = $value; |
|
763
|
|
|
|
|
|
|
} |
|
764
|
7
|
|
|
|
|
12
|
next; |
|
765
|
|
|
|
|
|
|
} |
|
766
|
1
|
|
|
|
|
3
|
$state = 'data'; |
|
767
|
|
|
|
|
|
|
# This passes through on purpose. |
|
768
|
|
|
|
|
|
|
} |
|
769
|
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
# This is not an elsif on purpose. |
|
771
|
8
|
50
|
|
|
|
17
|
if ($state eq 'data') { |
|
772
|
|
|
|
|
|
|
next unless ( |
|
773
|
8
|
100
|
|
|
|
62
|
my ($tag, $idx, $val) = ($line =~ /^\s*(.+?)(\d*)\s*\=\s*(.+?)\s*$/) |
|
774
|
|
|
|
|
|
|
); |
|
775
|
6
|
|
|
|
|
11
|
$tag = lc($tag); |
|
776
|
|
|
|
|
|
|
|
|
777
|
6
|
100
|
|
|
|
13
|
if ($idx ne '') { |
|
778
|
1
|
|
|
|
|
2
|
$tag .= 's'; |
|
779
|
1
|
50
|
|
|
|
6
|
$details{$tag} = [ ] unless exists $details{$tag}; |
|
780
|
1
|
|
|
|
|
5
|
$details{$tag}->[$idx] .= $val; |
|
781
|
1
|
|
|
|
|
5
|
$details{$tag}->[$idx] =~ s/^\s+//; |
|
782
|
1
|
|
|
|
|
11
|
$details{$tag}->[$idx] =~ s/\s+$//; |
|
783
|
1
|
|
|
|
|
5
|
$details{$tag}->[$idx] =~ s/\s+/ /g; |
|
784
|
|
|
|
|
|
|
} |
|
785
|
|
|
|
|
|
|
else { |
|
786
|
5
|
|
|
|
|
16
|
$details{$tag} .= $val; |
|
787
|
5
|
|
|
|
|
13
|
$details{$tag} =~ s/^\s+//; |
|
788
|
5
|
|
|
|
|
15
|
$details{$tag} =~ s/\s+$//; |
|
789
|
5
|
|
|
|
|
20
|
$details{$tag} =~ s/\s+/ /g; |
|
790
|
|
|
|
|
|
|
} |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
} |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# Translate disc offsets into seconds. This builds a virtual track |
|
795
|
|
|
|
|
|
|
# 0, which is the time from the beginning of the disc to the |
|
796
|
|
|
|
|
|
|
# beginning of the first song. That time's used later to calculate |
|
797
|
|
|
|
|
|
|
# the final track's length. |
|
798
|
|
|
|
|
|
|
|
|
799
|
1
|
|
|
|
|
2
|
my $last_offset = 0; |
|
800
|
1
|
|
|
|
|
2
|
foreach (@{$details{offsets}}) { |
|
|
1
|
|
|
|
|
3
|
|
|
801
|
1
|
|
|
|
|
2
|
push @{$details{seconds}}, int(($_ - $last_offset) / 75); |
|
|
1
|
|
|
|
|
6
|
|
|
802
|
1
|
|
|
|
|
3
|
$last_offset = $_; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# Create the final track length from the disc length. Remove the |
|
806
|
|
|
|
|
|
|
# virtual track 0 in the process. |
|
807
|
|
|
|
|
|
|
|
|
808
|
1
|
|
|
|
|
2
|
my $disc_length = $details{"disc length"}; |
|
809
|
1
|
|
|
|
|
8
|
$disc_length =~ s/ .*$//; |
|
810
|
|
|
|
|
|
|
|
|
811
|
1
|
|
|
|
|
2
|
my $first_start = shift @{$details{seconds}}; |
|
|
1
|
|
|
|
|
10
|
|
|
812
|
1
|
|
|
|
|
6
|
push( |
|
813
|
1
|
|
|
|
|
2
|
@{$details{seconds}}, |
|
814
|
|
|
|
|
|
|
$disc_length - int($details{offsets}->[-1] / 75) + 1 - $first_start |
|
815
|
|
|
|
|
|
|
); |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
# Add the genre, if we have it. |
|
818
|
1
|
|
|
|
|
2
|
$details{genre} = $genre; |
|
819
|
|
|
|
|
|
|
|
|
820
|
1
|
|
|
|
|
8
|
return \%details; |
|
821
|
|
|
|
|
|
|
} |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
############################################################################### |
|
824
|
|
|
|
|
|
|
# Evil voodoo e-mail submission stuff. |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
827
|
|
|
|
|
|
|
# Return true/false whether the libraries needed to submit discs are |
|
828
|
|
|
|
|
|
|
# present. |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
sub can_submit_disc { |
|
831
|
1
|
|
|
1
|
1
|
997
|
my $self = shift; |
|
832
|
1
|
|
|
|
|
3
|
$imported_mail; |
|
833
|
|
|
|
|
|
|
} |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
836
|
|
|
|
|
|
|
# Build an e-mail address, and return it. Caches the last built |
|
837
|
|
|
|
|
|
|
# address, and returns that on subsequent calls. |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub get_mail_address { |
|
840
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
841
|
1
|
50
|
|
|
|
6
|
return $self->{mail_from} if defined $self->{mail_from}; |
|
842
|
1
|
|
|
|
|
8
|
return $self->{mail_from} = $self->{login} . '@' . $self->{hostname}; |
|
843
|
|
|
|
|
|
|
} |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
846
|
|
|
|
|
|
|
# Build an e-mail host, and return it. Caches the last built e-mail |
|
847
|
|
|
|
|
|
|
# host, and returns that on subsequent calls. |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
sub get_mail_host { |
|
850
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
851
|
|
|
|
|
|
|
|
|
852
|
1
|
50
|
|
|
|
6
|
return $self->{mail_host} if defined $self->{mail_host}; |
|
853
|
|
|
|
|
|
|
|
|
854
|
1
|
50
|
|
|
|
1051
|
if (exists $ENV{SMTPHOSTS}) { |
|
|
|
50
|
|
|
|
|
|
|
855
|
0
|
|
|
|
|
0
|
$self->{mail_host} = $ENV{SMTPHOSTS}; |
|
856
|
|
|
|
|
|
|
} |
|
857
|
|
|
|
|
|
|
elsif (defined inet_aton('mail')) { |
|
858
|
1
|
|
|
|
|
6
|
$self->{mail_host} = 'mail'; |
|
859
|
|
|
|
|
|
|
} |
|
860
|
|
|
|
|
|
|
else { |
|
861
|
0
|
|
|
|
|
0
|
$self->{mail_host} = 'localhost'; |
|
862
|
|
|
|
|
|
|
} |
|
863
|
1
|
|
|
|
|
7
|
return $self->{mail_host}; |
|
864
|
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
# Build a cddbp disc submission and try to e-mail it. |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
sub submit_disc { |
|
869
|
1
|
|
|
1
|
1
|
13
|
my $self = shift; |
|
870
|
1
|
|
|
|
|
10
|
my %params = @_; |
|
871
|
|
|
|
|
|
|
|
|
872
|
1
|
50
|
|
|
|
8
|
croak( |
|
873
|
|
|
|
|
|
|
"submit_disc needs Mail::Internet, Mail::Header, and MIME::QuotedPrint" |
|
874
|
|
|
|
|
|
|
) unless $imported_mail; |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
# Try yet again to fetch the hostname. Fail if we cannot. |
|
877
|
1
|
50
|
|
|
|
14
|
unless (defined $self->{hostname}) { |
|
878
|
0
|
0
|
|
|
|
0
|
$self->{hostname} = &hostname() or croak "can't get hostname: $!"; |
|
879
|
|
|
|
|
|
|
} |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# Validate the required submission fields. XXX Duplicated code. |
|
882
|
1
|
50
|
|
|
|
88
|
(exists $params{Genre}) or croak "submit_disc needs a Genre"; |
|
883
|
1
|
50
|
|
|
|
7
|
(exists $params{Id}) or croak "submit_disc needs an Id"; |
|
884
|
1
|
50
|
|
|
|
14
|
(exists $params{Artist}) or croak "submit_disc needs an Artist"; |
|
885
|
1
|
50
|
|
|
|
6
|
(exists $params{DiscTitle}) or croak "submit_disc needs a DiscTitle"; |
|
886
|
1
|
50
|
|
|
|
4
|
(exists $params{TrackTitles}) or croak "submit_disc needs TrackTitles"; |
|
887
|
1
|
50
|
|
|
|
6
|
(exists $params{Offsets}) or croak "submit_disc needs Offsets"; |
|
888
|
1
|
50
|
|
|
|
5
|
(exists $params{Revision}) or croak "submit_disc needs a Revision"; |
|
889
|
1
|
50
|
|
|
|
6
|
if (exists $params{Year}) { |
|
890
|
0
|
0
|
|
|
|
0
|
unless ($params{Year} =~ /^\d{4}$/) { |
|
891
|
0
|
|
|
|
|
0
|
croak "submit_disc needs a 4 digit year"; |
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
} |
|
894
|
1
|
50
|
|
|
|
5
|
if (exists $params{GenreLong}) { |
|
895
|
0
|
0
|
|
|
|
0
|
unless ($params{GenreLong} =~ /^([A-Z][a-zA-Z0-9]*\s?)+$/) { |
|
896
|
0
|
|
|
|
|
0
|
croak( |
|
897
|
|
|
|
|
|
|
"GenreLong must start with a capital letter and contain only " . |
|
898
|
|
|
|
|
|
|
"letters and numbers" |
|
899
|
|
|
|
|
|
|
); |
|
900
|
|
|
|
|
|
|
} |
|
901
|
|
|
|
|
|
|
} |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
# Try to find a mail host. We could probably grab the MX record for |
|
904
|
|
|
|
|
|
|
# the current machine, but that would require yet more strange |
|
905
|
|
|
|
|
|
|
# modules. TODO: Use Net::DNS if it's available (why not?) and just |
|
906
|
|
|
|
|
|
|
# bypass it if it isn't installed. |
|
907
|
|
|
|
|
|
|
|
|
908
|
1
|
50
|
|
|
|
6
|
$self->{mail_host} = $params{Host} if exists $params{Host}; |
|
909
|
1
|
|
|
|
|
5
|
my $host = $self->get_mail_host(); |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
# Override the sender's e-mail address with whatever was specified |
|
912
|
|
|
|
|
|
|
# during the object's constructor call. |
|
913
|
1
|
50
|
|
|
|
7
|
$self->{mail_from} = $params{From} if exists $params{From}; |
|
914
|
1
|
|
|
|
|
7
|
my $from = $self->get_mail_address(); |
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
# Build the submission's headers. |
|
917
|
1
|
|
|
|
|
242
|
my $header = new Mail::Header; |
|
918
|
1
|
|
|
|
|
824
|
$header->add( 'MIME-Version' => '1.0' ); |
|
919
|
1
|
50
|
|
|
|
684
|
my $charset = $self->{'utf8'} ? 'utf-8' : 'iso-8859-1'; |
|
920
|
1
|
|
|
|
|
9
|
$header->add( 'Content-Type' => "text/plain; charset=$charset" ); |
|
921
|
1
|
|
|
|
|
181
|
$header->add( 'Content-Disposition' => 'inline' ); |
|
922
|
1
|
|
|
|
|
166
|
$header->add( 'Content-Transfer-Encoding' => 'quoted-printable' ); |
|
923
|
1
|
|
|
|
|
154
|
$header->add( From => $from ); |
|
924
|
1
|
|
|
|
|
2493
|
$header->add( To => $self->{cddbmail} ); |
|
925
|
|
|
|
|
|
|
# send a copy to ourselves if we are debugging |
|
926
|
1
|
50
|
|
|
|
133
|
$header->add( Cc => $from ) if $self->{debug}; |
|
927
|
1
|
|
|
|
|
9
|
$header->add( Subject => "cddb $params{Genre} $params{Id}" ); |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
# Build the submission's body. |
|
930
|
1
|
|
|
|
|
26
|
my @message_body = ( |
|
931
|
|
|
|
|
|
|
'# xmcd', |
|
932
|
|
|
|
|
|
|
'#', |
|
933
|
|
|
|
|
|
|
'# Track frame offsets:', |
|
934
|
1
|
|
|
|
|
128
|
map({ "#\t" . $_; } @{$params{Offsets}}), |
|
|
1
|
|
|
|
|
221
|
|
|
935
|
|
|
|
|
|
|
'#', |
|
936
|
|
|
|
|
|
|
'# Disc length: ' . (hex(substr($params{Id},2,4))+2) . ' seconds', |
|
937
|
|
|
|
|
|
|
'#', |
|
938
|
|
|
|
|
|
|
"# Revision: " . $params{Revision}, |
|
939
|
|
|
|
|
|
|
'# Submitted via: ' . $self->{libname} . ' ' . $self->{libver}, |
|
940
|
|
|
|
|
|
|
'#', |
|
941
|
|
|
|
|
|
|
'DISCID=' . $params{Id}, |
|
942
|
|
|
|
|
|
|
'DTITLE=' . $params{Artist} . ' / ' . $params{DiscTitle}, |
|
943
|
|
|
|
|
|
|
); |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
# add year and genre |
|
946
|
1
|
50
|
|
|
|
7
|
if (exists $params{Year}) { |
|
947
|
0
|
|
|
|
|
0
|
push @message_body, 'DYEAR='.$params{Year}; |
|
948
|
|
|
|
|
|
|
} |
|
949
|
1
|
50
|
|
|
|
6
|
if (exists $params{GenreLong}) { |
|
950
|
0
|
|
|
|
|
0
|
push @message_body, 'DGENRE='.$params{GenreLong}; |
|
951
|
|
|
|
|
|
|
} |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
# Dump the track titles. |
|
954
|
1
|
|
|
|
|
3
|
my $number = 0; |
|
955
|
1
|
|
|
|
|
4
|
foreach my $title (@{$params{TrackTitles}}) { |
|
|
1
|
|
|
|
|
29
|
|
|
956
|
1
|
|
|
|
|
2
|
my $copy = $title; |
|
957
|
1
|
|
|
|
|
7
|
while ($copy ne '') { |
|
958
|
1
|
|
|
|
|
8
|
push( @message_body, 'TTITLE' . $number . '=' . substr($copy, 0, 69)); |
|
959
|
1
|
|
|
|
|
7
|
substr($copy, 0, 69) = ''; |
|
960
|
|
|
|
|
|
|
} |
|
961
|
1
|
|
|
|
|
4
|
$number++; |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
# Dump extended information. |
|
965
|
1
|
|
|
|
|
5
|
push @message_body, 'EXTD='; |
|
966
|
1
|
|
|
|
|
5
|
push @message_body, map { "EXTT$_="; } (0..--$number); |
|
|
1
|
|
|
|
|
5
|
|
|
967
|
1
|
|
|
|
|
4
|
push @message_body, 'PLAYORDER='; |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
# Translate the message body to quoted printable. TODO: How can I |
|
970
|
|
|
|
|
|
|
# ensure that the quoted printable characters are within ISO-8859-1? |
|
971
|
|
|
|
|
|
|
# The cddbp submissions daemon will barf if it's not. |
|
972
|
1
|
|
|
|
|
3
|
foreach my $line (@message_body) { |
|
973
|
16
|
|
|
|
|
495
|
$line .= "\n"; |
|
974
|
16
|
|
|
|
|
45
|
$line = MIME::QuotedPrint::encode_qp(encode('utf8', $line)); |
|
975
|
|
|
|
|
|
|
} |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# Bundle the headers and body into an Internet mail. |
|
978
|
1
|
|
|
|
|
37
|
my $mail = new Mail::Internet( |
|
979
|
|
|
|
|
|
|
undef, |
|
980
|
|
|
|
|
|
|
Header => $header, |
|
981
|
|
|
|
|
|
|
Body => \@message_body, |
|
982
|
|
|
|
|
|
|
); |
|
983
|
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
# Try to send it using the "mail" utility. This is commented out: |
|
985
|
|
|
|
|
|
|
# it strips the MIME headers from the message, invalidating the |
|
986
|
|
|
|
|
|
|
# submission. |
|
987
|
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
#eval { |
|
989
|
|
|
|
|
|
|
# die unless $mail->send( 'mail' ); |
|
990
|
|
|
|
|
|
|
#}; |
|
991
|
|
|
|
|
|
|
#return 1 unless $@; |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
# Try to send it using "sendmail". |
|
994
|
1
|
|
|
|
|
93
|
eval { |
|
995
|
1
|
0
|
|
|
|
68
|
die unless $mail->send( 'sendmail' ); |
|
996
|
|
|
|
|
|
|
}; |
|
997
|
1
|
50
|
|
|
|
64814
|
return 1 unless $@; |
|
998
|
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
# Try to send it by making a direct SMTP connection. |
|
1000
|
1
|
|
|
|
|
2
|
eval { |
|
1001
|
1
|
0
|
|
|
|
8
|
die unless $mail->send( smtp => Server => $host ); |
|
1002
|
|
|
|
|
|
|
}; |
|
1003
|
1
|
50
|
|
|
|
10627
|
return 1 unless $@; |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
# Augh! Everything failed! |
|
1006
|
1
|
|
|
|
|
321
|
$self->debug_print( 0, '--- could not find a way to submit a disc' ); |
|
1007
|
1
|
|
|
|
|
33
|
return; |
|
1008
|
|
|
|
|
|
|
} |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
1; |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
__END__ |