network_content.cpp

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

Generated on Sat Jul 31 21:37:48 2010 for OpenTTD by  doxygen 1.6.1