# network_content.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #if defined(ENABLE_NETWORK)
    00013
    00014 #include "../stdafx.h"
    00015 #include "../rev.h"
    00016 #include "../ai/ai.hpp"
    00017 #include "../window_func.h"
    00018 #include "../gui.h"
    00019 #include "../base_media_base.h"
    00020 #include "../settings_type.h"
    00021 #include "network_content.h"
    00022
    00023 #include "table/strings.h"
    00024
    00025 #if defined(WITH_ZLIB)
    00026 #include <zlib.h>
    00027 #endif
    00028
    00029 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
    00030
    00032 ClientNetworkContentSocketHandler _network_content_client;
    00033
    00035 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
    00036 {
    00037   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? ci->md5sum : NULL) != NULL;
    00038 }
    00039
    00047 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
    00048
    00049 bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
    00050 {
    00051   ContentInfo *ci = new ContentInfo();
    00052   ci->type     = (ContentType)p->Recv_uint8();
    00053   ci->id       = (ContentID)p->Recv_uint32();
    00054   ci->filesize = p->Recv_uint32();
    00055
    00056   p->Recv_string(ci->name, lengthof(ci->name));
    00057   p->Recv_string(ci->version, lengthof(ci->name));
    00058   p->Recv_string(ci->url, lengthof(ci->url));
    00059   p->Recv_string(ci->description, lengthof(ci->description),  true);
    00060
    00061   ci->unique_id = p->Recv_uint32();
    00062   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00063     ci->md5sum[j] = p->Recv_uint8();
    00064   }
    00065
    00066   ci->dependency_count = p->Recv_uint8();
    00067   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
    00068   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
    00069
    00070   ci->tag_count = p->Recv_uint8();
    00071   ci->tags = MallocT<char[32]>(ci->tag_count);
    00072   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
    00073
    00074   if (!ci->IsValid()) {
    00075     delete ci;
    00076     this->Close();
    00077     return false;
    00078   }
    00079
    00080   /* Find the appropriate check function */
    00081   HasProc proc = NULL;
    00082   switch (ci->type) {
    00083     case CONTENT_TYPE_NEWGRF:
    00084       proc = HasGRFConfig;
    00085       break;
    00086
    00087     case CONTENT_TYPE_BASE_GRAPHICS:
    00088       proc = BaseGraphics::HasSet;
    00089       break;
    00090
    00091     case CONTENT_TYPE_BASE_MUSIC:
    00092       proc = BaseMusic::HasSet;
    00093       break;
    00094
    00095     case CONTENT_TYPE_BASE_SOUNDS:
    00096       proc = BaseSounds::HasSet;
    00097       break;
    00098
    00099     case CONTENT_TYPE_AI:
    00100     case CONTENT_TYPE_AI_LIBRARY:
    00101       proc = AI::HasAI; break;
    00102       break;
    00103
    00104     case CONTENT_TYPE_SCENARIO:
    00105     case CONTENT_TYPE_HEIGHTMAP:
    00106       proc = HasScenario;
    00107       break;
    00108
    00109     default:
    00110       break;
    00111   }
    00112
    00113   if (proc != NULL) {
    00114     if (proc(ci, true)) {
    00115       ci->state = ContentInfo::ALREADY_HERE;
    00116     } else {
    00117       ci->state = ContentInfo::UNSELECTED;
    00118       if (proc(ci, false)) ci->upgrade = true;
    00119     }
    00120   } else {
    00121     ci->state = ContentInfo::UNSELECTED;
    00122   }
    00123
    00124   /* Something we don't have and has filesize 0 does not exist in te system */
    00125   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
    00126
    00127   /* Do we already have a stub for this? */
    00128   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00129     ContentInfo *ici = *iter;
    00130     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
    00131         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
    00132       /* Preserve the name if possible */
    00133       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
    00134       if (ici->IsSelected()) ci->state = ici->state;
    00135
    00136       /*
    00137        * As ici might be selected by the content window we cannot delete that.
    00138        * However, we want to keep most of the values of ci, except the values
    00139        * we (just) already preserved.
    00140        * So transfer data and ownership of allocated memory from ci to ici.
    00141        */
    00142       ici->TransferFrom(ci);
    00143       delete ci;
    00144
    00145       this->OnReceiveContentInfo(ici);
    00146       return true;
    00147     }
    00148   }
    00149
    00150   /* Missing content info? Don't list it */
    00151   if (ci->filesize == 0) {
    00152     delete ci;
    00153     return true;
    00154   }
    00155
    00156   *this->infos.Append() = ci;
    00157
    00158   /* Incoming data means that we might need to reconsider dependencies */
    00159   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00160     this->CheckDependencyState(*iter);
    00161   }
    00162
    00163   this->OnReceiveContentInfo(ci);
    00164
    00165   return true;
    00166 }
    00167
    00172 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
    00173 {
    00174   if (type == CONTENT_TYPE_END) {
    00175     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
    00176     this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
    00177     this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
    00178     this->RequestContentList(CONTENT_TYPE_SCENARIO);
    00179     this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
    00180 #ifdef ENABLE_AI
    00181     this->RequestContentList(CONTENT_TYPE_AI);
    00182     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
    00183 #endif /* ENABLE_AI */
    00184     this->RequestContentList(CONTENT_TYPE_NEWGRF);
    00185     return;
    00186   }
    00187
    00188   this->Connect();
    00189
    00190   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
    00191   p->Send_uint8 ((byte)type);
    00192   p->Send_uint32(_openttd_newgrf_version);
    00193
    00194   this->SendPacket(p);
    00195 }
    00196
    00202 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
    00203 {
    00204   this->Connect();
    00205
    00206   while (count > 0) {
    00207     /* We can "only" send a limited number of IDs in a single packet.
    00208      * A packet begins with the packet size and a byte for the type.
    00209      * Then this packet adds a byte for the content type and a uint16
    00210      * for the count in this packet. The rest of the packet can be
    00211      * used for the IDs. */
    00212     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00213
    00214     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
    00215     p->Send_uint16(p_count);
    00216
    00217     for (uint i = 0; i < p_count; i++) {
    00218       p->Send_uint32(content_ids[i]);
    00219     }
    00220
    00221     this->SendPacket(p);
    00222     count -= p_count;
    00223     content_ids += p_count;
    00224   }
    00225 }
    00226
    00232 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
    00233 {
    00234   if (cv == NULL) return;
    00235
    00236   this->Connect();
    00237
    00238   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
    00239   assert(cv->Length() < 255);
    00240   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
    00241
    00242   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
    00243   p->Send_uint8(cv->Length());
    00244
    00245   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00246     const ContentInfo *ci = *iter;
    00247     p->Send_uint8((byte)ci->type);
    00248     p->Send_uint32(ci->unique_id);
    00249     if (!send_md5sum) continue;
    00250
    00251     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00252       p->Send_uint8(ci->md5sum[j]);
    00253     }
    00254   }
    00255
    00256   this->SendPacket(p);
    00257
    00258   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00259     ContentInfo *ci = *iter;
    00260     bool found = false;
    00261     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
    00262       ContentInfo *ci2 = *iter2;
    00263       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
    00264           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
    00265         found = true;
    00266         break;
    00267       }
    00268     }
    00269     if (!found) {
    00270       *this->infos.Append() = ci;
    00271     } else {
    00272       delete ci;
    00273     }
    00274   }
    00275 }
    00276
    00283 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
    00284 {
    00285   bytes = 0;
    00286
    00287   ContentIDList content;
    00288   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00289     const ContentInfo *ci = *iter;
    00290     if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
    00291
    00292     *content.Append() = ci->id;
    00293     bytes += ci->filesize;
    00294   }
    00295
    00296   files = content.Length();
    00297
    00298   /* If there's nothing to download, do nothing. */
    00299   if (files == 0) return;
    00300
    00301   if (_settings_client.network.no_http_content_downloads || fallback) {
    00302     this->DownloadSelectedContentFallback(content);
    00303   } else {
    00304     this->DownloadSelectedContentHTTP(content);
    00305   }
    00306 }
    00307
    00312 void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const ContentIDList &content)
    00313 {
    00314   uint count = content.Length();
    00315
    00316   /* Allocate memory for the whole request.
    00317    * Requests are "id\nid\n..." (as strings), so assume the maximum ID,
    00318    * which is uint32 so 10 characters long. Then the newlines and
    00319    * multiply that all with the count and then add the '\0'. */
    00320   uint bytes = (10 + 1) * count + 1;
    00321   char *content_request = MallocT<char>(bytes);
    00322   const char *lastof = content_request + bytes - 1;
    00323
    00324   char *p = content_request;
    00325   for (const ContentID *id = content.Begin(); id != content.End(); id++) {
    00326     p += seprintf(p, lastof, "%d\n", *id);
    00327   }
    00328
    00329   this->http_response_index = -1;
    00330
    00331   NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
    00332   new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
    00333   /* NetworkHTTPContentConnecter takes over freeing of content_request! */
    00334 }
    00335
    00340 void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const ContentIDList &content)
    00341 {
    00342   uint count = content.Length();
    00343   const ContentID *content_ids = content.Begin();
    00344   this->Connect();
    00345
    00346   while (count > 0) {
    00347     /* We can "only" send a limited number of IDs in a single packet.
    00348      * A packet begins with the packet size and a byte for the type.
    00349      * Then this packet adds a uint16 for the count in this packet.
    00350      * The rest of the packet can be used for the IDs. */
    00351     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00352
    00353     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
    00354     p->Send_uint16(p_count);
    00355
    00356     for (uint i = 0; i < p_count; i++) {
    00357       p->Send_uint32(content_ids[i]);
    00358     }
    00359
    00360     this->SendPacket(p);
    00361     count -= p_count;
    00362     content_ids += p_count;
    00363   }
    00364 }
    00365
    00373 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
    00374 {
    00375   Subdirectory dir;
    00376   switch (ci->type) {
    00377     default: return NULL;
    00378     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
    00379     case CONTENT_TYPE_BASE_MUSIC:    dir = GM_DIR;         break;
    00380     case CONTENT_TYPE_BASE_SOUNDS:   dir = DATA_DIR;       break;
    00381     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
    00382     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
    00383     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
    00384     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
    00385     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
    00386   }
    00387
    00388   static char buf[MAX_PATH];
    00389   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
    00390   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
    00391
    00392   return buf;
    00393 }
    00394
    00400 static bool GunzipFile(const ContentInfo *ci)
    00401 {
    00402 #if defined(WITH_ZLIB)
    00403   bool ret = true;
    00404   FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
    00405   gzFile fin = gzdopen(fileno(ftmp), "rb");
    00406   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
    00407
    00408   if (fin == NULL || fout == NULL) {
    00409     ret = false;
    00410   } else {
    00411     byte buff[8192];
    00412     for (;;) {
    00413       int read = gzread(fin, buff, sizeof(buff));
    00414       if (read == 0) {
    00415         /* If gzread() returns 0, either the end-of-file has been
    00416          * reached or an underlying read error has occurred.
    00417          *
    00418          * gzeof() can't be used, because:
    00419          * 1.2.5 - it is safe, 1 means 'everything was OK'
    00420          * 1.2.3.5, 1.2.4 - 0 or 1 is returned 'randomly'
    00421          * 1.2.3.3 - 1 is returned for truncated archive
    00422          *
    00423          * So we use gzerror(). When proper end of archive
    00424          * has been reached, then:
    00425          * errnum == Z_STREAM_END in 1.2.3.3,
    00426          * errnum == 0 in 1.2.4 and 1.2.5 */
    00427         int errnum;
    00428         gzerror(fin, &errnum);
    00429         if (errnum != 0 && errnum != Z_STREAM_END) ret = false;
    00430         break;
    00431       }
    00432       if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
    00433         /* If gzread() returns -1, there was an error in archive */
    00434         ret = false;
    00435         break;
    00436       }
    00437       /* DO NOT DO THIS! It will fail to detect broken archive with 1.2.3.3!
    00438        * if (read < sizeof(buff)) break; */
    00439     }
    00440   }
    00441
    00442   if (fin != NULL) {
    00443     /* Closes ftmp too! */
    00444     gzclose(fin);
    00445   } else if (ftmp != NULL) {
    00446     /* In case the gz stream was opened correctly this will
    00447      * be closed by gzclose. */
    00448     fclose(ftmp);
    00449   }
    00450   if (fout != NULL) fclose(fout);
    00451
    00452   return ret;
    00453 #else
    00454   NOT_REACHED();
    00455 #endif /* defined(WITH_ZLIB) */
    00456 }
    00457
    00458 bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p)
    00459 {
    00460   if (this->curFile == NULL) {
    00461     delete this->curInfo;
    00462     /* When we haven't opened a file this must be our first packet with metadata. */
    00463     this->curInfo = new ContentInfo;
    00464     this->curInfo->type     = (ContentType)p->Recv_uint8();
    00465     this->curInfo->id       = (ContentID)p->Recv_uint32();
    00466     this->curInfo->filesize = p->Recv_uint32();
    00467     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
    00468
    00469     if (!this->BeforeDownload()) {
    00470       this->Close();
    00471       return false;
    00472     }
    00473   } else {
    00474     /* We have a file opened, thus are downloading internal content */
    00475     size_t toRead = (size_t)(p->size - p->pos);
    00476     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
    00477       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00478       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
    00479       this->Close();
    00480       fclose(this->curFile);
    00481       this->curFile = NULL;
    00482
    00483       return false;
    00484     }
    00485
    00486     this->OnDownloadProgress(this->curInfo, (int)toRead);
    00487
    00488     if (toRead == 0) this->AfterDownload();
    00489   }
    00490
    00491   return true;
    00492 }
    00493
    00498 bool ClientNetworkContentSocketHandler::BeforeDownload()
    00499 {
    00500   if (!this->curInfo->IsValid()) {
    00501     delete this->curInfo;
    00502     this->curInfo = NULL;
    00503     return false;
    00504   }
    00505
    00506   if (this->curInfo->filesize != 0) {
    00507     /* The filesize is > 0, so we are going to download it */
    00508     const char *filename = GetFullFilename(this->curInfo, true);
    00509     if (filename == NULL || (this->curFile = fopen(filename, "wb")) == NULL) {
    00510       /* Unless that fails ofcourse... */
    00511       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00512       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
    00513       return false;
    00514     }
    00515   }
    00516   return true;
    00517 }
    00518
    00523 void ClientNetworkContentSocketHandler::AfterDownload()
    00524 {
    00525   /* We read nothing; that's our marker for end-of-stream.
    00526    * Now gunzip the tar and make it known. */
    00527   fclose(this->curFile);
    00528   this->curFile = NULL;
    00529
    00530   if (GunzipFile(this->curInfo)) {
    00531     unlink(GetFullFilename(this->curInfo, true));
    00532
    00533     TarScanner ts;
    00534     ts.AddFile(GetFullFilename(this->curInfo, false), 0);
    00535
    00536     if (this->curInfo->type == CONTENT_TYPE_BASE_MUSIC) {
    00537       /* Music can't be in a tar. So extract the tar! */
    00538       ExtractTar(GetFullFilename(this->curInfo, false));
    00539       unlink(GetFullFilename(this->curInfo, false));
    00540     }
    00541
    00542     this->OnDownloadComplete(this->curInfo->id);
    00543   } else {
    00544     ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);
    00545   }
    00546 }
    00547
    00548 /* Also called to just clean up the mess. */
    00549 void ClientNetworkContentSocketHandler::OnFailure()
    00550 {
    00551   /* If we fail, download the rest via the 'old' system. */
    00552   uint files, bytes;
    00553   this->DownloadSelectedContent(files, bytes, true);
    00554
    00555   this->http_response.Reset();
    00556   this->http_response_index = -2;
    00557
    00558   if (this->curFile != NULL) {
    00559     /* Revert the download progress when we are going for the old system. */
    00560     long size = ftell(this->curFile);
    00561     if (size > 0) this->OnDownloadProgress(this->curInfo, (int)-size);
    00562
    00563     fclose(this->curFile);
    00564     this->curFile = NULL;
    00565   }
    00566 }
    00567
    00568 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
    00569 {
    00570   assert(data == NULL || length != 0);
    00571
    00572   /* Ignore any latent data coming from a connection we closed. */
    00573   if (this->http_response_index == -2) return;
    00574
    00575   if (this->http_response_index == -1) {
    00576     if (data != NULL) {
    00577       /* Append the rest of the response. */
    00578       memcpy(this->http_response.Append((uint)length), data, length);
    00579       return;
    00580     } else {
    00581       /* Make sure the response is properly terminated. */
    00582       *this->http_response.Append() = '\0';
    00583
    00584       /* And prepare for receiving the rest of the data. */
    00585       this->http_response_index = 0;
    00586     }
    00587   }
    00588
    00589   if (data != NULL) {
    00590     /* We have data, so write it to the file. */
    00591     if (fwrite(data, 1, length, this->curFile) != length) {
    00592       /* Writing failed somehow, let try via the old method. */
    00593       this->OnFailure();
    00594     } else {
    00595       /* Just received the data. */
    00596       this->OnDownloadProgress(this->curInfo, (int)length);
    00597     }
    00598     /* Nothing more to do now. */
    00599     return;
    00600   }
    00601
    00602   if (this->curFile != NULL) {
    00603     /* We've finished downloading a file. */
    00604     this->AfterDownload();
    00605   }
    00606
    00607   if ((uint)this->http_response_index >= this->http_response.Length()) {
    00608     /* It's not a real failure, but if there's
    00609      * nothing more to download it helps with
    00610      * cleaning up the stuff we allocated. */
    00611     this->OnFailure();
    00612     return;
    00613   }
    00614
    00615   delete this->curInfo;
    00616   /* When we haven't opened a file this must be our first packet with metadata. */
    00617   this->curInfo = new ContentInfo;
    00618
    00620 #define check_not_null(p) { if ((p) == NULL) { this->OnFailure(); return; } }
    00621
    00622 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
    00623
    00624   for (;;) {
    00625     char *str = this->http_response.Begin() + this->http_response_index;
    00626     char *p = strchr(str, '\n');
    00627     check_and_terminate(p);
    00628
    00629     /* Update the index for the next one */
    00630     this->http_response_index += (int)strlen(str) + 1;
    00631
    00632     /* Read the ID */
    00633     p = strchr(str, ',');
    00634     check_and_terminate(p);
    00635     this->curInfo->id = (ContentID)atoi(str);
    00636
    00637     /* Read the type */
    00638     str = p + 1;
    00639     p = strchr(str, ',');
    00640     check_and_terminate(p);
    00641     this->curInfo->type = (ContentType)atoi(str);
    00642
    00643     /* Read the file size */
    00644     str = p + 1;
    00645     p = strchr(str, ',');
    00646     check_and_terminate(p);
    00647     this->curInfo->filesize = atoi(str);
    00648
    00649     /* Read the URL */
    00650     str = p + 1;
    00651     /* Is it a fallback URL? If so, just continue with the next one. */
    00652     if (strncmp(str, "ottd", 4) == 0) {
    00653       if ((uint)this->http_response_index >= this->http_response.Length()) {
    00654         /* Have we gone through all lines? */
    00655         this->OnFailure();
    00656         return;
    00657       }
    00658       continue;
    00659     }
    00660
    00661     p = strrchr(str, '/');
    00662     check_not_null(p);
    00663     p++; // Start after the '/'
    00664
    00665     char tmp[MAX_PATH];
    00666     if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
    00667       this->OnFailure();
    00668       return;
    00669     }
    00670     /* Remove the extension from the string. */
    00671     for (uint i = 0; i < 2; i++) {
    00672       p = strrchr(tmp, '.');
    00673       check_and_terminate(p);
    00674     }
    00675
    00676     /* Copy the string, without extension, to the filename. */
    00677     strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
    00678
    00679     /* Request the next file. */
    00680     if (!this->BeforeDownload()) {
    00681       this->OnFailure();
    00682       return;
    00683     }
    00684
    00685     NetworkHTTPSocketHandler::Connect(str, this);
    00686     return;
    00687   }
    00688
    00689 #undef check
    00690 #undef check_and_terminate
    00691 }
    00692
    00696 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
    00697   NetworkContentSocketHandler(),
    00698   http_response_index(-2),
    00699   curFile(NULL),
    00700   curInfo(NULL),
    00701   isConnecting(false)
    00702 {
    00703 }
    00704
    00706 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
    00707 {
    00708   delete this->curInfo;
    00709   if (this->curFile != NULL) fclose(this->curFile);
    00710
    00711   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    00712 }
    00713
    00715 class NetworkContentConnecter : TCPConnecter {
    00716 public:
    00721   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
    00722
    00723   virtual void OnFailure()
    00724   {
    00725     _network_content_client.isConnecting = false;
    00726     _network_content_client.OnConnect(false);
    00727   }
    00728
    00729   virtual void OnConnect(SOCKET s)
    00730   {
    00731     assert(_network_content_client.sock == INVALID_SOCKET);
    00732     _network_content_client.isConnecting = false;
    00733     _network_content_client.sock = s;
    00734     _network_content_client.Reopen();
    00735     _network_content_client.OnConnect(true);
    00736   }
    00737 };
    00738
    00742 void ClientNetworkContentSocketHandler::Connect()
    00743 {
    00744   this->lastActivity = _realtime_tick;
    00745
    00746   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
    00747   this->isConnecting = true;
    00748   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
    00749 }
    00750
    00754 void ClientNetworkContentSocketHandler::Close()
    00755 {
    00756   if (this->sock == INVALID_SOCKET) return;
    00757   NetworkContentSocketHandler::Close();
    00758
    00759   this->OnDisconnect();
    00760 }
    00761
    00766 void ClientNetworkContentSocketHandler::SendReceive()
    00767 {
    00768   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
    00769
    00770   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
    00771     this->Close();
    00772     return;
    00773   }
    00774
    00775   if (this->CanSendReceive()) {
    00776     this->ReceivePackets();
    00777     this->lastActivity = _realtime_tick;
    00778   }
    00779
    00780   this->SendPackets();
    00781 }
    00782
    00787 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
    00788 {
    00789   /* When we tried to download it already, don't try again */
    00790   if (this->requested.Contains(cid)) return;
    00791
    00792   *this->requested.Append() = cid;
    00793   assert(this->requested.Contains(cid));
    00794   this->RequestContentList(1, &cid);
    00795 }
    00796
    00802 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
    00803 {
    00804   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00805     ContentInfo *ci = *iter;
    00806     if (ci->id == cid) return ci;
    00807   }
    00808   return NULL;
    00809 }
    00810
    00811
    00816 void ClientNetworkContentSocketHandler::Select(ContentID cid)
    00817 {
    00818   ContentInfo *ci = this->GetContent(cid);
    00819   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
    00820
    00821   ci->state = ContentInfo::SELECTED;
    00822   this->CheckDependencyState(ci);
    00823 }
    00824
    00829 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
    00830 {
    00831   ContentInfo *ci = this->GetContent(cid);
    00832   if (ci == NULL || !ci->IsSelected()) return;
    00833
    00834   ci->state = ContentInfo::UNSELECTED;
    00835   this->CheckDependencyState(ci);
    00836 }
    00837
    00839 void ClientNetworkContentSocketHandler::SelectAll()
    00840 {
    00841   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00842     ContentInfo *ci = *iter;
    00843     if (ci->state == ContentInfo::UNSELECTED) {
    00844       ci->state = ContentInfo::SELECTED;
    00845       this->CheckDependencyState(ci);
    00846     }
    00847   }
    00848 }
    00849
    00851 void ClientNetworkContentSocketHandler::SelectUpgrade()
    00852 {
    00853   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00854     ContentInfo *ci = *iter;
    00855     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
    00856       ci->state = ContentInfo::SELECTED;
    00857       this->CheckDependencyState(ci);
    00858     }
    00859   }
    00860 }
    00861
    00863 void ClientNetworkContentSocketHandler::UnselectAll()
    00864 {
    00865   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00866     ContentInfo *ci = *iter;
    00867     if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
    00868   }
    00869 }
    00870
    00872 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
    00873 {
    00874   switch (ci->state) {
    00875     case ContentInfo::SELECTED:
    00876     case ContentInfo::AUTOSELECTED:
    00877       this->Unselect(ci->id);
    00878       break;
    00879
    00880     case ContentInfo::UNSELECTED:
    00881       this->Select(ci->id);
    00882       break;
    00883
    00884     default:
    00885       break;
    00886   }
    00887 }
    00888
    00894 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
    00895 {
    00896   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00897     const ContentInfo *ci = *iter;
    00898     if (ci == child) continue;
    00899
    00900     for (uint i = 0; i < ci->dependency_count; i++) {
    00901       if (ci->dependencies[i] == child->id) {
    00902         *parents.Append() = ci;
    00903         break;
    00904       }
    00905     }
    00906   }
    00907 }
    00908
    00914 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
    00915 {
    00916   *tree.Append() = child;
    00917
    00918   /* First find all direct parents. We can't use the "normal" iterator as
    00919    * we are including stuff into the vector and as such the vector's data
    00920    * store can be reallocated (and thus move), which means out iterating
    00921    * pointer gets invalid. So fall back to the indices. */
    00922   for (uint i = 0; i < tree.Length(); i++) {
    00923     ConstContentVector parents;
    00924     this->ReverseLookupDependency(parents, tree[i]);
    00925
    00926     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
    00927       tree.Include(*piter);
    00928     }
    00929   }
    00930 }
    00931
    00936 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
    00937 {
    00938   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
    00939     /* Selection is easy; just walk all children and set the
    00940      * autoselected state. That way we can see what we automatically
    00941      * selected and thus can unselect when a dependency is removed. */
    00942     for (uint i = 0; i < ci->dependency_count; i++) {
    00943       ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00944       if (c == NULL) {
    00945         this->DownloadContentInfo(ci->dependencies[i]);
    00946       } else if (c->state == ContentInfo::UNSELECTED) {
    00947         c->state = ContentInfo::AUTOSELECTED;
    00948         this->CheckDependencyState(c);
    00949       }
    00950     }
    00951     return;
    00952   }
    00953
    00954   if (ci->state != ContentInfo::UNSELECTED) return;
    00955
    00956   /* For unselection we need to find the parents of us. We need to
    00957    * unselect them. After that we unselect all children that we
    00958    * depend on and are not used as dependency for us, but only when
    00959    * we automatically selected them. */
    00960   ConstContentVector parents;
    00961   this->ReverseLookupDependency(parents, ci);
    00962   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00963     const ContentInfo *c = *iter;
    00964     if (!c->IsSelected()) continue;
    00965
    00966     this->Unselect(c->id);
    00967   }
    00968
    00969   for (uint i = 0; i < ci->dependency_count; i++) {
    00970     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00971     if (c == NULL) {
    00972       DownloadContentInfo(ci->dependencies[i]);
    00973       continue;
    00974     }
    00975     if (c->state != ContentInfo::AUTOSELECTED) continue;
    00976
    00977     /* Only unselect when WE are the only parent. */
    00978     parents.Clear();
    00979     this->ReverseLookupDependency(parents, c);
    00980
    00981     /* First check whether anything depends on us */
    00982     int sel_count = 0;
    00983     bool force_selection = false;
    00984     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00985       if ((*iter)->IsSelected()) sel_count++;
    00986       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
    00987     }
    00988     if (sel_count == 0) {
    00989       /* Nothing depends on us */
    00990       this->Unselect(c->id);
    00991       continue;
    00992     }
    00993     /* Something manually selected depends directly on us */
    00994     if (force_selection) continue;
    00995
    00996     /* "Flood" search to find all items in the dependency graph*/
    00997     parents.Clear();
    00998     this->ReverseLookupTreeDependency(parents, c);
    00999
    01000     /* Is there anything that is "force" selected?, if so... we're done. */
    01001     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    01002       if ((*iter)->state != ContentInfo::SELECTED) continue;
    01003
    01004       force_selection = true;
    01005       break;
    01006     }
    01007
    01008     /* So something depended directly on us */
    01009     if (force_selection) continue;
    01010
    01011     /* Nothing depends on us, mark the whole graph as unselected.
    01012      * After that's done run over them once again to test their children
    01013      * to unselect. Don't do it immediately because it'll do exactly what
    01014      * we're doing now. */
    01015     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    01016       const ContentInfo *c = *iter;
    01017       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
    01018     }
    01019     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    01020       this->CheckDependencyState(this->GetContent((*iter)->id));
    01021     }
    01022   }
    01023 }
    01024
    01026 void ClientNetworkContentSocketHandler::Clear()
    01027 {
    01028   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    01029
    01030   this->infos.Clear();
    01031   this->requested.Clear();
    01032 }
    01033
    01034 /*** CALLBACK ***/
    01035
    01036 void ClientNetworkContentSocketHandler::OnConnect(bool success)
    01037 {
    01038   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    01039     ContentCallback *cb = *iter;
    01040     cb->OnConnect(success);
    01041     if (iter != this->callbacks.End() && *iter == cb) iter++;
    01042   }
    01043 }
    01044
    01045 void ClientNetworkContentSocketHandler::OnDisconnect()
    01046 {
    01047   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    01048     ContentCallback *cb = *iter;
    01049     cb->OnDisconnect();
    01050     if (iter != this->callbacks.End() && *iter == cb) iter++;
    01051   }
    01052 }
    01053
    01054 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
    01055 {
    01056   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    01057     ContentCallback *cb = *iter;
    01058     cb->OnReceiveContentInfo(ci);
    01059     if (iter != this->callbacks.End() && *iter == cb) iter++;
    01060   }
    01061 }
    01062
    01063 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, int bytes)
    01064 {
    01065   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    01066     ContentCallback *cb = *iter;
    01067     cb->OnDownloadProgress(ci, bytes);
    01068     if (iter != this->callbacks.End() && *iter == cb) iter++;
    01069   }
    01070 }
    01071
    01072 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
    01073 {
    01074   ContentInfo *ci = this->GetContent(cid);
    01075   if (ci != NULL) {
    01076     ci->state = ContentInfo::ALREADY_HERE;
    01077   }
    01078
    01079   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    01080     ContentCallback *cb = *iter;
    01081     cb->OnDownloadComplete(cid);
    01082     if (iter != this->callbacks.End() && *iter == cb) iter++;
    01083   }
    01084 }
    01085
    01086 #endif /* ENABLE_NETWORK */