# network_content.cpp

Go to the documentation of this file.

    00001 /* $Id: network_content.cpp 15299 2009-01-31 20:16:06Z smatz $ */
    00002
    00005 #if defined(ENABLE_NETWORK)
    00006
    00007 #include "../stdafx.h"
    00008 #include "../rev.h"
    00009 #include "../fileio_func.h"
    00010 #include "../string_func.h"
    00011 #include "../ai/ai.hpp"
    00012 #include "../window_func.h"
    00013 #include "../gui.h"
    00014 #include "../variables.h"
    00015 #include "network_content.h"
    00016
    00017 #include "table/strings.h"
    00018
    00019 #if defined(WITH_ZLIB)
    00020 #include <zlib.h>
    00021 #endif
    00022
    00023 extern bool TarListAddFile(const char *filename);
    00024 extern bool HasGraphicsSet(const ContentInfo *ci, bool md5sum);
    00025 ClientNetworkContentSocketHandler _network_content_client;
    00026
    00028 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
    00029 {
    00030   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? ci->md5sum : NULL) != NULL;
    00031 }
    00032
    00040 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
    00041
    00042 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_INFO)
    00043 {
    00044   ContentInfo *ci = new ContentInfo();
    00045   ci->type     = (ContentType)p->Recv_uint8();
    00046   ci->id       = (ContentID)p->Recv_uint32();
    00047   ci->filesize = p->Recv_uint32();
    00048
    00049   p->Recv_string(ci->name, lengthof(ci->name));
    00050   p->Recv_string(ci->version, lengthof(ci->name));
    00051   p->Recv_string(ci->url, lengthof(ci->url));
    00052   p->Recv_string(ci->description, lengthof(ci->description),  true);
    00053
    00054   ci->unique_id = p->Recv_uint32();
    00055   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00056     ci->md5sum[j] = p->Recv_uint8();
    00057   }
    00058
    00059   ci->dependency_count = p->Recv_uint8();
    00060   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
    00061   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
    00062
    00063   ci->tag_count = p->Recv_uint8();
    00064   ci->tags = MallocT<char[32]>(ci->tag_count);
    00065   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
    00066
    00067   if (!ci->IsValid()) {
    00068     delete ci;
    00069     this->Close();
    00070     return false;
    00071   }
    00072
    00073   /* Find the appropriate check function */
    00074   HasProc proc = NULL;
    00075   switch (ci->type) {
    00076     case CONTENT_TYPE_NEWGRF:
    00077       proc = HasGRFConfig;
    00078       break;
    00079
    00080     case CONTENT_TYPE_BASE_GRAPHICS:
    00081       proc = HasGraphicsSet;
    00082       break;
    00083
    00084     case CONTENT_TYPE_AI:
    00085     case CONTENT_TYPE_AI_LIBRARY:
    00086       proc = AI::HasAI; break;
    00087       break;
    00088
    00089     default:
    00090       break;
    00091   }
    00092
    00093   if (proc != NULL) {
    00094     if (proc(ci, true)) {
    00095       ci->state = ContentInfo::ALREADY_HERE;
    00096     } else {
    00097       ci->state = ContentInfo::UNSELECTED;
    00098       if (proc(ci, false)) ci->upgrade = true;
    00099     }
    00100   } else {
    00101     ci->state = ContentInfo::UNSELECTED;
    00102   }
    00103
    00104   /* Something we don't have and has filesize 0 does not exist in te system */
    00105   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
    00106
    00107   /* Do we already have a stub for this? */
    00108   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00109     ContentInfo *ici = *iter;
    00110     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
    00111         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
    00112       /* Preserve the name if possible */
    00113       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
    00114       if (ici->IsSelected()) ci->state = ici->state;
    00115
    00116       delete ici;
    00117       *iter = ci;
    00118
    00119       this->OnReceiveContentInfo(ci);
    00120       return true;
    00121     }
    00122   }
    00123
    00124   /* Missing content info? Don't list it */
    00125   if (ci->filesize == 0) {
    00126     delete ci;
    00127     return true;
    00128   }
    00129
    00130   *this->infos.Append() = ci;
    00131
    00132   /* Incoming data means that we might need to reconsider dependencies */
    00133   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00134     this->CheckDependencyState(*iter);
    00135   }
    00136
    00137   this->OnReceiveContentInfo(ci);
    00138
    00139   return true;
    00140 }
    00141
    00142 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
    00143 {
    00144   if (type == CONTENT_TYPE_END) {
    00145     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
    00146     this->RequestContentList(CONTENT_TYPE_AI);
    00147     this->RequestContentList(CONTENT_TYPE_NEWGRF);
    00148     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
    00149     return;
    00150   }
    00151
    00152   this->Connect();
    00153
    00154   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
    00155   p->Send_uint8 ((byte)type);
    00156   p->Send_uint32(_openttd_newgrf_version);
    00157
    00158   this->Send_Packet(p);
    00159 }
    00160
    00161 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
    00162 {
    00163   this->Connect();
    00164
    00165   while (count > 0) {
    00166     /* We can "only" send a limited number of IDs in a single packet.
    00167      * A packet begins with the packet size and a byte for the type.
    00168      * Then this packet adds a byte for the content type and a uint16
    00169      * for the count in this packet. The rest of the packet can be
    00170      * used for the IDs. */
    00171     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00172
    00173     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
    00174     p->Send_uint16(p_count);
    00175
    00176     for (uint i = 0; i < p_count; i++) {
    00177       p->Send_uint32(content_ids[i]);
    00178     }
    00179
    00180     this->Send_Packet(p);
    00181     count -= p_count;
    00182     content_ids += count;
    00183   }
    00184 }
    00185
    00186 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
    00187 {
    00188   if (cv == NULL) return;
    00189
    00190   this->Connect();
    00191
    00192   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
    00193   assert(cv->Length() < 255);
    00194   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
    00195
    00196   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
    00197   p->Send_uint8(cv->Length());
    00198
    00199   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00200     const ContentInfo *ci = *iter;
    00201     p->Send_uint8((byte)ci->type);
    00202     p->Send_uint32(ci->unique_id);
    00203     if (!send_md5sum) continue;
    00204
    00205     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00206       p->Send_uint8(ci->md5sum[j]);
    00207     }
    00208   }
    00209
    00210   this->Send_Packet(p);
    00211
    00212   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00213     ContentInfo *ci = *iter;
    00214     bool found = false;
    00215     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
    00216       ContentInfo *ci2 = *iter2;
    00217       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
    00218           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
    00219         found = true;
    00220         break;
    00221       }
    00222     }
    00223     if (!found) {
    00224       *this->infos.Append() = ci;
    00225     } else {
    00226       delete ci;
    00227     }
    00228   }
    00229 }
    00230
    00231 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes)
    00232 {
    00233   files = 0;
    00234   bytes = 0;
    00235
    00237   ContentID *ids = MallocT<ContentID>(infos.Length());
    00238   for (ContentIterator iter = infos.Begin(); iter != infos.End(); iter++) {
    00239     const ContentInfo *ci = *iter;
    00240     if (!ci->IsSelected()) continue;
    00241
    00242     ids[files++] = ci->id;
    00243     bytes += ci->filesize;
    00244   }
    00245
    00246   uint count = files;
    00247   ContentID *content_ids = ids;
    00248   this->Connect();
    00249
    00250   while (count > 0) {
    00251     /* We can "only" send a limited number of IDs in a single packet.
    00252     * A packet begins with the packet size and a byte for the type.
    00253     * Then this packet adds a uint16 for the count in this packet.
    00254     * The rest of the packet can be used for the IDs. */
    00255     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00256
    00257     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
    00258     p->Send_uint16(p_count);
    00259
    00260     for (uint i = 0; i < p_count; i++) {
    00261       p->Send_uint32(content_ids[i]);
    00262     }
    00263
    00264     this->Send_Packet(p);
    00265     count -= p_count;
    00266     content_ids += count;
    00267   }
    00268
    00269   free(ids);
    00270 }
    00271
    00279 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
    00280 {
    00281   Subdirectory dir;
    00282   switch (ci->type) {
    00283     default: return NULL;
    00284     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
    00285     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
    00286     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
    00287     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
    00288     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
    00289     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
    00290   }
    00291
    00292   static char buf[MAX_PATH];
    00293   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
    00294   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
    00295
    00296   return buf;
    00297 }
    00298
    00304 static bool GunzipFile(const ContentInfo *ci)
    00305 {
    00306 #if defined(WITH_ZLIB)
    00307   bool ret = true;
    00308   gzFile fin = gzopen(GetFullFilename(ci, true), "rb");
    00309   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
    00310
    00311   if (fin == NULL || fout == NULL) {
    00312     ret = false;
    00313     goto exit;
    00314   }
    00315
    00316   byte buff[8192];
    00317   while (!gzeof(fin)) {
    00318     int read = gzread(fin, buff, sizeof(buff));
    00319     if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
    00320       ret = false;
    00321       break;
    00322     }
    00323   }
    00324
    00325 exit:
    00326   if (fin != NULL) gzclose(fin);
    00327   if (fout != NULL) fclose(fout);
    00328
    00329   return ret;
    00330 #else
    00331   NOT_REACHED();
    00332 #endif /* defined(WITH_ZLIB) */
    00333 }
    00334
    00335 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_CONTENT)
    00336 {
    00337   if (this->curFile == NULL) {
    00338     /* When we haven't opened a file this must be our first packet with metadata. */
    00339     this->curInfo = new ContentInfo;
    00340     this->curInfo->type     = (ContentType)p->Recv_uint8();
    00341     this->curInfo->id       = (ContentID)p->Recv_uint32();
    00342     this->curInfo->filesize = p->Recv_uint32();
    00343     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
    00344
    00345     if (!this->curInfo->IsValid()) {
    00346       delete this->curInfo;
    00347       this->curInfo = NULL;
    00348       this->Close();
    00349       return false;
    00350     }
    00351
    00352     if (this->curInfo->filesize != 0) {
    00353       /* The filesize is > 0, so we are going to download it */
    00354       const char *filename = GetFullFilename(this->curInfo, true);
    00355       if (filename == NULL) {
    00356         /* Unless that fails ofcourse... */
    00357         DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00358         ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, 0, 0);
    00359         this->Close();
    00360         return false;
    00361       }
    00362
    00363       this->curFile = fopen(filename, "wb");
    00364     }
    00365   } else {
    00366     /* We have a file opened, thus are downloading internal content */
    00367     size_t toRead = (size_t)(p->size - p->pos);
    00368     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
    00369       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00370       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, 0, 0);
    00371       this->Close();
    00372       fclose(this->curFile);
    00373       this->curFile = NULL;
    00374
    00375       return false;
    00376     }
    00377
    00378     this->OnDownloadProgress(this->curInfo, (uint)toRead);
    00379
    00380     if (toRead == 0) {
    00381       /* We read nothing; that's our marker for end-of-stream.
    00382        * Now gunzip the tar and make it known. */
    00383       fclose(this->curFile);
    00384       this->curFile = NULL;
    00385
    00386       if (GunzipFile(this->curInfo)) {
    00387         unlink(GetFullFilename(this->curInfo, true));
    00388
    00389         TarListAddFile(GetFullFilename(this->curInfo, false));
    00390
    00391         this->OnDownloadComplete(this->curInfo->id);
    00392       } else {
    00393         ShowErrorMessage(INVALID_STRING_ID, STR_CONTENT_ERROR_COULD_NOT_EXTRACT, 0, 0);
    00394       }
    00395     }
    00396   }
    00397
    00398   /* We ended this file, so clean up the mess */
    00399   if (this->curFile == NULL) {
    00400     delete this->curInfo;
    00401     this->curInfo = NULL;
    00402   }
    00403
    00404   return true;
    00405 }
    00406
    00412 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
    00413   NetworkContentSocketHandler(INVALID_SOCKET, NULL),
    00414   curFile(NULL),
    00415   curInfo(NULL),
    00416   isConnecting(false)
    00417 {
    00418 }
    00419
    00421 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
    00422 {
    00423   delete this->curInfo;
    00424   if (this->curFile != NULL) fclose(this->curFile);
    00425
    00426   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    00427 }
    00428
    00429 class NetworkContentConnecter : TCPConnecter {
    00430 public:
    00431   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
    00432
    00433   virtual void OnFailure()
    00434   {
    00435     _network_content_client.isConnecting = false;
    00436     _network_content_client.OnConnect(false);
    00437   }
    00438
    00439   virtual void OnConnect(SOCKET s)
    00440   {
    00441     assert(_network_content_client.sock == INVALID_SOCKET);
    00442     _network_content_client.isConnecting = false;
    00443     _network_content_client.sock = s;
    00444     _network_content_client.has_quit = false;
    00445     _network_content_client.OnConnect(true);
    00446   }
    00447 };
    00448
    00452 void ClientNetworkContentSocketHandler::Connect()
    00453 {
    00454   this->lastActivity = _realtime_tick;
    00455
    00456   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
    00457   this->isConnecting = true;
    00458   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT));
    00459 }
    00460
    00464 void ClientNetworkContentSocketHandler::Close()
    00465 {
    00466   if (this->sock == INVALID_SOCKET) return;
    00467   NetworkContentSocketHandler::Close();
    00468
    00469   this->OnDisconnect();
    00470 }
    00471
    00476 void ClientNetworkContentSocketHandler::SendReceive()
    00477 {
    00478   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
    00479
    00480   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
    00481     this->Close();
    00482     return;
    00483   }
    00484
    00485   fd_set read_fd, write_fd;
    00486   struct timeval tv;
    00487
    00488   FD_ZERO(&read_fd);
    00489   FD_ZERO(&write_fd);
    00490
    00491   FD_SET(this->sock, &read_fd);
    00492   FD_SET(this->sock, &write_fd);
    00493
    00494   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
    00495 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
    00496   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
    00497 #else
    00498   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
    00499 #endif
    00500   if (FD_ISSET(this->sock, &read_fd)) this->Recv_Packets();
    00501   this->writable = !!FD_ISSET(this->sock, &write_fd);
    00502   this->Send_Packets();
    00503 }
    00504
    00509 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
    00510 {
    00511   /* When we tried to download it already, don't try again */
    00512   if (this->requested.Contains(cid)) return;
    00513
    00514   *this->requested.Append() = cid;
    00515   assert(this->requested.Contains(cid));
    00516   this->RequestContentList(1, &cid);
    00517 }
    00518
    00524 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
    00525 {
    00526   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00527     ContentInfo *ci = *iter;
    00528     if (ci->id == cid) return ci;
    00529   }
    00530   return NULL;
    00531 }
    00532
    00533
    00538 void ClientNetworkContentSocketHandler::Select(ContentID cid)
    00539 {
    00540   ContentInfo *ci = this->GetContent(cid);
    00541   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
    00542
    00543   ci->state = ContentInfo::SELECTED;
    00544   this->CheckDependencyState(ci);
    00545 }
    00546
    00551 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
    00552 {
    00553   ContentInfo *ci = this->GetContent(cid);
    00554   if (ci == NULL || !ci->IsSelected()) return;
    00555
    00556   ci->state = ContentInfo::UNSELECTED;
    00557   this->CheckDependencyState(ci);
    00558 }
    00559
    00561 void ClientNetworkContentSocketHandler::SelectAll()
    00562 {
    00563   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00564     ContentInfo *ci = *iter;
    00565     if (ci->state == ContentInfo::UNSELECTED) {
    00566       ci->state = ContentInfo::SELECTED;
    00567       this->CheckDependencyState(ci);
    00568     }
    00569   }
    00570 }
    00571
    00573 void ClientNetworkContentSocketHandler::SelectUpgrade()
    00574 {
    00575   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00576     ContentInfo *ci = *iter;
    00577     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
    00578       ci->state = ContentInfo::SELECTED;
    00579       this->CheckDependencyState(ci);
    00580     }
    00581   }
    00582 }
    00583
    00585 void ClientNetworkContentSocketHandler::UnselectAll()
    00586 {
    00587   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00588     ContentInfo *ci = *iter;
    00589     if (ci->IsSelected()) ci->state = ContentInfo::UNSELECTED;
    00590   }
    00591 }
    00592
    00594 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
    00595 {
    00596   switch (ci->state) {
    00597     case ContentInfo::SELECTED:
    00598     case ContentInfo::AUTOSELECTED:
    00599       this->Unselect(ci->id);
    00600       break;
    00601
    00602     case ContentInfo::UNSELECTED:
    00603       this->Select(ci->id);
    00604       break;
    00605
    00606     default:
    00607       break;
    00608   }
    00609 }
    00610
    00616 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
    00617 {
    00618   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00619     const ContentInfo *ci = *iter;
    00620     if (ci == child) continue;
    00621
    00622     for (uint i = 0; i < ci->dependency_count; i++) {
    00623       if (ci->dependencies[i] == child->id) {
    00624         *parents.Append() = ci;
    00625         break;
    00626       }
    00627     }
    00628   }
    00629 }
    00630
    00636 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
    00637 {
    00638   *tree.Append() = child;
    00639
    00640   /* First find all direct parents */
    00641   for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
    00642     ConstContentVector parents;
    00643     this->ReverseLookupDependency(parents, *iter);
    00644
    00645     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
    00646       tree.Include(*piter);
    00647     }
    00648   }
    00649 }
    00650
    00655 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
    00656 {
    00657   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
    00658     /* Selection is easy; just walk all children and set the
    00659      * autoselected state. That way we can see what we automatically
    00660      * selected and thus can unselect when a dependency is removed. */
    00661     for (uint i = 0; i < ci->dependency_count; i++) {
    00662       ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00663       if (c == NULL) {
    00664         this->DownloadContentInfo(ci->dependencies[i]);
    00665       } else if (c->state == ContentInfo::UNSELECTED) {
    00666         c->state = ContentInfo::AUTOSELECTED;
    00667         this->CheckDependencyState(c);
    00668       }
    00669     }
    00670     return;
    00671   }
    00672
    00673   if (ci->state != ContentInfo::UNSELECTED) return;
    00674
    00675   /* For unselection we need to find the parents of us. We need to
    00676    * unselect them. After that we unselect all children that we
    00677    * depend on and are not used as dependency for us, but only when
    00678    * we automatically selected them. */
    00679   ConstContentVector parents;
    00680   this->ReverseLookupDependency(parents, ci);
    00681   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00682     const ContentInfo *c = *iter;
    00683     if (!c->IsSelected()) continue;
    00684
    00685     this->Unselect(c->id);
    00686   }
    00687
    00688   for (uint i = 0; i < ci->dependency_count; i++) {
    00689     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00690     if (c == NULL) {
    00691       DownloadContentInfo(ci->dependencies[i]);
    00692       continue;
    00693     }
    00694     if (c->state != ContentInfo::AUTOSELECTED) continue;
    00695
    00696     /* Only unselect when WE are the only parent. */
    00697     parents.Clear();
    00698     this->ReverseLookupDependency(parents, c);
    00699
    00700     /* First check whether anything depends on us */
    00701     int sel_count = 0;
    00702     bool force_selection = false;
    00703     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00704       if ((*iter)->IsSelected()) sel_count++;
    00705       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
    00706     }
    00707     if (sel_count == 0) {
    00708       /* Nothing depends on us */
    00709       this->Unselect(c->id);
    00710       continue;
    00711     }
    00712     /* Something manually selected depends directly on us */
    00713     if (force_selection) continue;
    00714
    00715     /* "Flood" search to find all items in the dependency graph*/
    00716     parents.Clear();
    00717     this->ReverseLookupTreeDependency(parents, c);
    00718
    00719     /* Is there anything that is "force" selected?, if so... we're done. */
    00720     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00721       if ((*iter)->state != ContentInfo::SELECTED) continue;
    00722
    00723       force_selection = true;
    00724       break;
    00725     }
    00726
    00727     /* So something depended directly on us */
    00728     if (force_selection) continue;
    00729
    00730     /* Nothing depends on us, mark the whole graph as unselected.
    00731      * After that's done run over them once again to test their children
    00732      * to unselect. Don't do it immediatelly because it'll do exactly what
    00733      * we're doing now. */
    00734     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00735       const ContentInfo *c = *iter;
    00736       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
    00737     }
    00738     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00739       this->CheckDependencyState(this->GetContent((*iter)->id));
    00740     }
    00741   }
    00742 }
    00743
    00744 void ClientNetworkContentSocketHandler::Clear()
    00745 {
    00746   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    00747
    00748   this->infos.Clear();
    00749 }
    00750
    00751 /*** CALLBACK ***/
    00752
    00753 void ClientNetworkContentSocketHandler::OnConnect(bool success)
    00754 {
    00755   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00756     ContentCallback *cb = *iter;
    00757     cb->OnConnect(success);
    00758     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00759   }
    00760 }
    00761
    00762 void ClientNetworkContentSocketHandler::OnDisconnect()
    00763 {
    00764   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00765     ContentCallback *cb = *iter;
    00766     cb->OnDisconnect();
    00767     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00768   }
    00769 }
    00770
    00771 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
    00772 {
    00773   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00774     ContentCallback *cb = *iter;
    00775     cb->OnReceiveContentInfo(ci);
    00776     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00777   }
    00778 }
    00779
    00780 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, uint bytes)
    00781 {
    00782   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00783     ContentCallback *cb = *iter;
    00784     cb->OnDownloadProgress(ci, bytes);
    00785     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00786   }
    00787 }
    00788
    00789 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
    00790 {
    00791   ContentInfo *ci = this->GetContent(cid);
    00792   if (ci != NULL) {
    00793     ci->state = ContentInfo::ALREADY_HERE;
    00794   }
    00795
    00796   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00797     ContentCallback *cb = *iter;
    00798     cb->OnDownloadComplete(cid);
    00799     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00800   }
    00801 }
    00802
    00803 #endif /* ENABLE_NETWORK */