/Volumes/Plantain/MyDocuments/Projects/MacTrek/MacTrek/Design Stuff/COW/parsemeta.c

00001 /* meta.c
00002  * 
00003  * $Log: parsemeta.c,v $
00004  * Revision 1.9  2006/02/22 22:55:22  quozl
00005  * fix ReadMetasRecv regression
00006  *
00007  * Revision 1.8  2006/01/27 09:57:27  quozl
00008  * *** empty log message ***
00009  *
00010  * Revision 1.7  2001/07/24 05:00:21  quozl
00011  * fix metaserver window delays
00012  *
00013  * Revision 1.6  1999/08/02 07:21:48  carlos
00014  *
00015  * Fixed a minor bug in the UDP menu display where it wouldn't highlight
00016  * the selection.
00017  *
00018  * --Carlos V.
00019  *
00020  * Revision 1.5  1999/03/25 20:56:26  siegl
00021  * CygWin32 autoconfig fixes
00022  *
00023  * Revision 1.4  1999/03/07 10:29:21  siegl
00024  * History log formating
00025  *
00026  *
00027  * - Nick Trown         May 1993    Original Version. 
00028  * - Andy McFadden      May 1993 ?  Connect to Metaserver. 
00029  * - BDyess (Paradise)  ???         Bug Fixes.  Add server type field. 
00030  * - Michael Kellen     Jan 1995    Don't list Paradise Servers. List empty servers. 
00031  * - James Soutter      Jan 1995    Big Parsemeta changes.  
00032  *       Included some Paradise Code.  Added Known Servers
00033  *       Option.  Added option for metaStatusLevel.  Bug Fixes.
00034  * - Jonathan Shekter Aug 1995 --  changed to read into buffer in all cases,
00035  *       use findfile() interface for cachefiles.
00036  * - James Cameron, Carlos Villalpando Mar 1999 --  Added UDP metaserver
00037  *     queries
00038  */
00039 
00040 #include <stdio.h>
00041 #include <ctype.h>
00042 #include <sys/types.h>
00043 #include <sys/time.h>
00044 #include <stdlib.h>
00045 #include <sys/socket.h>
00046 #include <arpa/inet.h>
00047 #include <netdb.h>
00048 #include <errno.h>
00049 #include "defs.h"
00050 #include "struct.h"
00051 #include "data.h"
00052 #include "parsemeta.h"
00053 
00054 /* Constants */
00055 #define BUF     6144
00056 #define MAXMETABYTES 2048       /* maximum metaserver UDP packet size    */
00057 static int msock = -1;          /* the socket to talk to the metaservers */
00058 static int sent = 0;            /* number of solicitations sent          */
00059 static int seen = 0;            /* number of replies seen                */
00060 static int verbose = 0;         /* whether to talk a lot about it all    */
00061 static int type;                /* type of connection requested          */
00062 
00063 /* Local Types */
00064 int num_servers = 0;            /* The number of servers.       */
00065 int metaHeight = 0;                     /* The number of list lines.    */
00066 char *metaWindowName;                   /* The window's name.           */
00067 char   *serverName = NULL;
00068 int statusLevel;
00069 struct servers *serverlist = NULL;      /* The record for each server.  */
00070 
00071 
00072 char   *metaserver = "metaserver.netrek.org";    /* US metaserver. */
00073 int     metaport = 3521;                         /* HAVE to use nicely *
00074 
00075 
00076 
00077 
00078 /* The status strings:  The order of the strings up until statusNull is
00079  * important because the meta-client will display all the strings up to a
00080  * particular point.
00081  * 
00082  * The strings after statusNull are internal status types and are formatted
00083  * separatly from the other strings.
00084  * 
00085  * The string corresponding to "statusNull" is assigned to thoes servers which
00086  * have "statusNobody" or earlier strings in old, cached, meta-server data. */
00087 
00088 char   *statusStrings[] =
00089 {"OPEN:", "Wait queue:", "Nobody", "Timed out", "No connection",
00090  "Active", "CANNOT CONNECT", "DEFAULT SERVER"};
00091 
00092 enum statusTypes {
00093   statusOpen = 0, statusWait, statusNobody, statusTout, statusNoConnect,
00094   statusNull, statusCantConnect, statusDefault
00095 };
00096 
00097 const int defaultStatLevel = statusTout;
00098 
00099 
00100 /* Functions */
00101 extern void terminate(int error);
00102 
00103 static int open_port(char *host, int port, int verbose)
00104 /* The connection to the metaserver is by Andy McFadden. This calls the
00105  * metaserver and parses the output into something useful */
00106 {
00107   struct sockaddr_in addr;
00108   struct hostent *hp;
00109   int     sock;
00110 
00111   /* Connect to the metaserver */
00112   /* get numeric form */
00113   if ((addr.sin_addr.s_addr = inet_addr(host)) == -1) {
00114     if ((hp = gethostbyname(host)) == NULL) {
00115       if (verbose) fprintf(stderr, "unknown host '%s'\n", host);
00116       return (-1);
00117     } else {
00118       addr.sin_addr.s_addr = *(long *) hp->h_addr;
00119     }
00120   }
00121   addr.sin_family = AF_INET;
00122   addr.sin_port = htons(port);
00123   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
00124     if (verbose) perror("socket");
00125     return (-1);
00126   }
00127   if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00128     if (verbose) perror("connect");
00129     close(sock);
00130     return (-1);
00131   }
00132   return (sock);
00133 }
00134 
00135 
00136 static void parseInput(char *in, FILE * out)
00137 /* Read the information we need from the meta-server. */
00138 {
00139   char    line[LINE + 1], *numstr, *point, **statStr;
00140   struct servers *slist;
00141   int     rtn, max_servers;
00142   int     count;
00143 
00144   
00145   /* Create some space to hold the entries that will be read.  More space can
00146    * be added later */
00147 
00148   serverlist = (struct servers *) malloc(sizeof(struct servers) * 10);
00149 
00150   max_servers = 10;
00151   num_servers = 0;
00152 
00153 
00154   /* Add the default server */
00155   if (serverName != NULL) {
00156     strcpy(serverlist[num_servers].address, serverName);
00157     serverlist[num_servers].port = xtrekPort;
00158     serverlist[num_servers].status = statusDefault;
00159     serverlist[num_servers].players = 0;
00160     serverlist[num_servers].typeflag = ' ';
00161     num_servers++;
00162   }
00163 
00164 
00165   while (1) {
00166     /* Read a line */
00167     point = line;
00168     count = LINE + 1;
00169     
00170     do {
00171       if (!(--count)) {
00172         fputs("Warning: Line from meta server was too long!!!\n", stderr);
00173         ++point;                           /* Pretend we read a '\n' */
00174         break;
00175       }
00176       
00177       rtn = *in++;
00178       if (!rtn)                              /* use a zero to mark end of buffer */
00179         return;
00180       
00181       *(point++) = rtn;
00182     }
00183     while (rtn != EOF && rtn != '\n');
00184     
00185     *(--point) = '\0';
00186     
00187     if (out != NULL) {                           /* Save a copy of the stuff
00188                                                   * we read */
00189       fputs(line, out);
00190       putc('\n', out);
00191     }
00192 
00193 
00194       /* Find somewhere to put the information that is just about to be
00195        * parsed */
00196 
00197     if (num_servers >= max_servers) {
00198       max_servers += 5;
00199       size_t size = sizeof(struct servers) * max_servers;
00200       serverlist = (struct servers *) realloc(serverlist, size);
00201     }
00202     
00203     slist = serverlist + num_servers;
00204 
00205 
00206 
00207     /* Is this a line we want? */
00208 
00209     if (sscanf(line, "-h %s -p %d %d %d",
00210                slist->address, &(slist->port),
00211                &(slist->age)) != 3) {
00212       continue;
00213     }
00214     
00215     /* Find the status of the server, eg "Not responding". */
00216     
00217     for (statStr = statusStrings + statusLevel
00218            ; statStr >= statusStrings
00219            ; --statStr) {
00220       if ((numstr = strstr(line, *statStr)) != NULL) {
00221         (slist->status) = statStr - statusStrings;
00222         (slist->players) = 0;
00223         sscanf(numstr, "%*[^0123456789] %d", &(slist->players));
00224         break;
00225       }
00226     }
00227     
00228     if (statStr < statusStrings)               /* No status was allocated */
00229       continue;
00230     
00231     
00232     /* Read the flags */
00233     
00234     slist->typeflag = *(point - 1);
00235     
00236     
00237     /* Don't list Paradise Servers  */
00238     
00239     if (slist->typeflag != 'P') {
00240       
00241 #ifdef DEBUG 
00242       printf("HOST:%-30s PORT:%-6d %12s %-5d %d %c\n",
00243              serverlist[num_servers].address,
00244              serverlist[num_servers].port,
00245              statusStrings[serverlist[num_servers].status],
00246              serverlist[num_servers].players,
00247              serverlist[num_servers].typeflag);
00248 #endif
00249       
00250       ++num_servers;
00251     }
00252   }
00253 } 
00254 
00255 static int ReadMetasSend()
00256 {
00257   char *metaservers;            /* our copy of the metaserver host names */
00258   char *token;                  /* current metaserver host name          */
00259   struct sockaddr_in address;   /* the address of the metaservers        */
00260   int length;                   /* length of the address                 */
00261  
00262   /* host names of metaservers, default in data.c, comma delimited */ 
00263   if ((getdefault("metaserver")) != NULL)
00264     metaserver = getdefault("metaserver");
00265 
00266   /* port number of metaservers, unlikely to change, not a list */
00267   metaport = intDefault("metaport", metaport);
00268 
00269   /* whether to report everything that happens */
00270   verbose = booleanDefault("metaverbose", verbose);
00271 
00272   /* create the socket */
00273   if (msock < 0) {
00274     msock = socket(AF_INET, SOCK_DGRAM, 0);
00275     if (msock < 0) { perror("ReadMetasSend: socket"); return 0; }
00276     
00277     /* bind the socket to any address */
00278     address.sin_addr.s_addr = INADDR_ANY;
00279     address.sin_family      = AF_INET;
00280     address.sin_port        = 0;
00281     if (bind(msock,(struct sockaddr *)&address, sizeof(address)) < 0) {
00282       perror("ReadMetasSend: bind");
00283       close(msock);
00284       return 0;
00285     }
00286   }
00287 
00288   /* send request to a multicast metaserver on local area network */
00289   address.sin_family = AF_INET;
00290   address.sin_port = htons(metaport);
00291   address.sin_addr.s_addr = inet_addr("224.0.0.1");
00292   if (verbose) fprintf(stderr, 
00293                        "Requesting player list from nearby servers on %s\n",
00294                        inet_ntoa(address.sin_addr));
00295   if (sendto(msock, "?", 1, 0, (struct sockaddr *)&address,
00296              sizeof(address)) < 0) {
00297     perror("ReadMetasSend: sendto");
00298   } else {
00299     sent++;
00300   }
00301 
00302   /* try each of the comma delimited metaserver host names */
00303   metaservers = strdup(metaserver);
00304   token = strtok(metaservers,",");
00305 
00306   while (token != NULL) {
00307     /* compose the address structure */
00308     address.sin_family = AF_INET;
00309     address.sin_port = htons(metaport);
00310 
00311     /* skip any blanks */
00312     while (*token == ' ') token++;
00313 
00314     /* attempt numeric translation first */
00315     if ((address.sin_addr.s_addr = inet_addr(token)) == -1) {
00316       struct hostent *hp;
00317         
00318       /* then translation by name */
00319       if ((hp = gethostbyname(token)) == NULL) {
00320         /* if it didn't work, return failure and warning */
00321         fprintf(stderr,
00322           "Cannot resolve host %s, check for DNS problems?\n",
00323           token);
00324       } else {
00325         int i;
00326 
00327         /* resolution worked, send a query to every metaserver listed */
00328         for(i=0;;i++) {
00329 
00330           /* check for end of list of addresses */
00331           if (hp->h_addr_list[i] == NULL) break;
00332           address.sin_addr.s_addr = *(long *) hp->h_addr_list[i];
00333           if (verbose) fprintf(stderr,
00334                 "Requesting player list from metaserver %s at %s\n",
00335                 token, inet_ntoa(address.sin_addr));
00336           if (sendto(msock, "?", 1, 0, (struct sockaddr *)&address,
00337                 sizeof(address)) < 0) {
00338             perror("ReadMetasSend: sendto");
00339           } else {
00340             sent++;
00341           }
00342         }
00343       }
00344     } else {
00345       /* call to inet_addr() worked, host name is in IP address form */
00346       if (verbose) fprintf(stderr, 
00347                            "Requesting player list from metaserver %s\n",
00348                            inet_ntoa(address.sin_addr));
00349       if (sendto(msock, "?", 1, 0, (struct sockaddr *)&address,
00350         sizeof(address)) < 0) {
00351         perror("ReadMetasSend: sendto");
00352       } else {
00353         sent++;
00354       }
00355     }
00356 
00357     /* look for next host name in list */
00358     token = strtok(NULL,",");
00359   } /* while (token != NULL) */
00360   return sent;
00361 }
00362 
00363 /* allocate or extend the server list */
00364 static void grow(int servers)
00365 {
00366   int size;
00367   if (serverlist == NULL) {
00368     size = sizeof(struct servers) * servers;
00369     serverlist = (struct servers *) malloc(size);
00370   } else {
00371     size = sizeof(struct servers) * ( servers + num_servers );
00372     serverlist = (struct servers *) realloc(serverlist, size);
00373   }
00374 }
00375 
00376 static struct servers *server_find(char *address, int port)
00377 {
00378   int j;
00379 
00380   for(j=0;j<num_servers;j++) {
00381     struct servers *sp = serverlist + j;
00382     if ((!strcmp(sp->address, address)) && (sp->port == port)) {
00383       return sp;
00384     }
00385   }
00386   return NULL;
00387 }
00388 
00389 static void version_r(struct sockaddr_in *address) {
00390   char *p;
00391   int servers, i, j;
00392   time_t now = time(NULL);
00393 
00394   /* number of servers */
00395   p = strtok(NULL,"\n");
00396   if (p == NULL) return;
00397   servers = atoi(p);
00398 
00399   /* sanity check on number of servers */
00400   if (servers > 2048) return;
00401   if (servers < 0) return;
00402 
00403   if (verbose) fprintf(stderr,
00404                        "Metaserver at %s responded with %d server%s\n",
00405                        inet_ntoa(address->sin_addr),
00406                        servers,
00407                        servers == 1 ? "" : "s" );
00408 
00409   if (servers == 0) return;
00410 
00411   grow(servers);
00412 
00413   /* for each server listed by this metaserver packet */
00414   for(i=0;i<servers;i++) {
00415     struct servers *sp = NULL;
00416     char *host, type;
00417     int port, status, age, players, queue, throwaway;
00418       
00419     throwaway = 0;
00420 
00421     host = strtok(NULL,",");            /* hostname */
00422     if (host == NULL) continue;
00423 
00424     p = strtok(NULL,",");               /* port */
00425     if (p == NULL) continue;
00426     port = atoi(p);
00427 
00428     p = strtok(NULL,",");               /* status */
00429     if (p == NULL) continue;
00430     status = atoi(p);
00431 
00432     /* ignore servers based on status */
00433     if (status > statusLevel)
00434       throwaway++;
00435     /* the sp->why_dead workaround */
00436     if (status == 6)
00437       if ((status - 3) <= statusLevel) 
00438         throwaway--;
00439 
00440     p = strtok(NULL,",");               /* age (of data in seconds) */
00441     if (p == NULL) continue;
00442     age = atoi(p);
00443 
00444     p = strtok(NULL,",");               /* players */
00445     if (p == NULL) continue;
00446     players = atoi(p);
00447 
00448     p = strtok(NULL,",");               /* queue size */
00449     if (p == NULL) continue;
00450     queue = atoi(p);
00451 
00452     p = strtok(NULL,"\n");              /* server type */
00453     if (p == NULL) continue;
00454     type = p[0];
00455 
00456     /* ignore paradise servers */
00457     if (type == 'P') throwaway++;
00458 
00459     /* if it's to be thrown away, do not add this server, skip to next */
00460     if (throwaway) continue;
00461 
00462     /* find in current server list? */
00463     sp = server_find(host, port);
00464 
00465     /* if it was found, check age */
00466     if (sp != NULL) {
00467       if ((now-age) < (sp->when-sp->age)) {
00468         sp->age = now - (sp->when-sp->age);
00469         sp->when = now;
00470         sp->refresh = 1;
00471         sp->lifetime = 20;
00472         continue;
00473       } else {
00474         sp->age = age;
00475         sp->when = now;
00476         sp->lifetime = 20;
00477       }
00478     } else {
00479       /* not found, store it at the end of the list */
00480       sp = serverlist + sizeof(struct servers);
00481       strncpy(sp->address,host,LINE);
00482       sp->port = port;
00483       sp->age = age;
00484       sp->when = now;
00485       sp->lifetime = 4;
00486       num_servers++;
00487     }
00488     sp->refresh = 1;
00489 
00490     /* from meta.h of metaserver code */
00491 #define SS_WORKING 0
00492 #define SS_QUEUE 1
00493 #define SS_OPEN 2
00494 #define SS_EMPTY 3
00495 #define SS_NOCONN 4
00496 #define SS_INIT 5
00497     /* not a real metaserver number, but overcomes a limitation of dropping text */
00498     /* description of sp->why_dead */
00499 #define SS_TOUT 6
00500     switch (status) {
00501     case SS_QUEUE:
00502       sp->status = statusWait;
00503       sp->players = queue;
00504       break;
00505     case SS_OPEN:
00506       sp->status = statusOpen;
00507       sp->players = players;
00508       break;
00509     case SS_EMPTY:
00510       sp->status = statusNobody;
00511       sp->players = 0;
00512       break;
00513     case SS_TOUT:
00514       sp->status = statusTout;
00515       sp->players = 0;
00516       break;
00517     case SS_NOCONN:                     /* no connection */
00518     case SS_WORKING:            /* metaserver should not return this */
00519     case SS_INIT:                       /* nor this */
00520     default:
00521       sp->status = statusNoConnect;
00522       sp->players = 0;
00523       break;
00524     }
00525     sp->typeflag = type;
00526     strcpy(sp->comment, "");
00527   }
00528 }
00529 
00530 static void version_s(struct sockaddr_in *address)
00531 {
00532   char *p;
00533   time_t now = time(NULL);
00534   int throwaway = 0;
00535 
00536   /* use return address on packet as host address for this server,
00537   since it isn't practical for the server to know it's own address; as
00538   is the case with multihomed machines */
00539   char *host = inet_ntoa(address->sin_addr);
00540 
00541   if (verbose) fprintf(stderr, "Server at %s responded\n", host);
00542 
00543   p = strtok(NULL,","); /* server type */
00544   if (p == NULL) return;
00545   char type = p[0];
00546   
00547   /* ignore paradise servers */
00548   if (type == 'P') return;
00549   
00550   p = strtok(NULL,",");         /* comment */
00551   if (p == NULL) return;
00552   char *comment = strdup(p);
00553 
00554   p = strtok(NULL,",");         /* number of ports */
00555   if (p == NULL) return;
00556   int ports = atoi(p);
00557 
00558   // TODO: accept more than one port reply
00559   
00560   p = strtok(NULL,",");         /* port */
00561   if (p == NULL) return;
00562   int port = atoi(p);
00563   
00564   p = strtok(NULL,",");         /* players */
00565   if (p == NULL) return;
00566   int players = atoi(p);
00567 
00568   p = strtok(NULL,",");         /* queue size */
00569   if (p == NULL) return;
00570   int queue = atoi(p);
00571 
00572   /* find in current server list? */
00573   struct servers *sp = server_find(host, port);
00574 
00575   /* if it was not found, add it to the end of the list */
00576   if (sp == NULL) {
00577     grow(1);
00578     sp = serverlist + num_servers;
00579     num_servers++;
00580   }
00581 
00582   /* add or update the entry */
00583   strncpy(sp->address, host, LINE);
00584   sp->port = port;
00585   sp->age = 0;
00586   sp->when = now;
00587   sp->refresh = 1;
00588   sp->lifetime = 20;
00589   sp->players = players;
00590   sp->status = statusOpen;
00591   sp->typeflag = type;
00592   strncpy(sp->comment, comment, LINE);
00593   free(comment);
00594 }
00595 
00596 static int ReadMetasRecv(int x)
00597 {
00598   struct sockaddr_in address;   /* the address of the metaservers        */
00599   socklen_t length;             /* length of the address                 */
00600   int bytes;                    /* number of bytes received from meta'   */
00601   fd_set readfds;               /* the file descriptor set for select()  */
00602   struct timeval timeout;       /* timeout for select() call             */
00603   char packet[MAXMETABYTES];    /* buffer for packet returned by meta'   */
00604   time_t now;                   /* current time for age calculations     */
00605   int isawsomething = 0;        /* have I seen a response at all?        */ 
00606 
00607   /* now await and process replies */
00608   while(1) {
00609     char *p;
00610 
00611     FD_ZERO(&readfds);
00612     if (msock >= 0) FD_SET(msock, &readfds);
00613     timeout.tv_sec=4;
00614     timeout.tv_usec=0;
00615 
00616     if (x != -1) FD_SET(x, &readfds);
00617     if (select(FD_SETSIZE, &readfds, NULL, NULL,
00618                (x != -1) ? NULL : &timeout) < 0) {
00619       perror("ReadMetasRecv: select");
00620       return 0;
00621     }
00622 
00623     /* if x activity, return immediately */
00624     if (x != -1 && FD_ISSET(x, &readfds)) return 0;
00625     if (msock < 0) return 0;
00626 
00627     /* if the wait timed out, then we give up */
00628     if (!FD_ISSET(msock, &readfds)) {
00629       if(isawsomething)
00630         return 1;          /* I do have new metaserver data */
00631       else
00632         return 0;          /* I don't have metaserver data at all */
00633     }
00634 
00635     /* so we have data back from a metaserver or server */
00636     isawsomething++;
00637     length = sizeof(address);
00638     bytes = recvfrom(msock, packet, MAXMETABYTES, 0, (struct sockaddr *)&address,
00639         &length );
00640     if (bytes < 0) {
00641       perror("ReadMetasRecv: recvfrom");
00642       return 0;
00643     }
00644 
00645     /* terminate the packet received */
00646     packet[bytes] = 0;
00647 #ifdef DEBUG
00648     fprintf(stderr, "%s", packet);
00649 #endif /* DEBUG */
00650 
00651     /* process the packet, updating our server list */
00652 
00653     /* version identifier */
00654     p = strtok(packet,",");
00655     if (p == NULL) continue;
00656 
00657     switch (p[0]) {
00658     case 'r': version_r(&address); seen++; break;
00659     case 's': version_s(&address); seen++; break;
00660     }
00661 
00662     /* finished processing the packet */
00663 
00664     /* if this is the first call, return on first reply, for sizing list */
00665     if (x == -1) return 1;
00666 
00667     /* if we have seen the same number of replies to what we sent, end */
00668     if (sent == seen) return 1;
00669   }
00670 }
00671 
00672 static void SaveMetasCache()
00673 {
00674   FILE *cache;
00675   char cacheFileName[PATH_MAX];
00676   char tmpFileName[PATH_MAX];
00677   char *cacheName;
00678   int len;
00679 
00680   cacheName = getdefault("metaUDPCache");
00681   /* overwrite existing file if possible */
00682   if (cacheName && !findfile(cacheName, cacheFileName))
00683    strcpy(cacheFileName, cacheName);
00684 
00685   if (cacheName)
00686     {
00687       len = strlen(cacheFileName);
00688       strcpy(tmpFileName, cacheFileName);
00689 
00690       /* create a temporary file with roughly the same name */
00691       if ((cacheFileName[len - 1] == 'T') || (cacheFileName[len - 1] == 't'))
00692         tmpFileName[len-1] = 'R';
00693       else
00694         tmpFileName[len-1] = 'T';
00695       
00696       cache = fopen(tmpFileName, "w");
00697 
00698       if (cache == NULL)
00699         {
00700           fprintf(stderr,
00701                    "Cannot create a metaUDPCache temporary file `%s'\n",
00702               tmpFileName);
00703           fprintf(stderr, "Meta-server read will not be cached.\n");
00704         }
00705     }
00706   else
00707     {
00708       cache = NULL;
00709     }
00710 
00711 
00712   if (cache != NULL)
00713     {
00714 
00715       fwrite(&statusLevel, sizeof(statusLevel), 1, cache);
00716       fwrite(&num_servers, sizeof(num_servers), 1, cache);
00717       fwrite(serverlist, sizeof(struct servers), num_servers, cache);
00718 
00719       fclose(cache);
00720 
00721 #ifdef WIN32
00722       /* Can't rename file to existing name under NT */
00723 #ifdef _MSC_VER
00724       _unlink(cacheName);
00725 #else
00726       unlink(cacheName);
00727 #endif
00728 #endif    
00729       if (rename(tmpFileName, cacheName) == -1)
00730         perror("Could not rename new cache file");
00731     }
00732 
00733 }
00734 
00735 static void LoadMetasCache()
00736 {
00737   FILE *cache;
00738   char *cacheName;
00739   char cacheFileName[PATH_MAX];
00740   int  i;
00741 
00742   cacheName = getdefault("metaUDPCache");
00743 
00744   if(!cacheName)
00745     {
00746       num_servers = 0;
00747       return;
00748     }
00749 
00750   findfile(cacheName, cacheFileName);
00751 
00752   cache = fopen(cacheFileName, "r");
00753   if (cache == NULL) 
00754     { 
00755       num_servers = 0; 
00756       return; 
00757     }
00758  
00759   /* ignore the cache if user changed statusLevel */
00760   fread(&i, sizeof(i), 1, cache);
00761   if (i != statusLevel)
00762     {
00763       num_servers = 0;
00764       fclose(cache);
00765       return; 
00766     }
00767  
00768 
00769   /* read the server list into memory from the file */
00770   fread(&num_servers, sizeof(num_servers), 1, cache);
00771   serverlist = (struct servers *) malloc(sizeof(struct servers)*num_servers);
00772   fread(serverlist, sizeof(struct servers), num_servers, cache);
00773   fclose(cache);
00774   
00775   /* hunt and kill old server lines from cache */
00776   for(i=0;i<num_servers;i++)
00777     {
00778       int j;
00779 
00780       /* mark each server as needing to be refreshed */
00781       serverlist[i].refresh = 1;
00782 
00783       /* skip the deletion below if the entry was received recently */
00784       if (serverlist[i].lifetime-- > 0) continue;
00785 
00786       /* delete this entry by moving the ones above down */
00787       for(j=i;j<num_servers-1;i++)
00788         {
00789           memcpy(&serverlist[j],&serverlist[j+1],sizeof(struct servers));
00790         }
00791       
00792       /* adjust the current entry pointer, limit, and resize the memory */
00793       i--;
00794       num_servers--;
00795       serverlist = 
00796         (struct servers *) realloc(serverlist, 
00797                                    sizeof(struct servers) * ( num_servers ));
00798     }
00799 }
00800 
00801 static int ReadFromMeta()
00802 /* Read from the meta-server.  Return TRUE on success and FALSE on failure. */
00803 {
00804   FILE   *out;
00805   char   *cacheName;
00806   char    cacheFileName[PATH_MAX];
00807   char    tmpFileName[PATH_MAX];
00808   char   *sockbuf, *buf;
00809   int     bufleft = BUF - 1;
00810   int     len;
00811   int     sock;
00812 
00813   if ((getdefault("metaserver")) != NULL)
00814     metaserver = getdefault("metaserver");
00815 
00816   metaport = intDefault("metaport", metaport);
00817 
00818   if ((sock = open_port(metaserver, metaport, 1)) <= 0)
00819     {
00820       fprintf(stderr, "Cannot connect to MetaServer (%s , %d)\n",
00821               metaserver, metaport);
00822       return 0;
00823     }
00824 
00825   /* Allocate a buffer and read until full */
00826   buf = sockbuf = (char *)malloc(BUF);
00827   while (bufleft > 0 && (len = read(sock, buf, bufleft)) > 0)
00828     {
00829       bufleft-=len;
00830       buf += len;
00831 #ifdef DEBUG    
00832       printf("Read %d bytes from Metaserver\n", len);
00833 #endif
00834     }
00835   close (sock);
00836   *buf = 0;                   /* End of buffer marker */
00837 
00838   if (len<0)
00839     {
00840       perror ("read");
00841       free(sockbuf);
00842       return 0;
00843     }
00844 
00845   cacheName = getdefault("metaCache");
00846   if (cacheName && !findfile(cacheName, cacheFileName))
00847    strcpy(cacheFileName, cacheName);        /* overwrite existing file if possible */
00848 
00849   if (cacheName)
00850     {
00851       len = strlen(cacheFileName);
00852       strcpy(tmpFileName, cacheFileName);
00853 
00854       /* Create a temporary file with roughly the same name */
00855       
00856       if ((cacheFileName[len - 1] == 'T') || (cacheFileName[len - 1] == 't'))
00857         tmpFileName[len-1] = 'R';
00858       else
00859         tmpFileName[len-1] = 'T';
00860       
00861       out = fopen(tmpFileName, "w");
00862 
00863       if (out == NULL)
00864         {
00865           fprintf(stderr,
00866                    "Cannot write to the metaCache temporary file `%s'.\n",
00867               tmpFileName);
00868           fprintf(stderr, "Meta-server read will not be cached.\n");
00869         }
00870     }
00871   else
00872     {
00873       out = NULL;
00874     }
00875 
00876   parseInput(sockbuf, out);
00877 
00878   if (out != NULL)
00879   {
00880     fclose(out);
00881 
00882 #ifdef WIN32
00883    /* Can't rename file to existing name under NT */
00884 #ifdef _MSC_VER
00885    _unlink(cacheName);
00886 #else
00887    unlink(cacheName);
00888 #endif
00889 #endif    
00890     if (rename(tmpFileName, cacheName) == -1)
00891       perror("Could not write to cache file");
00892    }
00893 
00894   free(sockbuf);
00895   metaWindowName = "MetaServer List";
00896 
00897   return 1;
00898 }
00899 
00900 
00901 static int ReadFromCache()
00902 /* Read from the cache.  Return TRUE on success and FALSE on failure. */
00903 {
00904   FILE   *in;
00905   char   *cacheName;
00906   struct  servers *slist;
00907   char   *sockbuf, *buf;
00908   int     bufleft = BUF - 1;
00909   int     len;
00910   char    cacheFileName[PATH_MAX];
00911   
00912   cacheName = getdefault("metaCache");
00913 
00914   if (!cacheName)
00915     {
00916       fprintf(stderr,
00917               "You must define the .xtrekrc variable `metaCache' in\n");
00918       fprintf(stderr,
00919               "order to use the `show known servers' option.\n");
00920       return 0;
00921     }
00922   else
00923      if (!findfile(cacheName, cacheFileName) || !(in = fopen(cacheFileName, "r")) )
00924        {
00925          fprintf(stderr,
00926                  "The metaCache file `%s' is empty or not accessable.\n",
00927                  cacheName);
00928          return 0;
00929        }
00930 
00931 
00932   /* Allocate a buffer and read until full. Why do we
00933      go through this silly stuff? Because sockets are
00934      not file handles on all verions of Windows */
00935   buf = sockbuf = (char *)malloc(BUF);
00936 
00937   while (bufleft >0 && ((len = fread(buf, 1, bufleft, in)) > 0))
00938     {
00939     bufleft-=len;
00940     buf += len;
00941 #ifdef DEBUG    
00942     printf("Read %d bytes from Metaserver cache file\n", len);
00943 #endif
00944     }
00945   *buf = 0;                   /* End of buffer marker */
00946   fclose (in);
00947 
00948   if (len<0)
00949     {
00950     perror ("fread");
00951     free(sockbuf);
00952     return 0;
00953     }
00954 
00955   /* Show all servers known to have been reachable */
00956 
00957   if (statusLevel <= statusNobody)
00958     statusLevel = statusNobody;
00959 
00960   parseInput(sockbuf, NULL);
00961 
00962 
00963   /* Don't promise games from old data */
00964   for (slist = serverlist + num_servers - 1
00965        ; slist >= serverlist
00966        ; --slist)
00967     {
00968       if (slist->status <= statusNobody)
00969         slist->status = statusNull;
00970     }
00971 
00972   free(sockbuf);
00973   metaWindowName = "Known Servers";
00974 
00975   return 1;
00976 }
00977 
00978 
00979 void    parsemeta(int metaType)
00980 /* Read and Parse the metaserver information, either from the metaservers
00981  * by UDP (1), from a single metaserver by TCP (3), or from the cache (2).
00982  * 
00983  * NOTE: This function sets the variable "num_servers" which is
00984  * used in newwin() to set the height of the meta-server window.
00985  */
00986 {
00987   statusLevel = intDefault("metaStatusLevel", defaultStatLevel);
00988 
00989   if (statusLevel < 0)
00990     statusLevel = 0;
00991   else if (statusLevel >= statusNull)
00992     statusLevel = statusNull - 1;
00993 
00994   type = metaType;
00995   switch (type)
00996     {
00997       case 1:
00998         ReadMetasSend();
00999         LoadMetasCache();
01000         if (num_servers == 0) ReadMetasRecv(-1);
01001         if (num_servers != 0) {
01002           metaHeight = num_servers + 5;
01003         } else {
01004           printf("Warning: no response from metaservers, "
01005                  "are you firewalled?\n"
01006                  "         (no reply to probe on UDP port %d)\n", metaport);
01007           metaHeight = num_servers + 10;
01008         }
01009         return;
01010         break;
01011       case 2:
01012         if (ReadFromCache() || ReadFromMeta()) 
01013           {
01014            /* add 2 for header and quit button */
01015             metaHeight = num_servers + 2;
01016             return;
01017           }
01018         terminate(0);
01019         break;
01020       case 3:
01021         if (ReadFromMeta() || ReadFromCache()) 
01022           {
01023            /* add 2 for header and quit button */
01024             metaHeight = num_servers + 2;
01025             return;
01026           }
01027         //terminate(0);
01028         break;
01029     }
01030 }
01031 
01032 
01033 static void metarefresh(int i)
01034 /* Refresh line i in the list */
01035 {
01036   char    buf[LINE + 1];
01037   struct servers *sp;
01038 
01039   /* can't say a thing if line is beyond server list */
01040   if (i >= num_servers) {
01041     /* but we can at least blank the line shown */
01042  //   if (i < metaHeight-3) W_WriteText(metaWin, 0, i+1, color, "", 0, 0);
01043     return;
01044   }
01045 
01046   sp = serverlist + i;
01047 
01048   sprintf(buf, "%-40s %14s ",
01049           strlen(sp->comment) > 0 ? sp->comment : sp->address,
01050           statusStrings[sp->status]);
01051 
01052   if (sp->status <= statusNull)
01053     {
01054       if (sp->status == statusOpen || sp->status == statusWait)
01055         {
01056           /* Don't print the number of players if nobody is playing */
01057           sprintf(buf + strlen(buf), "%-5d  ", sp->players);
01058         }
01059       else
01060         {
01061           strcat(buf, "       ");
01062         }
01063 
01064       switch (sp->typeflag)
01065         {
01066         case 'P':
01067           strcat(buf, "Paradise");
01068           break;
01069         case 'B':
01070           strcat(buf, "Bronco  ");
01071           break;
01072         case 'C':
01073           strcat(buf, "Chaos   ");
01074           break;
01075         case 'I':
01076           strcat(buf, "INL     ");
01077           break;
01078         case 'S':
01079           strcat(buf, "Sturgeon");
01080           break;
01081         case 'H':
01082           strcat(buf, "Hockey  ");
01083           break;
01084         case 'F':
01085           strcat(buf, "Dogfight");
01086           break;
01087         default:
01088           strcat(buf, "Unknown ");
01089           break;
01090         }
01091       
01092       if (type == 1)
01093         {
01094           int age = sp->age;
01095           char *units;
01096 
01097           if (age > 86400)
01098             {
01099               age = age / 86400;
01100               units = "d";
01101             }
01102           else if (age > 3600)
01103             {
01104               age = age / 3600;
01105               units = "h";
01106             }
01107           else if (age > 90)
01108             {
01109               age = age / 60;
01110               units = "m";
01111             }
01112           else
01113             {
01114               units = "s";
01115             }
01116           sprintf(buf + strlen(buf), " %4d%s", age, units);
01117         }
01118     }
01119 
01120 //  W_WriteText(metaWin, 0, i+1, color, buf, strlen(buf), 0);
01121   sp->refresh = 0;
01122 }
01123 
01124 
01125 void    metawindow()
01126 /* Show the meta server menu window */
01127 {
01128   int     i;
01129   char *header;
01130 
01131   if (type == 1) {
01132     header = "Server ----------------------------------------- Status ------ Type ----- Age";
01133   } else {
01134     header = "Server ----------------------------------------- Status ------ Type";
01135   }
01136   //W_WriteText(metaWin, 0, 0, W_Yellow, header, strlen(header), 0);
01137 
01138   for (i = 0; i < metaHeight; i++)
01139     metarefresh(i);
01140     
01141   /* Give the window the right name */
01142 //  W_RenameWindow(metaWin, metaWindowName);
01143 
01144   /* Add additional options */
01145  // if (type == 1)
01146 //    W_WriteText(metaWin, 0, metaHeight-2, W_Yellow, "Refresh", 7, 0);
01147 //  W_WriteText(metaWin, 0, metaHeight-1, W_Yellow, "Quit", 4, 0);
01148 
01149   /* Map window */
01150 //  W_MapWindow(metaWin);
01151 }
01152 
01153 
01154 static void metadone(void)
01155 /* Unmap the metaWindow */
01156 {
01157   /* Unmap window */
01158 //  W_UnmapWindow(metaWin);
01159   if (type == 1) SaveMetasCache();
01160   free(serverlist);
01161 }
01162 
01163 
01164 
01165 

Generated on Fri Jul 28 19:15:18 2006 for MacTrek by  doxygen 1.4.7