Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
vsnet_dloadmgr.cpp
Go to the documentation of this file.
1 #include "config.h"
2 
3 #include "vsfilesystem.h"
4 #include "vs_globals.h"
12 
13 
14 extern void getZoneInfoBuffer( unsigned short zoneid, NetBuffer &netbuf );
15 
16 #ifndef HAVE_ACCESS
17  #ifdef R_OK
18  #undef R_OK
19  #endif
20  #define R_OK 1
21 
22  #ifdef W_OK
23  #undef W_OK
24  #endif
25  #define W_OK 2
26 
27 extern "C" int vs_access( const char *name, int mode );
28 #else
29 
30 //Make it use the real access().
31 #define vs_access access
32 
33 #endif
34 
35 #include <sys/stat.h>
36 #ifdef _WIN32
37 #define HAVE_LSTAT
38 #define lstat _stat
39 #define stat _stat
40 #endif
41 
42 #ifndef HAVE_LSTAT
43 struct stat
44 {
45  size_t st_size;
46 };
47 
48 extern "C" int lstat( const char *name, struct stat *buf );
49 #endif
50 
51 namespace VsnetDownload
52 {
53 namespace Client
54 {
55 /*------------------------------------------------------------*
56 * definition VsnetDownload::Client::Manager
57 *------------------------------------------------------------*/
58 
59 Manager::Manager( SocketSet &sets, const char **local_search_paths ) :
60  _set( sets )
61 {
62  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
63 
64  const char **c = local_search_paths;
65  while (*c != NULL) {
66  _local_search_paths.push_back( *c );
67  c++;
68  }
69 }
70 
72  _set( sets )
73 {
74  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
75 
76  _local_search_paths.push_back( VSFileSystem::datadir );
77 }
78 
79 void Manager::addItem( Item *item )
80 {
81  list< Item* >l;
82  l.push_back( item );
83  addItems( l );
84 }
85 
86 void Manager::addItems( list< Item* > &items )
87 {
88  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
89 
90  list< Item* >::iterator it;
91 
92  _pending_mx.lock();
93  for (it = items.begin(); it != items.end(); it++) {
94  Item *item = (*it);
95  if (item->getFilename() != "") {
96  COUT<<"adding item: "<<item->getFilename()<<endl;
97  _pending.push( item );
98  }
99  }
100  _pending_mx.unlock();
101  for (it = items.begin(); it != items.end(); it++) {
102  Item *item = (*it);
103  if (item->getFilename() == "")
105  else
106  item->changeState( Queued );
107  }
108  _set.wakeup();
109 }
110 
112 {
113  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
114 
115  char c = buffer.getChar();
116  Subcommand sc = (Subcommand) c;
117  switch (sc)
118  {
119  case ResolveResponse:
120  private_eval_resolve_response( sock, buffer );
121  break;
122  case DownloadError:
123  private_eval_download_error( sock, buffer );
124  break;
125  case Download:
127  case DownloadFragment:
129  private_eval_download( sock, buffer, sc );
130  break;
131  case DownloadRequest:
133  case ResolveRequest:
134  default:
135  COUT<<"unexpected subcommand "<<sc<<", ignoring"<<endl;
136  break;
137  }
138 }
139 
141 {
142  private_lower_poll();
143 }
144 
145 void Manager::private_lower_poll()
146 {
147  _pending_mx.lock();
148  if ( _pending.empty() ) {
149  _pending_mx.unlock();
150  return;
151  }
152  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
153 
154  ItemListMap collect;
155  while (_pending.empty() == false) {
156  Item *i = _pending.front();
157  _pending.pop();
158 
159  _pending_mx.unlock();
160  if ( private_lower_test_access( i ) ) {
161  i->changeState( Completed, Ok );
162  } else if (i->getSock().valid() == false) {
164  } else {
165  i->changeState( Resolving );
166  collect[i->getSock()].push_back( i );
167  _asked[i->getSock()].insert( ItemPair( i->getFilename(), i ) );
168  }
169  _pending_mx.lock();
170  }
171  _pending_mx.unlock();
172 
173  ItemListMap_I it;
174  for (it = collect.begin(); it != collect.end(); it++) {
175  ItemList *cl = &it->second;
176  if (cl->empty() == false) {
177  NetBuffer netbuf;
178  netbuf.addChar( (char) ResolveRequest );
179 
180  assert( cl->size() < 0xffff );
181  netbuf.addShort( cl->size() );
182  for (ItemList_I strit = cl->begin(); strit != cl->end(); strit++) {
183  netbuf.addChar( (*strit)->getFileType() );
184  netbuf.addString( (*strit)->getFilename() );
185  }
186  Packet packet;
187  packet.send( CMD_DOWNLOAD, 0,
188  netbuf.getData(), netbuf.getDataLength(),
190  NULL, it->first,
191  __FILE__, PSEUDO__LINE__( 258 ) );
192  }
193  }
194 }
195 
196 void Manager::private_eval_resolve_response( SOCKETALT sock, NetBuffer &respbuffer )
197 {
198  ItemMapMap_I it = _asked.find( sock );
199  if ( it == _asked.end() ) {
200  COUT<<"received resolve response for unasked socket "<<sock<<endl;
201  } else {
202  ItemList requestlist;
203  ItemList_I rli;
204  ItemMap *cl = &it->second;
205  short num;
206  for (num = respbuffer.getShort(); num > 0; num--) {
207  string filename = respbuffer.getString();
208  char ok = respbuffer.getChar();
209 
210  ItemMap_I mapi = cl->find( filename );
211  if ( mapi != cl->end() ) {
212  if (ok == 1) {
213  COUT<<"file "<<filename<<" found on server"<<endl;
214  mapi->second->changeState( Resolved, Ok );
215  requestlist.push_back( mapi->second );
216  } else {
217  COUT<<"file "<<filename<<" not found on server"<<endl;
218  mapi->second->changeState( Completed, FileNotFound );
219  cl->erase( mapi );
220  }
221  } else {
222  COUT<<"file "<<filename<<" not in request list any more"<<endl;
223  }
224  }
225  if (requestlist.empty() == false) {
226  short num = requestlist.size();
227 
228  NetBuffer reqbuffer;
229  reqbuffer.addChar( DownloadRequest );
230  reqbuffer.addShort( num );
231  for (rli = requestlist.begin(); rli != requestlist.end(); rli++) {
232  reqbuffer.addChar( (*rli)->getFileType() );
233  reqbuffer.addString( (*rli)->getFilename() );
234  (*rli)->changeState( Requested, Ok );
235  }
236  Packet packet;
237  packet.send( CMD_DOWNLOAD, 0,
238  reqbuffer.getData(), reqbuffer.getDataLength(),
240  NULL, sock,
241  __FILE__, PSEUDO__LINE__( 462 ) );
242  }
243  }
244 }
245 
246 void Manager::private_eval_download_error( SOCKETALT sock, NetBuffer &respbuffer )
247 {
248  ItemMapMap_I it = _asked.find( sock );
249  if ( it == _asked.end() ) {
250  COUT<<"received resolve response for unasked socket "<<sock<<endl;
251  } else {
252  string filename = respbuffer.getString();
253  ItemMap *cl = &it->second;
254  ItemMap_I mapi = cl->find( filename );
255  if ( mapi != cl->end() ) {
256  COUT<<"file "<<filename<<" not found on server"<<endl;
257  mapi->second->changeState( Completed, FileNotFound );
258  cl->erase( mapi );
259  } else {
260  COUT<<"file "<<filename<<" not in request list any more"<<endl;
261  }
262  }
263 }
264 
265 void Manager::private_eval_download( SOCKETALT sock, NetBuffer &buffer, Subcommand sc )
266 {
267  ItemMapMap_I it = _asked.find( sock );
268  if ( it == _asked.end() ) {
269  COUT<<"received resolve response for unasked socket "<<sock<<endl;
270  } else {
271  COUT<<"got data in a "<<sc<<endl;
272  switch (sc)
273  {
274  case Download:
275  {
276  string filename = buffer.getString();
277  ItemMap *cl = &it->second;
278  ItemMap_I mapi = cl->find( filename );
279  if ( mapi != cl->end() ) {
280  ItemSockMap_I smi = _currentItems.find( sock );
281  if ( smi != _currentItems.end() ) {
282  smi->second->changeState( Completed, DownloadInterrupted );
283  _currentItems.erase( smi );
284  }
285  short sz = buffer.getShort();
286  mapi->second->setSize( sz );
287  mapi->second->append( buffer.getBuffer( sz ), sz );
288  mapi->second->changeState( Completed, Ok );
289 
290  cl->erase( mapi ); //mapi is now bad!
291  } else {
292  COUT<<"file "<<filename<<" not in request list any more"<<endl;
293  return;
294  }
295  break;
296  }
298  {
299  string filename = buffer.getString();
300  ItemMap *cl = &it->second;
301  ItemMap_I mapi = cl->find( filename );
302  if ( mapi != cl->end() ) {
303  ItemSockMap_I smi = _currentItems.find( sock );
304  if ( smi != _currentItems.end() ) {
305  smi->second->changeState( Completed, DownloadInterrupted );
306  _currentItems.erase( smi );
307  }
308  _currentItems.insert( ItemSockPair( sock, mapi->second ) );
309 
310  int len = buffer.getInt32();
311  short sz = buffer.getShort();
312  mapi->second->setSize( len );
313  mapi->second->append( buffer.getBuffer( sz ), sz );
314  mapi->second->changeState( FragmentReceived, Ok );
315 
316  cl->erase( mapi );
317  } else {
318  COUT<<"file "<<filename<<" not in request list any more"<<endl;
319  return;
320  }
321  break;
322  }
323  case DownloadFragment:
325  {
326  ItemSockMap_I smi = _currentItems.find( sock );
327  if ( smi != _currentItems.end() ) {
328  short sz = buffer.getShort();
329  smi->second->append( buffer.getBuffer( sz ), sz );
330  if (sc == DownloadFragment) {
331  smi->second->changeState( FragmentReceived, Ok );
332  } else {
333  smi->second->changeState( Completed, Ok );
334  _currentItems.erase( smi );
335  }
336  } else {
337  COUT<<"no current item for this socket"<<endl;
338  return;
339  }
340  break;
341  }
342  default:
343  COUT<<"programming error"<<endl;
344  break;
345  }
346  }
347 }
348 
349 bool Manager::private_lower_test_access( Item *i )
350 {
351  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
352 
353  string file( i->getFilename() );
354  for (vector< string >::const_iterator it = _local_search_paths.begin();
355  it != _local_search_paths.end();
356  it++) {
357  string path = *it+"/"+file;
358  if (::vs_access( path.c_str(), R_OK ) == 0) {
359  COUT<<"Found local file "<<path.c_str()<<endl;
360  return true;
361  }
362  }
363  return false;
364 }
365 }; //namespace Client
366 
367 namespace Server
368 {
369 /*------------------------------------------------------------*
370 * definition VsnetDownload::Server::DownloadItem
371 *------------------------------------------------------------*/
372 
373 DownloadItem::DownloadItem( SOCKETALT sock, bool error, const string &file ) :
374  _sock( sock )
375  , _error( error )
376  , _file( file )
377 {}
378 
380 {}
381 
383 {
384  return _sock;
385 }
386 
388 {
389  return _error;
390 }
391 
392 string DownloadItem::file() const
393 {
394  return _file;
395 }
396 
397 /*------------------------------------------------------------*
398 * definition VsnetDownload::Server::DownloadItemFile
399 *------------------------------------------------------------*/
400 
401 DownloadItemFile::DownloadItemFile( SOCKETALT sock, const string &file ) :
402  DownloadItem( sock, true, file )
403  , _handle( NULL )
404  , _size( 0 )
405  , _offset( 0 )
406 {}
407 
408 DownloadItemFile::DownloadItemFile( SOCKETALT sock, const string &file, VSFileSystem::VSFile *f, size_t sz ) :
409  DownloadItem( sock, false, file )
410  , _handle( f )
411  , _size( sz )
412  , _offset( 0 )
413 {}
414 
416 {
417  if (_handle) delete _handle;
418 }
419 
421 {
422  return _offset;
423 }
424 
426 {
427  return _size-_offset;
428 }
429 
430 void DownloadItemFile::copyFromFile( unsigned char *buf, size_t sz )
431 {
432  if (_handle) {
433  _handle->Read( (char*) buf, sz );
434  _offset += sz;
435  }
436 }
437 
438 /*------------------------------------------------------------*
439 * definition VsnetDownload::Server::DownloadItemBuf
440 *------------------------------------------------------------*/
441 
442 DownloadItemBuf::DownloadItemBuf( SOCKETALT sock, const string &file, const char *buf, size_t sz ) :
443  DownloadItem( sock, false, file )
444  , _size( sz )
445  , _offset( 0 )
446 {
447  _buf = new char[sz];
448  VsnetOSS::memcpy( _buf, buf, sz );
449 }
450 
452 {
453  if (_buf) delete[] _buf;
454 }
455 
457 {
458  return _offset;
459 }
460 
462 {
463  return _size-_offset;
464 }
465 
466 void DownloadItemBuf::copyFromFile( unsigned char *buf, size_t sz )
467 {
468  if (remainingSize() < sz) sz = remainingSize();
469  if (sz > 0) {
470  VsnetOSS::memcpy( buf, &_buf[_offset], sz );
471  _offset += sz;
472  }
473 }
474 
475 /*------------------------------------------------------------*
476 * definition VsnetDownload::Server::Manager
477 *------------------------------------------------------------*/
478 
479 LOCALCONST_DEF( Manager, int, _packetWorkahead, 5 ) Manager::Manager( SocketSet &sets, const char **local_search_paths ) :
480  _set( sets )
481 {
482  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
483 
484  const char **c = local_search_paths;
485  while (*c != NULL) {
486  _local_search_paths.push_back( *c );
487  c++;
488  }
489 }
490 
492  _set( sets )
493 {
494  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
495 
496  _local_search_paths.push_back( VSFileSystem::datadir );
497 }
498 
500 {
501  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
502 
503  RecvCmdDownload recv( buffer );
504  Subcommand c = recv.parse();
505 
506  COUT<<" *** "<<" cmd "<<c<<endl;
507  switch (c)
508  {
509  case ResolveRequest:
510  {
512 
513  short num = r->num;
514 
515  NetBuffer respbuffer;
516  respbuffer.addChar( ResolveResponse );
517  respbuffer.addShort( num );
518 
520  for (iter = r->files.begin(); iter != r->files.end(); iter++) {
521  bool ok;
522  char ft = iter->ft;
523  string file = iter->file;
524  //If we want to download a memory buffer from server access is considered ok
525  if (ft == VSFileSystem::ZoneBuffer) {
526  //We receive a filename containing a zone id so we test it exists
527  int zoneid = atoi( file.c_str() );
528  ok = true;
529  } else {
530  ok = private_test_access( file, (VSFileSystem::VSFileType) ft );
531  }
532  respbuffer.addString( file );
533  respbuffer.addChar( ok ? 1 : 0 );
534  }
535  Packet packet;
536  packet.send( CMD_DOWNLOAD, 0,
537  respbuffer.getData(), respbuffer.getDataLength(),
539  NULL, sock,
540  __FILE__, PSEUDO__LINE__( 334 ) );
541  break;
542  }
543  case DownloadRequest:
544  {
546  short num = r->num;
547  if (num > 0) {
549  for (iter = r->files.begin(); iter != r->files.end(); iter++) {
550  char ft = iter->ft;
551  string file = iter->file;
552  string path = file;
553  DownloadItemPtr di;
555  //If a request for ZoneBuffer we create a VSFile based on a memory buffer
556  if (ft == VSFileSystem::ZoneBuffer) {
557  NetBuffer netbuf;
558  getZoneInfoBuffer( atoi( file.c_str() ), netbuf );
559  //f = new VSFile( netbuf.getData(), netbuf.getDataLength());
560 
561  DownloadItemBuf *buf;
562  buf = new DownloadItemBuf( sock, file, netbuf.getData(), netbuf.getDataLength() );
563  di.reset( buf );
564  } else {
565  f = private_access( path, (VSFileSystem::VSFileType) ft );
566  if (f) {
567  size_t bytes = f->Size();
568  di.reset( new DownloadItemFile( sock, file, f, bytes ) );
569  } else {
570  //Couldn't open for reading, maybe removed since resolve?
571  di.reset( new DownloadItemFile( sock, file ) );
572  }
573  }
574  _download_mx.lock();
575  _download.push( di );
576  _download_mx.unlock();
577  }
578  _set.wakeup();
579  }
580  break;
581  }
582  default:
583  {
584  NetBuffer respbuffer;
585 
586  respbuffer.addChar( UnexpectedSubcommand );
587  respbuffer.addChar( c );
588  Packet packet;
589  packet.send( CMD_DOWNLOAD, 0,
590  respbuffer.getData(), respbuffer.getDataLength(),
592  NULL, sock,
593  __FILE__, PSEUDO__LINE__( 334 ) );
594  break;
595  }
596  }
597 }
598 
600 {
601  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
602 
603  _lower_download.erase( s );
604 }
605 
607 {
608  _download_mx.lock();
609  while (_download.empty() == false) {
610  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
611 
612  DownloadItemPtr item = _download.front();
613  _download.pop();
614  ItemMap_I it = _lower_download.find( item->getSock() );
615  if ( it == _lower_download.end() ) {
616  ItemQueuePtr q( new ItemQueue );
617  _lower_download.insert( ItemMapPair( item->getSock(), q ) );
618  it = _lower_download.find( item->getSock() );
619  }
620  it->second->push( item );
621  }
622  _download_mx.unlock();
623 
624  list< SOCKETALT >tbd; //to be deleted
625  list< SOCKETALT >::const_iterator tbdi;
626 
627  ItemMap_I it;
628  for (it = _lower_download.begin(); it != _lower_download.end(); it++) {
629  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
630 
631  bool done = private_lower_try_push_queue( it->first, it->second );
632  if (done)
633  tbd.push_back( it->first );
634  /* This is expensive but I don't dare to call erase() here
635  * because I don't know whether the STL standard allows iterator
636  * invalidation for map<>. And anyway, MSVC doesn't care about
637  * standards, so better safe than sorry.
638  */
639  }
640  for (tbdi = tbd.begin(); tbdi != tbd.end(); tbdi++)
641  _lower_download.erase( *tbdi );
642 }
643 
648 bool Manager::private_lower_try_push_queue( SOCKETALT sock, ItemQueuePtr q )
649 {
650  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
651  if (!q->empty() && sock.queueLen( LOPRI ) <= _packetWorkahead) {
652  NetBuffer respbuffer;
653 
654  DownloadItemPtr item = q->front();
655  if ( item->error() ) {
656  respbuffer.addChar( DownloadError );
657  respbuffer.addString( item->file() );
658  Packet packet;
659  packet.send( CMD_DOWNLOAD, 0,
660  respbuffer.getData(), respbuffer.getDataLength(),
662  NULL, sock,
663  __FILE__, PSEUDO__LINE__( 425 ) );
664  q->pop();
665  } else {
666  size_t l = sock.optPayloadSize();
667  if ( !sock.isTcp() ) {
668  //UDP fragments itself because it must. But if we allow clients
669  //to load large chunks into these intermediate buffers all at
670  //once, that's an invitation for a DDOS attack. So we seek a
671  //middle path.
672  l *= 5;
673  }
674  bool firstfrag;
675  string f( item->file() );
676  if (item->offset() == 0) {
677  firstfrag = true;
678 
679  /* Subtract packet header length, 1 byte download subcommand,
680  * 2 bytes string length, length of the string itself,
681  * 4 bytes total file size
682  * 2 bytes payload length.
683  */
684  l = l-Packet::getHeaderLength()-1-2-f.length()-4-2;
685  } else {
686  firstfrag = false;
687  l = l-Packet::getHeaderLength()-1-2;
688  }
689  if (item->remainingSize() <= l) {
690  q->pop();
691 
692  short sz = item->remainingSize();
693  unsigned char *buf;
694  if (firstfrag) {
695  string f( item->file() );
696  respbuffer.addChar( Download );
697  respbuffer.addString( f );
698  } else {
699  respbuffer.addChar( DownloadLastFragment );
700  }
701  respbuffer.addShort( sz );
702  buf = respbuffer.extAddBuffer( sz );
703  item->copyFromFile( buf, sz );
704  } else {
705  unsigned char *buf;
706  if (firstfrag) {
707  respbuffer.addChar( DownloadFirstFragment );
708  respbuffer.addString( f );
709  respbuffer.addInt32( item->remainingSize() );
710  } else {
711  respbuffer.addChar( DownloadFragment );
712  }
713  respbuffer.addShort( l );
714  buf = respbuffer.extAddBuffer( l );
715  item->copyFromFile( buf, l );
716  }
717  Packet packet;
718  packet.send( CMD_DOWNLOAD, 0,
719  respbuffer.getData(), respbuffer.getDataLength(),
721  NULL, sock,
722  __FILE__, PSEUDO__LINE__( 525 ) );
723  }
724  }
725  return q->empty();
726 }
727 
728 bool Manager::private_test_access( const string &file, VSFileSystem::VSFileType ft )
729 {
730  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
731  for (vector< string >::const_iterator it = _local_search_paths.begin();
732  it != _local_search_paths.end();
733  it++) {
734  string path = *it+"/"+file;
735  //if( ::vs_access( path.c_str(), R_OK ) == 0 )
736  string ffile( file );
737  if (LookForFile( ffile, ft ) <= Ok) {
738  COUT<<"Found local file "<<path.c_str()<<endl;
739  return true;
740  }
741  }
742  COUT<<"Didn't find local file for "<<file.c_str()<<endl;
743  return false;
744 }
745 
746 VSFileSystem::VSFile* Manager::private_access( string &file, VSFileSystem::VSFileType ft )
747 {
748  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
749 
752  if ( ( err = f->OpenReadOnly( file, ft ) ) <= Ok ) {
753  COUT<<"Opened local file "<<file<<endl;
754  return f;
755  }
756  /*
757  * for( vector<string>::const_iterator it=_local_search_paths.begin();
758  * it!=_local_search_paths.end();
759  * it++ )
760  * {
761  * string path = *it + "/" + file;
762  * ifstream* f = new ifstream( path.c_str(), ios::in );
763  * if( f && f->good() )
764  * {
765  * COUT << "Opened local file " << path.c_str() << endl;
766  * file = path;
767  * return f;
768  * }
769  * }
770  */
771  COUT<<"Couldn't open local file for "<<file<<endl;
772  delete f;
773  return NULL;
774 }
775 
776 size_t Manager::private_file_size( const string &file )
777 {
778  COUT<<"Enter "<<__PRETTY_FUNCTION__<<endl;
779 
780  struct stat buf;
781  if (::lstat( file.c_str(), &buf ) == 0) {
782  size_t ret = buf.st_size;
783  return ret;
784  } else {
785  return 0;
786  }
787 }
788 }; //namespace Server
789 }; //namespace VsnetDownload
790 
791 #ifndef HAVE_ACCESS
792 extern "C" {
793 int vs_access( const char *name, int mode )
794 {
795  FILE *fp;
796  if (mode&R_OK) {
797  fp = fopen( name, "rb" );
798  if (fp)
799  fclose( fp );
800  else
801  return -1;
802  }
803  if (mode&W_OK) {
804  fp = fopen( name, "r+b" ); //NOTE: any other mode may destroy existing data
805  if (fp)
806  fclose( fp );
807  else
808  return -1;
809  }
810  return 1;
811 }
812 }
813 #endif /* HAVE_ACCESS */
814 
815 #ifndef HAVE_LSTAT
816 int lstat( const char *name, struct stat *buf )
817 {
818  FILE *f = VSFileSystem::OpenFile( name, "rb" );
819  if (f == NULL) return -1;
820  int retval = -1;
821  if (VSFileSystem::Fseek( f, 0, SEEK_END ) == 0) {
822  buf->st_size = VSFileSystem::Ftell( f );
823  if (buf->st_size >= 0) retval = 0;
824  }
825  VSFileSystem::Close( f );
826  return retval;
827 }
828 #endif /* HAVE_LSTAT */
829 
830 using namespace VsnetDownload::Client;
831 
832 #define CASE( a ) \
833 case a: \
834  return #a; break
835 
836 const char * getState( State s )
837 {
838  switch (s)
839  {
840  CASE( Idle );
841  CASE( Queued );
842  CASE( Resolving );
843  CASE( Resolved );
844  CASE( Requested );
846  CASE( Completed );
847  default:
848  return "Unknown state";
849  }
850 }
851