# network_content.cpp

Go to the documentation of this file.

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