1 /*------------------------------------------------------------------------------
2
3 Copyright (c) 2000-2007 Tyrell Corporation. All rights reserved.
4
5 Tyrell DarkIce
6
7 File : ShoutCast.cpp
8 Version : $Revision$
9 Author : $Author$
10 Location : $Source$
11
12 Copyright notice:
13
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 as published by the Free Software Foundation; either version 3
17 of the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27
28 ------------------------------------------------------------------------------*/
29
30 /* ============================================================ include files */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #ifdef HAVE_STDIO_H
37 #include <stdio.h>
38 #else
39 #error need stdio.h
40 #endif
41
42 #ifdef HAVE_STRING_H
43 #include <string.h>
44 #else
45 #error need string.h
46 #endif
47
48 #ifdef HAVE_MATH_H
49 #include <math.h>
50 #else
51 #error need math.h
52 #endif
53
54 #include <sstream>
55
56
57 #include "Exception.h"
58 #include "Source.h"
59 #include "Sink.h"
60 #include "Util.h"
61 #include "ShoutCast.h"
62
63
64 /* =================================================== local data structures */
65
66
67 /* ================================================ local constants & macros */
68
69 /*------------------------------------------------------------------------------
70 * File identity
71 *----------------------------------------------------------------------------*/
72 static const char fileid[] = "$Id$";
73
74
75 /*------------------------------------------------------------------------------
76 * Size of string conversion buffer
77 *----------------------------------------------------------------------------*/
78 #define STRBUF_SIZE 32
79 #define HEADERLINE_LENGTH 50
80
81 /* =============================================== local function prototypes */
82
83
84 /* ============================================================= module code */
85
86 /*------------------------------------------------------------------------------
87 * Initialize the object
88 *----------------------------------------------------------------------------*/
89 void
init(const char * irc,const char * aim,const char * icq,const char * mountPoint)90 ShoutCast :: init ( const char * irc,
91 const char * aim,
92 const char * icq,
93 const char * mountPoint )
94 throw ( Exception )
95 {
96 this->irc = irc ? Util::strDup( irc) : 0;
97 this->aim = aim ? Util::strDup( aim) : 0;
98 this->icq = icq ? Util::strDup( icq) : 0;
99 this->mountPoint = mountPoint ? Util::strDup( mountPoint ) : 0;
100 }
101
102
103 /*------------------------------------------------------------------------------
104 * De-initialize the object
105 *----------------------------------------------------------------------------*/
106 void
strip(void)107 ShoutCast :: strip ( void ) throw ( Exception )
108 {
109 if ( irc ) {
110 delete[] irc;
111 }
112 if ( aim ) {
113 delete[] aim;
114 }
115 if ( icq ) {
116 delete[] icq;
117 }
118 if (mountPoint ){
119 delete[] mountPoint;
120 }
121 }
122
123
124 /*------------------------------------------------------------------------------
125 * Log in to the ShoutCast server using the icy login scheme
126 *----------------------------------------------------------------------------*/
127 bool
sendLogin(void)128 ShoutCast :: sendLogin ( void ) throw ( Exception )
129 {
130 Sink * sink = getSink();
131 Source * source = getSocket();
132 const char * str;
133 char resp[STRBUF_SIZE];
134 unsigned int len;
135 bool needsMountPoint = false;
136 const char * mountPoint = getMountPoint();
137 char header_line[HEADERLINE_LENGTH+2]; // ... + \n + \0
138
139 if ( !source->isOpen() ) {
140 return false;
141 }
142 if ( !sink->isOpen() ) {
143 return false;
144 }
145
146 // We will add SOURCE only if really needed: if the mountPoint is not null
147 // and is different of "/". This is to keep maximum compatibility with
148 // NullSoft Shoutcast server.
149 if (mountPoint != 0L
150 && strlen(mountPoint) > 0 && 0 != strcmp("/", mountPoint)) {
151 needsMountPoint = true;
152 }
153
154 std::ostringstream os;
155
156 if (needsMountPoint) {
157 os << "SOURCE ";
158 }
159
160 /* first line is the password in itself */
161 os << getPassword();
162 os << "\n";
163
164 // send the mount point
165 if (needsMountPoint) {
166 os << " ";
167 if (strncmp("/", mountPoint, 1) != 0) {
168 os << "/";
169 }
170 os << mountPoint;
171 os << "\n";
172 }
173
174 str = os.str().c_str();
175
176 // Ok, now we send login which will be different of classical Shoutcast
177 // if mountPoint is not null and is different from "/" ...
178 sink->write( str, strlen( str));
179 sink->flush();
180
181 /* read the anticipated response: "OK" */
182 len = source->read( resp, STRBUF_SIZE);
183 reportEvent(8, "server response length: ", len);
184 reportEvent(8, "server response: ", resp);
185
186 if ( Util::strEq( resp, "invalid password",16) ) {
187 throw Exception( __FILE__, __LINE__,
188 "ShoutCast - wrong password");
189 }
190
191 if ( len < 2 || resp[0] != 'O' || resp[1] != 'K' ) {
192 return false;
193 }
194
195 /* suck anything that the other side has to say */
196 while ( source->canRead( 0, 0) &&
197 (len = source->read( resp, STRBUF_SIZE)) ) {
198 ;
199 }
200
201 /* send the icy headers */
202 if ( getName() ) {
203 snprintf(header_line, HEADERLINE_LENGTH, "icy-name:%s", getName());
204 strcat(header_line, "\n");
205 sink->write( header_line, strlen( header_line));
206 }
207
208 if ( getUrl() ) {
209 snprintf(header_line, HEADERLINE_LENGTH, "icy-url:%s", getUrl());
210 strcat(header_line, "\n");
211 sink->write( header_line, strlen( header_line));
212 }
213
214 if ( getGenre() ) {
215 snprintf(header_line, HEADERLINE_LENGTH, "icy-genre:%s", getGenre());
216 strcat(header_line, "\n");
217 sink->write( header_line, strlen( header_line));
218 }
219
220 if ( getIrc() ) {
221 snprintf(header_line, HEADERLINE_LENGTH, "icy-irc:%s", getIrc());
222 strcat(header_line, "\n");
223 sink->write( header_line, strlen( header_line));
224 }
225
226 if ( getAim() ) {
227 snprintf(header_line, HEADERLINE_LENGTH, "icy-aim:%s", getAim());
228 strcat(header_line, "\n");
229 sink->write( header_line, strlen( header_line));
230 }
231
232 if ( getIcq() ) {
233 snprintf(header_line, HEADERLINE_LENGTH, "icy-icq:%s", getIcq());
234 strcat(header_line, "\n");
235 sink->write( header_line, strlen( header_line));
236 }
237
238 snprintf(header_line, HEADERLINE_LENGTH, "icy-br:%d", getBitRate());
239 strcat(header_line, "\n");
240 sink->write( header_line, strlen( header_line));
241
242 snprintf(header_line, HEADERLINE_LENGTH, "icy-pub:%s", getIsPublic() ? "1" : "0");
243 strcat(header_line, "\n");
244 sink->write( header_line, strlen( header_line));
245
246 str = "\n";
247 sink->write( str, strlen( str));
248 sink->flush();
249
250 return true;
251 }
252
253
254