network_content.cpp

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

Generated on Sat Jun 5 21:52:06 2010 for OpenTTD by  doxygen 1.6.1