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