Updated init script.
[dtbartle/bnbt.git] / client.cpp
1 /***\r
2 *\r
3 * BNBT Beta 8.0 - A C++ BitTorrent Tracker\r
4 * Copyright (C) 2003-2004 Trevor Hogan\r
5 *\r
6 * CBTT variations (C) 2003-2005 Harold Feit\r
7 *\r
8 * This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU Lesser General Public\r
10 * License as published by the Free Software Foundation; either\r
11 * version 2.1 of the License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
16 * Lesser General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU Lesser General Public\r
19 * License along with this library; if not, write to the Free Software\r
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
21 *\r
22 ***/\r
23 \r
24 #include "bnbt.h"\r
25 #include "atom.h"\r
26 #include "base64.h"\r
27 #include "client.h"\r
28 #include "md5.h"\r
29 #include "server.h"\r
30 #include "tracker.h"\r
31 #include "util.h"\r
32 \r
33 #include <zlib.h>\r
34 \r
35 CClient :: CClient( SOCKET sckClient, struct sockaddr_in sinAddress, struct timeval tvTimeOut, int iCompression )\r
36 {\r
37         m_sckClient = sckClient;\r
38 \r
39         m_tvTimeOut = tvTimeOut;\r
40         m_iCompression = iCompression;\r
41 \r
42         rqst.sin = sinAddress;\r
43         rsp.bCompressOK = true;\r
44 \r
45         m_iState = CS_RECEIVING;\r
46 }\r
47 \r
48 CClient :: ~CClient( )\r
49 {\r
50         int rc = closesocket( m_sckClient );\r
51         \r
52         if( rc == SOCKET_ERROR )\r
53                 UTIL_LogPrint( "client error - Closing Socket (%d)\n", GetLastError( ) );\r
54         else\r
55         {\r
56                 if( gbDebug )\r
57                         UTIL_LogPrint( "client - Socket Closed\n" );\r
58         }\r
59 }\r
60 \r
61 void CClient :: StartReceiving( )\r
62 {\r
63         fd_set fdClient;\r
64 \r
65         m_strReceiveBuf.erase( );\r
66         m_strReceiveBuf.reserve( 1024 );\r
67 \r
68         rqst.strMethod.erase( );\r
69         rqst.strURL.erase( );\r
70         rqst.mapParams.clear( );\r
71         rqst.mapHeaders.clear( );\r
72 \r
73         rsp.strCode.erase( );\r
74         rsp.mapHeaders.clear( );\r
75         rsp.strContent.erase( );\r
76         rsp.strContent.reserve( 1024 );\r
77 \r
78         m_bKeepAlive = false;\r
79 \r
80         while( m_strReceiveBuf.find( "\r\n\r\n" ) == string :: npos )\r
81         {\r
82                 if( m_strReceiveBuf.size( ) > giMaxRecvSize )\r
83                 {\r
84                         UTIL_LogPrint( "client error - exceeded max recv size\n" );\r
85 \r
86                         m_iState = CS_DEAD;\r
87                         return;\r
88                 }\r
89 \r
90                 FD_ZERO( &fdClient );\r
91                 FD_SET( m_sckClient, &fdClient );\r
92 \r
93 #ifdef WIN32\r
94                 if( select( 1, &fdClient, NULL, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
95 #else\r
96                 if( select( m_sckClient + 1, &fdClient, NULL, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
97 #endif\r
98                 {\r
99                         UTIL_LogPrint( "client error - select error (error %d)\n", GetLastError( ) );\r
100 \r
101                         m_iState = CS_DEAD;\r
102                         return;\r
103                 }\r
104 \r
105                 if( FD_ISSET( m_sckClient, &fdClient ) )\r
106                 {\r
107                         char pTemp[8192];\r
108 \r
109                         memset( pTemp, 0, sizeof( char ) * 8192 );\r
110 \r
111                         // don't receive more than the buffer size\r
112 \r
113                         int c = recv( m_sckClient, pTemp, 8192, 0 );\r
114 \r
115                         if( c == SOCKET_ERROR )\r
116                         {\r
117                                 if( GetLastError( ) != ECONNRESET )\r
118                                         UTIL_LogPrint( "client error - receive error (error %d)\n", GetLastError( ) );\r
119 \r
120                                 m_iState = CS_DEAD;\r
121                                 return;\r
122                         }\r
123                         else if( c == 0 )\r
124                         {\r
125                                 m_iState = CS_DEAD;\r
126                                 return;\r
127                         }\r
128                         else if( c > 0 )\r
129                                 m_strReceiveBuf += string( pTemp, c );\r
130                         else\r
131                         {\r
132                                 UTIL_LogPrint( "client error - recv returned garbage\n" );\r
133 \r
134                                 m_iState = CS_DEAD;\r
135                                 return;\r
136                         }\r
137                 }\r
138                 else\r
139                 {\r
140                         m_iState = CS_DEAD;\r
141                         return;\r
142                 }\r
143         }\r
144 \r
145         // grab method\r
146 \r
147         string :: size_type iMethodEnd = m_strReceiveBuf.find( " " );\r
148 \r
149         if( iMethodEnd == string :: npos )\r
150         {\r
151                 UTIL_LogPrint( "client error - malformed HTTP request (can't find method)\n" );\r
152 \r
153                 m_iState = CS_DEAD;\r
154                 return;\r
155         }\r
156 \r
157         rqst.strMethod = m_strReceiveBuf.substr( 0, iMethodEnd );\r
158 \r
159         // grab url\r
160 \r
161         string strTemp = m_strReceiveBuf.substr( iMethodEnd + 1 );\r
162 \r
163         string :: size_type iURLEnd = strTemp.find( " " );\r
164 \r
165         if( iURLEnd == string :: npos )\r
166         {\r
167                 UTIL_LogPrint( "client error - malformed HTTP request (can't find URL)\n" );\r
168 \r
169                 m_iState = CS_DEAD;\r
170                 return;\r
171         }\r
172 \r
173         strTemp = strTemp.substr( 0, iURLEnd );\r
174 \r
175         string :: size_type iParamsStart = strTemp.find( "?" );\r
176 \r
177         if( iParamsStart == string :: npos ){\r
178                 rqst.strURL = strTemp;\r
179                 rqst.hasQuery = false;\r
180         }\r
181         else {\r
182         rqst.strURL = strTemp.substr( 0, iParamsStart );\r
183                 rqst.hasQuery = true;\r
184         }\r
185 \r
186         // grab params\r
187         if( iParamsStart != string :: npos )\r
188         {\r
189                 strTemp = strTemp.substr( iParamsStart + 1 );\r
190                 rqst.hasQuery = true;\r
191 \r
192                 while( 1 )\r
193                 {\r
194                         string :: size_type iSplit = strTemp.find( "=" );\r
195                         string :: size_type iEnd = strTemp.find( "&" );\r
196 \r
197                         if ((iSplit == string::npos) && (strTemp.length() == 40)) {\r
198                                 if( gbDebug )\r
199                                         UTIL_LogPrint( "Azureus-style Torrent URL passed\n" );\r
200                                 rqst.mapParams.insert( pair<string, string>( string("info_hash"), strTemp) );\r
201                                 strTemp = strTemp.substr( iEnd + 1, strTemp.size( ) - iEnd - 1 );\r
202                                 if( iEnd == string :: npos )\r
203                                         break;\r
204                                 continue;\r
205             } \r
206                         if( iSplit == string :: npos && strTemp.length() != 40 )\r
207                         {\r
208                                 UTIL_LogPrint( "client warning - malformed HTTP request (found param key without value)\n" );\r
209 \r
210                                 break;\r
211                         }\r
212 \r
213                         string strKey = UTIL_EscapedToString( strTemp.substr( 0, iSplit ) );\r
214                         string strValue = UTIL_EscapedToString( strTemp.substr( iSplit + 1, iEnd - iSplit - 1 ) );\r
215 \r
216                         // multimap for scrape, regular map for everything else\r
217                         if ( rqst.strURL == "/scrape" )\r
218                                 rqst.multiParams.insert( pair<string,string>( strKey, strValue ) );\r
219                         else\r
220                                 rqst.mapParams.insert( pair<string, string>( strKey, strValue ) );\r
221 \r
222                         strTemp = strTemp.substr( iEnd + 1, strTemp.size( ) - iEnd - 1 );\r
223 \r
224                         if( iEnd == string :: npos )\r
225                                 break;\r
226                 }\r
227         }\r
228 \r
229         // grab headers\r
230 \r
231         string :: size_type iNewLine = m_strReceiveBuf.find( "\r\n" );\r
232         string :: size_type iDoubleNewLine = m_strReceiveBuf.find( "\r\n\r\n" );\r
233 \r
234         strTemp = m_strReceiveBuf.substr( iNewLine + strlen( "\r\n" ), iDoubleNewLine - iNewLine - strlen( "\r\n" ) );\r
235 \r
236         while( 1 )\r
237         {\r
238                 string :: size_type iSplit = strTemp.find( ":" );\r
239                 string :: size_type iEnd = strTemp.find( "\r\n" );\r
240 \r
241                 // http://www.addict3d.org/index.php?page=viewarticle&type=security&ID=4861\r
242                 if( iSplit == string :: npos || iSplit == 0 || strTemp.find(": ") == string :: npos )\r
243                 {\r
244                         UTIL_LogPrint( "client warning - malformed HTTP request (bad header)\n" );\r
245                         break;\r
246                 }\r
247 \r
248                 string strKey = strTemp.substr( 0, iSplit );\r
249                 string strValue = strTemp.substr( iSplit + strlen( ": " ), iEnd - iSplit - strlen( "\r\n" ) );\r
250 \r
251                 rqst.mapHeaders.insert( pair<string, string>( strKey, strValue ) );\r
252 \r
253                 strTemp = strTemp.substr( iEnd + strlen( "\r\n" ) );\r
254 \r
255                 if( iEnd == string :: npos )\r
256                         break;\r
257         }\r
258 \r
259         // grab cookies\r
260 \r
261         string strCookies = rqst.mapHeaders["Cookie"];\r
262 \r
263         if( !strCookies.empty( ) )\r
264         {\r
265                 while( 1 )\r
266                 {\r
267                         string :: size_type iWhite = strCookies.find_first_not_of( " " );\r
268 \r
269                         if( iWhite != string :: npos )\r
270                                 strCookies = strCookies.substr( iWhite );\r
271 \r
272                         string :: size_type iSplit = strCookies.find( "=" );\r
273                         string :: size_type iEnd = strCookies.find( ";" );\r
274 \r
275                         if( iSplit == string :: npos )\r
276                         {\r
277                                 UTIL_LogPrint( "client warning - malformed HTTP request (found cookie key without value)\n" );\r
278 \r
279                                 break;\r
280                         }\r
281 \r
282                         string strKey = UTIL_EscapedToString( strCookies.substr( 0, iSplit ) );\r
283                         string strValue = UTIL_EscapedToString( strCookies.substr( iSplit + 1, iEnd - iSplit - 1 ) );\r
284 \r
285                         // strip quotes\r
286 \r
287                         if( strValue.size( ) > 1 && strValue[0] == '"' )\r
288                                 strValue = strValue.substr( 1, strValue.size( ) - 2 );\r
289 \r
290                         rqst.mapCookies.insert( pair<string, string>( strKey, strValue ) );\r
291 \r
292                         strCookies = strCookies.substr( iEnd + 1, strCookies.size( ) - iEnd - 1 );\r
293 \r
294                         if( iEnd == string :: npos )\r
295                                 break;\r
296                 }\r
297         }\r
298 \r
299         // don't grab authentication here\r
300 \r
301         if( rqst.strMethod == "POST" )\r
302         {\r
303                 string strContentLength = rqst.mapHeaders["Content-Length"];\r
304 \r
305                 if( strContentLength.empty( ) )\r
306                 {\r
307                         UTIL_LogPrint( "client error - malformed HTTP request (no Content-Length with POST)\n" );\r
308 \r
309                         m_iState = CS_DEAD;\r
310                         return;\r
311                 }\r
312 \r
313                 long iLength = atol( strContentLength.c_str( ) );\r
314 \r
315                 string :: size_type iContent = iDoubleNewLine + strlen( "\r\n\r\n" );\r
316 \r
317                 while( m_strReceiveBuf.size( ) < iContent + iLength )\r
318                 {\r
319                         if( m_strReceiveBuf.size( ) > giMaxRecvSize )\r
320                         {\r
321                                 UTIL_LogPrint( "client error - exceeded max recv size\n" );\r
322 \r
323                                 m_iState = CS_DEAD;\r
324                                 return;\r
325                         }\r
326 \r
327                         FD_ZERO( &fdClient );\r
328                         FD_SET( m_sckClient, &fdClient );\r
329 \r
330 #ifdef WIN32\r
331                         if( select( 1, &fdClient, NULL, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
332 #else\r
333                         if( select( m_sckClient + 1, &fdClient, NULL, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
334 #endif\r
335                         {\r
336                                 UTIL_LogPrint( "client error - select error (error %d)\n", GetLastError( ) );\r
337 \r
338                                 m_iState = CS_DEAD;\r
339                                 return;\r
340                         }\r
341 \r
342                         if( FD_ISSET( m_sckClient, &fdClient ) )\r
343                         {\r
344                                 char pTemp[8192];\r
345 \r
346                                 memset( pTemp, 0, sizeof( char ) * 8192 );\r
347 \r
348                                 // don't receive more than the buffer size\r
349 \r
350                                 int c = recv( m_sckClient, pTemp, 8192, 0 );\r
351 \r
352                                 if( c == SOCKET_ERROR )\r
353                                 {\r
354                                         if( GetLastError( ) != ECONNRESET )\r
355                                                 UTIL_LogPrint( "client error - receive error (error %d)\n", GetLastError( ) );\r
356 \r
357                                         m_iState = CS_DEAD;\r
358                                         return;\r
359                                 }\r
360                                 else if( c == 0 )\r
361                                 {\r
362                                         m_iState = CS_DEAD;\r
363                                         return;\r
364                                 }\r
365                                 else if( c > 0 )\r
366                                         m_strReceiveBuf += string( pTemp, c );\r
367                                 else\r
368                                 {\r
369                                         UTIL_LogPrint( "client error - recv returned garbage\n" );\r
370 \r
371                                         m_iState = CS_DEAD;\r
372                                         return;\r
373                                 }\r
374                         }\r
375                         else\r
376                         {\r
377                                 m_iState = CS_DEAD;\r
378                                 return;\r
379                         }\r
380                 }\r
381         }\r
382 \r
383         m_iState = CS_WAITING1;\r
384 }\r
385 \r
386 void CClient :: Process( )\r
387 {\r
388         m_iState = CS_PROCESSING;\r
389 \r
390         // grab authentication\r
391 \r
392         string strLogin = rqst.mapCookies["login"];\r
393         string strMD5 = rqst.mapCookies["md5"];\r
394 \r
395         string strAuthorization = rqst.mapHeaders["Authorization"];\r
396 \r
397         if( !strAuthorization.empty( ) )\r
398         {\r
399                 string :: size_type iWhite = strAuthorization.find( " " );\r
400 \r
401                 if( iWhite != string :: npos )\r
402                 {\r
403                         string strType = strAuthorization.substr( 0, iWhite );\r
404                         string strBase64 = strAuthorization.substr( iWhite + 1 );\r
405 \r
406                         if( strType == "Basic" )\r
407                         {\r
408                                 char *szAuth = b64decode( strBase64.c_str( ) );\r
409 \r
410                                 if( szAuth )\r
411                                 {\r
412                                         string strAuth = szAuth;\r
413 \r
414                                         free( szAuth );\r
415 \r
416                                         string :: size_type iSplit = strAuth.find( ":" );\r
417 \r
418                                         if( iSplit != string :: npos )\r
419                                         {\r
420                                                 strLogin = strAuth.substr( 0, iSplit );\r
421                                                 string strPass = strAuth.substr( iSplit + 1 );\r
422 \r
423                                                 // calculate md5 hash of A1\r
424 \r
425                                                 string strA1 = strLogin + ":" + gstrRealm + ":" + strPass;\r
426 \r
427                                                 unsigned char szMD5[16];\r
428 \r
429                                                 MD5_CTX md5;\r
430 \r
431                                                 MD5Init( &md5 );\r
432                                                 MD5Update( &md5, (unsigned char *)strA1.c_str( ), strA1.size( ) );\r
433                                                 MD5Final( szMD5, &md5 );\r
434 \r
435                                                 strMD5 = string( (char *)szMD5, 16 );\r
436                                         }\r
437                                 }\r
438                         }\r
439                 }\r
440         }\r
441 \r
442         user_t user = gpServer->getTracker( )->checkUser( strLogin, strMD5 );\r
443 \r
444         // process\r
445 \r
446         if( rqst.strMethod == "GET" )\r
447                 gpServer->getTracker( )->serverResponseGET( &rqst, &rsp, user );\r
448         else if( rqst.strMethod == "POST" )\r
449         {\r
450                 CAtomList *pPost = UTIL_DecodeHTTPPost( m_strReceiveBuf );\r
451 \r
452                 gpServer->getTracker( )->serverResponsePOST( &rqst, &rsp, pPost, user );\r
453 \r
454                 if( pPost )\r
455                         delete pPost;\r
456 \r
457                 pPost = NULL;\r
458         }\r
459         else\r
460                 rsp.strCode = "400 Bad Request";\r
461 \r
462         // compress\r
463 \r
464         int iCompress = COMPRESS_NONE;\r
465 \r
466         if( rsp.bCompressOK && m_iCompression > 0 )\r
467         {\r
468                 string strAcceptEncoding = UTIL_ToLower( rqst.mapHeaders["Accept-Encoding"] );\r
469 \r
470                 if( strAcceptEncoding.find( "gzip" ) != string :: npos )\r
471                         iCompress = COMPRESS_GZIP;\r
472                 else if( strAcceptEncoding.find( "deflate" ) != string :: npos )\r
473                         iCompress = COMPRESS_DEFLATE;\r
474         }\r
475 \r
476         if( !rsp.strContent.empty( ) && iCompress != COMPRESS_NONE )\r
477         {\r
478                 // allocate avail_in * 1.001 + 18 bytes (12 + 6 for gzip)\r
479 \r
480                 unsigned int iSize = (unsigned int)( rsp.strContent.size( ) * 1.001 + 18 );\r
481 \r
482                 unsigned char *pBuf = new unsigned char[iSize];\r
483                 memset( pBuf, 0, sizeof( unsigned char ) * iSize );\r
484 \r
485                 z_stream_s zCompress;\r
486 \r
487                 zCompress.next_in = (unsigned char *)rsp.strContent.c_str( );\r
488                 zCompress.avail_in = rsp.strContent.size( );\r
489                 zCompress.next_out = pBuf;\r
490                 zCompress.avail_out = iSize;\r
491                 zCompress.zalloc = (alloc_func)0;\r
492                 zCompress.zfree = (free_func)0;\r
493                 zCompress.opaque = (voidpf)0;\r
494                 zCompress.total_in = 0;\r
495                 zCompress.total_out = 0;\r
496 \r
497                 int windowBits;\r
498 \r
499                 if( iCompress == COMPRESS_GZIP )\r
500                         windowBits = 31;\r
501                 else\r
502                         windowBits = 15;\r
503 \r
504                 int iResult = deflateInit2( &zCompress, m_iCompression, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY );\r
505 \r
506                 if( iResult == Z_OK )\r
507                 {\r
508                         iResult = deflate( &zCompress, Z_FINISH );\r
509 \r
510                         if( iResult == Z_STREAM_END )\r
511                         {\r
512                                 if( gbDebug )\r
513                                         UTIL_LogPrint( "client - (zlib) compressed %u bytes to %u bytes\n", zCompress.total_in, zCompress.total_out );\r
514 \r
515                                 if( iCompress == COMPRESS_GZIP )\r
516                                         rsp.mapHeaders.insert( pair<string, string>( "Content-Encoding", "gzip" ) );\r
517                                 else\r
518                                         rsp.mapHeaders.insert( pair<string, string>( "Content-Encoding", "deflate" ) );\r
519 \r
520                                 rsp.strContent = string( (char *)pBuf, zCompress.total_out );\r
521 \r
522                                 deflateEnd( &zCompress );\r
523 \r
524                                 delete [] pBuf;\r
525                         }\r
526                         else\r
527                         {\r
528                                 if( iResult != Z_OK )\r
529                                         UTIL_LogPrint( "client warning - (zlib) deflate error (%d) on \"%s\", in = %u, sending raw\n", iResult, rqst.strURL.c_str( ), rsp.strContent.size( ) );\r
530 \r
531                                 deflateEnd( &zCompress );\r
532 \r
533                                 delete [] pBuf;\r
534                         }\r
535                 }\r
536                 else\r
537                 {\r
538                         UTIL_LogPrint( "client warning - (zlib) deflateInit2 error (%d), sending raw\n", iResult );\r
539 \r
540                         delete [] pBuf;\r
541                 }\r
542         }\r
543 \r
544         // keep alive\r
545 \r
546         if( UTIL_ToLower( rqst.mapHeaders["Connection"] ) == "keep-alive" )\r
547         {\r
548                 if( !gpServer->isDying( ) )\r
549                 {\r
550                         m_bKeepAlive = true;\r
551 \r
552                         rsp.mapHeaders.insert( pair<string, string>( "Connection", "Keep-Alive" ) );\r
553                         rsp.mapHeaders.insert( pair<string, string>( "Keep-Alive", CAtomInt( m_tvTimeOut.tv_sec - 1 ).toString( ) ) );\r
554                 }\r
555                 else\r
556                 {\r
557                         m_bKeepAlive = false;\r
558 \r
559                         rsp.mapHeaders.insert( pair<string, string>( "Connection", "Close" ) );\r
560                         rsp.mapHeaders.insert( pair<string, string>( "Keep-Alive", CAtomInt( m_tvTimeOut.tv_sec - 1 ).toString( ) ) );\r
561                 }\r
562         }\r
563 \r
564         rsp.mapHeaders.insert( pair<string, string>( "Content-Length", CAtomLong( rsp.strContent.size( ) ).toString( ) ) );\r
565 \r
566         // access log\r
567 \r
568         string strRequest;\r
569 \r
570         int iNewLine = m_strReceiveBuf.find( "\r\n" );\r
571 \r
572         if( iNewLine != string :: npos )\r
573                 strRequest = m_strReceiveBuf.substr( 0, iNewLine );\r
574 \r
575         UTIL_AccessLogPrint( inet_ntoa( rqst.sin.sin_addr ), user.strLogin, strRequest, atoi( rsp.strCode.substr( 0, 3 ).c_str( ) ), rsp.strContent.size( ) );\r
576 \r
577         m_iState = CS_WAITING2;\r
578 }\r
579 \r
580 void CClient :: StartSending( )\r
581 {\r
582         fd_set fdClient;\r
583 \r
584         // compose send buffer\r
585 \r
586         string strSendBuf;\r
587         strSendBuf.reserve( 1024 );\r
588 \r
589         strSendBuf += "HTTP/1.1 " + rsp.strCode + "\r\n";\r
590 \r
591         for( multimap<string, string> :: iterator i = rsp.mapHeaders.begin( ); i != rsp.mapHeaders.end( ); i++ )\r
592                 strSendBuf += (*i).first + ": " + (*i).second + "\r\n";\r
593 \r
594         strSendBuf += "\r\n";\r
595         strSendBuf += rsp.strContent;\r
596 \r
597         // send\r
598 \r
599         while( !strSendBuf.empty( ) )\r
600         {\r
601                 FD_ZERO( &fdClient );\r
602                 FD_SET( m_sckClient, &fdClient );\r
603 \r
604 #ifdef WIN32\r
605                 if( select( 1, NULL, &fdClient, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
606 #else\r
607                 if( select( m_sckClient + 1, NULL, &fdClient, NULL, &m_tvTimeOut ) == SOCKET_ERROR )\r
608 #endif\r
609                 {\r
610                         UTIL_LogPrint( "client error - select error (error %d)\n", GetLastError( ) );\r
611 \r
612                         m_iState = CS_DEAD;\r
613                         return;\r
614                 }\r
615 \r
616                 if( FD_ISSET( m_sckClient, &fdClient ) )\r
617                 {\r
618                         int s = send( m_sckClient, strSendBuf.c_str( ), strSendBuf.size( ), MSG_NOSIGNAL );\r
619 \r
620                         if( s == SOCKET_ERROR )\r
621                         {\r
622                                 UTIL_LogPrint( "client error - send error (error %d)\n", GetLastError( ) );\r
623 \r
624                                 m_iState = CS_DEAD;\r
625                                 return;\r
626                         }\r
627                         else if( s == 0 )\r
628                         {\r
629                                 UTIL_LogPrint( "client error - send returned 0\n" );\r
630 \r
631                                 m_iState = CS_DEAD;\r
632                                 return;\r
633                         }\r
634                         else if( s > 0 )\r
635                                 strSendBuf = strSendBuf.substr( s );\r
636                         else\r
637                         {\r
638                                 UTIL_LogPrint( "client error - send returned garbage\n" );\r
639 \r
640                                 m_iState = CS_DEAD;\r
641                                 return;\r
642                         }\r
643                 }\r
644                 else\r
645                 {\r
646                         m_iState = CS_DEAD;\r
647                         return;\r
648                 }\r
649         }\r
650 \r
651         if( m_bKeepAlive )\r
652         {\r
653                 m_iState = CS_RECEIVING;\r
654 \r
655                 StartReceiving( );\r
656         }\r
657         else\r
658                 m_iState = CS_DEAD;\r
659 }\r
660 \r
661 void StartReceiving( CClient *pClient )\r
662 {\r
663         if( pClient )\r
664                 pClient->StartReceiving( );\r
665 }\r
666 \r
667 void StartSending( CClient *pClient )\r
668 {\r
669         if( pClient )\r
670                 pClient->StartSending( );\r
671 }\r