1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
14 /**
15  * @file
16  * Contains image related functions at a high level.  It mostly deals with the
17  * caching of the images, processing the image commands from the server, etc.
18  */
19 
20 #include "client.h"
21 
22 #include <ctype.h>
23 #include <glib/gstdio.h>
24 
25 #ifdef WIN32
26 #include <io.h>
27 #include <direct.h>
28 #endif
29 
30 #include "external.h"
31 
32 /* Rotate right from bsd sum. */
33 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
34 
35 /*#define CHECKSUM_DEBUG*/
36 
37 struct FD_Cache {
38     char    name[MAX_BUF];
39     int	    fd;
40 } fd_cache[MAX_FACE_SETS];
41 
42 /**
43  * Given a filename, this tries to load the data.  It returns 0 success, -1 on
44  * failure.  It returns the data and len, the passed options.  This function
45  * is called only if the client caching feature is enabled.
46  *
47  * @param filename File name of an image to try to load.
48  * @param data Caller-allocated pointer to a buffer to load image into.
49  * @param len Amount of buffer used by the loaded image.
50  * @param csum Returns 0/unset (caller already knows if checksum matches?).
51  *             Changes have made such that the caller knows whether or not
52  *             the checksum matches, so there is little point to re-do it.
53  * @return 0 on success, -1 on failure.
54  */
load_image(char * filename,guint8 * data,int * len,guint32 * csum)55 static int load_image(char *filename, guint8 *data, int *len, guint32 *csum) {
56     int fd, i;
57     char *cp;
58 
59     /* If the name includes an @, then that is a combined image file, so we
60      * need to load the image a bit specially. By using these combined image
61      * files, it reduces number of opens needed. In fact, we keep track of
62      * which ones we have opened to improve performance. Note that while not
63      * currently done, this combined image scheme could be done when storing
64      * images in the player's image cache. */
65     if ((cp = strchr(filename, '@')) != NULL) {
66         char *lp;
67         int offset, last = -1;
68 
69 #ifdef WIN32
70         int length;
71 #endif
72 
73         offset = atoi(cp + 1);
74         lp = strchr(cp, ':');
75         if (!lp) {
76             LOG(LOG_ERROR, "common::load_image",
77                 "Corrupt filename - has '@' but no ':' ?(%s)", filename);
78             return -1;
79         }
80 #ifdef WIN32
81         length = atoi(lp + 1);
82 #endif
83         *cp = 0;
84         for (i = 0; i < MAX_FACE_SETS; i++) {
85             if (!strcmp(fd_cache[i].name, filename)) {
86                 break;
87             }
88             if (last == -1 && fd_cache[i].fd == -1) {
89                 last = i;
90             }
91         }
92         /* Didn't find a matching entry yet, so make one */
93         if (i == MAX_FACE_SETS) {
94             if (last == -1) {
95                 LOG(LOG_WARNING, "common::load_image",
96                     "fd_cache filled up?  unable to load matching cache entry");
97                 *cp = '@';	/* put @ back in string */
98                 return -1;
99             }
100 #ifdef WIN32
101             if ((fd_cache[last].fd = open(filename, O_RDONLY | O_BINARY)) == -1)
102 #else
103             if ((fd_cache[last].fd = open(filename, O_RDONLY)) == -1)
104 #endif
105             {
106                 LOG(LOG_WARNING, "common::load_image", "unable to load listed cache file %s",
107                     filename);
108                 *cp = '@';	/* put @ back in string */
109                 return -1;
110             }
111             strcpy(fd_cache[last].name, filename);
112             i = last;
113         }
114         lseek(fd_cache[i].fd, offset, SEEK_SET);
115 #ifdef WIN32
116         *len = read(fd_cache[i].fd, data, length);
117 #else
118         *len = read(fd_cache[i].fd, data, 65535);
119 #endif
120         *cp = '@';
121     } else {
122 #ifdef WIN32
123         int length = 0;
124         if ((fd = open(filename, O_RDONLY | O_BINARY)) == -1) {
125             return -1;
126         }
127         length = lseek(fd, 0, SEEK_END);
128         lseek(fd, 0, SEEK_SET);
129         *len = read(fd, data, length);
130 #else
131         if ((fd = open(filename, O_RDONLY)) == -1) {
132             return -1;
133         }
134         *len = read(fd, data, 65535);
135 #endif
136         close(fd);
137     }
138 
139     face_info.cache_hits++;
140     *csum = 0;
141     return 0;
142 
143 #if 0
144     /* Shouldn't be needed anymore */
145     *csum = 0;
146     for (i = 0; i < *len; i++) {
147         ROTATE_RIGHT(*csum);
148         *csum += data[i];
149         *csum &= 0xffffffff;
150     }
151 #endif
152 
153 }
154 
155 /****************************************************************************
156  * This is our image caching logic.  We use a hash to make the name lookups
157  * happen quickly - this is done for speed, but also because we don't really
158  * have a good idea on how many images may used.  It also means that as the
159  * cache gets filled up with images in a random order, the lookup is still
160  * pretty quick.
161  *
162  * If a bucket is filled with an entry that is not of the right name,
163  * we store/look for the correct one in the next bucket.
164  */
165 
166 /* This should be a power of 2 */
167 #define IMAGE_HASH  8192
168 
169 Face_Information face_info;
170 
171 /** This holds the name we recieve with the 'face' command so we know what
172  * to save it as when we actually get the face.
173  */
174 static char *facetoname[MAXPIXMAPNUM];
175 
176 
177 struct Image_Cache {
178     char    *image_name;
179     struct Cache_Entry	*cache_entry;
180 } image_cache[IMAGE_HASH];
181 
182 /**
183  * This function is basically hasharch from the server, common/arch.c a few
184  * changes - first, we stop processing when we reach the first . - this is
185  * because I'm not sure if hashing .111 at the end of all the image names will
186  * be very useful.
187  */
image_hash_name(char * str,int tablesize)188 static guint32 image_hash_name(char *str, int tablesize) {
189     guint32 hash = 0;
190     char *p;
191 
192     /* use the same one-at-a-time hash function the server now uses */
193     for (p = str; *p != '\0' && *p != '.'; p++) {
194         hash += *p;
195         hash += hash << 10;
196         hash ^= hash >>  6;
197     }
198     hash += hash <<  3;
199     hash ^= hash >> 11;
200     hash += hash << 15;
201     return hash % tablesize;
202 }
203 
204 /**
205  * This function returns an index into the image_cache for a matching entry,
206  * -1 if no match is found.
207  */
image_find_hash(char * str)208 static gint32 image_find_hash(char *str) {
209     guint32  hash = image_hash_name(str, IMAGE_HASH), newhash;
210 
211     newhash = hash;
212     do {
213         /* No entry - return immediately */
214         if (image_cache[newhash].image_name == NULL) {
215             return -1;
216         }
217         if (!strcmp(image_cache[newhash].image_name, str)) {
218             return newhash;
219         }
220         newhash ++;
221         if (newhash == IMAGE_HASH) {
222             newhash = 0;
223         }
224     } while (newhash != hash);
225 
226     /* If the hash table is full, this is bad because we won't be able to
227      * add any new entries.
228      */
229     LOG(LOG_WARNING, "common::image_find_hash",
230         "Hash table is full, increase IMAGE_CACHE size");
231     return -1;
232 }
233 
234 /**
235  *
236  */
image_remove_hash(char * imagename,Cache_Entry * ce)237 static void image_remove_hash(char *imagename, Cache_Entry *ce) {
238     int	hash_entry;
239     Cache_Entry	*last;
240 
241     hash_entry = image_find_hash(imagename);
242     if (hash_entry == -1) {
243         LOG(LOG_ERROR, "common::image_remove_hash",
244             "Unable to find cache entry for %s, %s", imagename, ce->filename);
245         return;
246     }
247     if (image_cache[hash_entry].cache_entry == ce) {
248         image_cache[hash_entry].cache_entry = ce->next;
249         free(ce->filename);
250         free(ce);
251         return;
252     }
253     last = image_cache[hash_entry].cache_entry;
254     while (last->next && last->next != ce) {
255         last = last->next;
256     }
257     if (!last->next) {
258         LOG(LOG_ERROR, "common::image_rmove_hash",
259             "Unable to find cache entry for %s, %s", imagename, ce->filename);
260         return;
261     }
262     last->next = ce->next;
263     free(ce->filename);
264     free(ce);
265 }
266 
267 /**
268  * This finds and returns the Cache_Entry of the image that matches name
269  * and checksum if has_sum is set.  If has_sum is not set, we can't
270  * do a checksum comparison.
271  */
image_find_cache_entry(char * imagename,guint32 checksum,int has_sum)272 static Cache_Entry *image_find_cache_entry(char *imagename, guint32 checksum,
273         int has_sum) {
274     int	hash_entry;
275     Cache_Entry	*entry;
276 
277     hash_entry = image_find_hash(imagename);
278     if (hash_entry == -1) {
279         return NULL;
280     }
281     entry = image_cache[hash_entry].cache_entry;
282     if (has_sum) {
283         while (entry) {
284             if (entry->checksum == checksum) {
285                 break;
286             }
287             entry = entry->next;
288         }
289     }
290     return entry;   /* This could be NULL */
291 }
292 
293 /**
294  * Add a hash entry.  Returns the entry we added, NULL on failure.
295  */
image_add_hash(char * imagename,char * filename,guint32 checksum,guint32 ispublic)296 static Cache_Entry *image_add_hash(char *imagename, char *filename,
297                                    guint32 checksum, guint32 ispublic) {
298     Cache_Entry *new_entry;
299     guint32  hash = image_hash_name(imagename, IMAGE_HASH), newhash;
300 
301     newhash = hash;
302     while (image_cache[newhash].image_name != NULL &&
303             strcmp(image_cache[newhash].image_name, imagename)) {
304         newhash ++;
305         if (newhash == IMAGE_HASH) {
306             newhash = 0;
307         }
308         /* If the hash table is full, can't do anything */
309         if (newhash == hash) {
310             LOG(LOG_WARNING, "common::image_find_hash",
311                 "Hash table is full, increase IMAGE_CACHE size");
312             return NULL;
313         }
314     }
315     if (!image_cache[newhash].image_name) {
316         image_cache[newhash].image_name = g_strdup(imagename);
317     }
318 
319     /* We insert the new entry at the start of the list of the buckets
320      * for this entry.  In the case of the players entries, this probably
321      * improves performance, presuming ones later in the file are more likely
322      * to be used compared to those at the start of the file.
323      */
324     new_entry = g_malloc(sizeof(struct Cache_Entry));
325     new_entry->filename = g_strdup(filename);
326     new_entry->checksum = checksum;
327     new_entry->ispublic = ispublic;
328     new_entry->image_data = NULL;
329     new_entry->next = image_cache[newhash].cache_entry;
330     image_cache[newhash].cache_entry = new_entry;
331     return new_entry;
332 }
333 
334 /**
335  * Process a line from the bmaps.client file.  In theory, the format should be
336  * quite strict, as it is computer generated, but we try to be lenient/follow
337  * some conventions.  Note that this is destructive to the data passed in
338  * line.
339  */
image_process_line(char * line,guint32 ispublic)340 static void image_process_line(char *line, guint32 ispublic) {
341     char imagename[MAX_BUF], filename[MAX_BUF];
342     guint32 checksum;
343 
344     if (line[0] == '#') {
345         return;    /* Ignore comments */
346     }
347 
348     if (sscanf(line, "%s %u %s", imagename, &checksum, filename) == 3) {
349         image_add_hash(imagename, filename, checksum, ispublic);
350     } else {
351         LOG(LOG_WARNING, "common::image_process_line",
352             "Did not parse line %s properly?", line);
353     }
354 }
355 
356 /**
357  *
358  */
init_common_cache_data(void)359 void init_common_cache_data(void) {
360     FILE *fp;
361     char    bmaps[MAX_BUF], inbuf[MAX_BUF];
362     int i;
363 
364     if (!want_config[CONFIG_CACHE]) {
365         return;
366     }
367 
368     for (i = 0; i < MAXPIXMAPNUM; i++) {
369         facetoname[i] = NULL;
370     }
371 
372     /* First, make sure that image_cache is nulled out */
373     memset(image_cache, 0, IMAGE_HASH * sizeof(struct Image_Cache));
374 
375     snprintf(bmaps, sizeof(bmaps), "%s/bmaps.client", CF_DATADIR);
376     if ((fp = fopen(bmaps, "r")) != NULL) {
377         while (fgets(inbuf, MAX_BUF - 1, fp) != NULL) {
378             image_process_line(inbuf, 1);
379         }
380         fclose(fp);
381     } else {
382         snprintf(inbuf, sizeof(inbuf),
383                  "Unable to open %s.  You may wish to download and install the image file to improve performance.\n",
384                  bmaps);
385         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, inbuf);
386     }
387 
388     snprintf(bmaps, sizeof(bmaps), "%s/image-cache/bmaps.client", cache_dir);
389     if ((fp = fopen(bmaps, "r")) != NULL) {
390         while (fgets(inbuf, MAX_BUF - 1, fp) != NULL) {
391             image_process_line(inbuf, 0);
392         }
393         fclose(fp);
394     } /* User may not have a cache, so no error if not found */
395     for (i = 0; i < MAX_FACE_SETS; i++) {
396         fd_cache[i].fd = -1;
397         fd_cache[i].name[0] = '\0';
398     }
399 }
400 
401 /******************************************************************************
402  *
403  * Code related to face caching.
404  *
405  *****************************************************************************/
406 
407 char facecachedir[MAX_BUF];
408 
409 /**
410  *
411  */
requestface(int pnum,char * facename)412 void requestface(int pnum, char *facename) {
413     face_info.cache_misses++;
414     facetoname[pnum] = g_strdup(facename);
415     cs_print_string(csocket.fd, "askface %d", pnum);
416 }
417 
418 /**
419  * This is common for all the face commands (face2, face1, face).
420  * For face1 and face commands, faceset should always be zero.
421  * for face commands, has_sum and checksum will be zero.
422  * pnum is the face number, while face is the name.
423  * We actually don't care what the set it - it could be useful right now,
424  * but in the current caching scheme, we look through all the facesets for
425  * the image and if the checksum matches, we assume we have match.
426  * This approach makes sure that we don't have to store the same image multiple
427  * times simply because the set number may be different.
428  */
finish_face_cmd(int pnum,guint32 checksum,int has_sum,char * face,int faceset)429 void finish_face_cmd(int pnum, guint32 checksum, int has_sum, char *face,
430                      int faceset) {
431     int len;
432     guint32 nx, ny;
433     guint8 data[65536], *png_tmp;
434     char filename[1024];
435     guint32 newsum = 0;
436     Cache_Entry *ce = NULL;
437 
438 #if 0
439     fprintf(stderr, "finish_face_cmd, pnum=%d, checksum=%d, face=%s\n",
440             pnum, checksum, face);
441 #endif
442 
443     /* In the case of gfx, we don't care about checksum.  For public and
444      * private areas, if we care about checksum, and the image doesn't match,
445      * we go onto the next step.  If nothing found, we request it
446      * from the server.
447      */
448     snprintf(filename, sizeof(filename), "%s/gfx/%s.png", cache_dir, face);
449     if (load_image(filename, data, &len, &newsum) == -1) {
450         ce = image_find_cache_entry(face, checksum, has_sum);
451         if (!ce) {
452             /* Not in our cache, so request it from the server */
453             requestface(pnum, face);
454             return;
455         } else if (ce->image_data) {
456             /* If this has image_data, then it has already been rendered */
457             if (!associate_cache_entry(ce, pnum)) {
458                 return;
459             }
460         }
461         if (ce->ispublic)
462             snprintf(filename, sizeof(filename), "%s/%s",
463                      CF_DATADIR, ce->filename);
464         else
465             snprintf(filename, sizeof(filename), "%s/image-cache/%s",
466                     cache_dir, ce->filename);
467         if (load_image(filename, data, &len, &newsum) == -1) {
468             LOG(LOG_WARNING, "common::finish_face_cmd",
469                 "file %s listed in cache file, but unable to load", filename);
470             requestface(pnum, face);
471             return;
472         }
473     }
474 
475     /* If we got here, we found an image and the checksum is OK. */
476 
477     if (!(png_tmp = png_to_data(data, len, &nx, &ny))) {
478         /* If the data is bad, remove it if it is in the players private cache */
479         LOG(LOG_WARNING, "common::finish_face_cmd",
480             "Got error on png_to_data, image=%s", face);
481         if (ce) {
482             if (!ce->ispublic) {
483                 unlink(filename);
484             }
485             image_remove_hash(face, ce);
486         }
487 
488         requestface(pnum, face);
489     }
490 
491     /* create_and_rescale_image_from data is an external reference to a piece in
492      * the gui section of the code.
493      */
494     if (create_and_rescale_image_from_data(ce, pnum, png_tmp, nx, ny)) {
495         LOG(LOG_WARNING, "common::finish_face_cmd",
496             "Got error on create_and_rescale_image_from_data, file=%s", filename);
497         requestface(pnum, face);
498     }
499     free(png_tmp);
500 }
501 
502 
503 /**
504  * We can now connect to different servers, so we need to clear out any old
505  * images.  We try to free the data also to prevent memory leaks.
506  * Note that we don't touch our hashed entries - so that when we connect to a
507  * new server, we still have all that information.
508  */
reset_image_cache_data(void)509 void reset_image_cache_data(void) {
510     int i;
511 
512     if (want_config[CONFIG_CACHE]) {
513         for (i = 1; i < MAXPIXMAPNUM; i++) {
514             free(facetoname[i]);
515             facetoname[i] = NULL;
516         }
517     }
518 }
519 
520 /**
521  * We only get here if the server believes we are caching images.  We rely on
522  * the fact that the server will only send a face command for a particular
523  * number once - at current time, we have no way of knowing if we have already
524  * received a face for a particular number.
525  */
Face2Cmd(guint8 * data,int len)526 void Face2Cmd(guint8 *data,  int len) {
527     int pnum;
528     guint8 setnum;
529     guint32  checksum;
530     char *face;
531 
532     /* A quick sanity check, since if client isn't caching, all the data
533      * structures may not be initialized.
534      */
535     if (!use_config[CONFIG_CACHE]) {
536         LOG(LOG_WARNING, "common::Face2Cmd",
537             "Received a 'face' command when we are not caching");
538         return;
539     }
540     pnum = GetShort_String(data);
541     setnum = data[2];
542     checksum = GetInt_String(data + 3);
543     face = (char *)data + 7;
544     data[len] = '\0';
545 
546     finish_face_cmd(pnum, checksum, 1, face, setnum);
547 }
548 
549 /**
550  *
551  */
Image2Cmd(guint8 * data,int len)552 void Image2Cmd(guint8 *data,  int len) {
553     int pnum, plen;
554     guint8 setnum;
555 
556     pnum = GetInt_String(data);
557     setnum = data[4];
558     plen = GetInt_String(data + 5);
559     if (len < 9 || (len - 9) != plen) {
560         LOG(LOG_WARNING, "common::Image2Cmd", "Lengths don't compare (%d,%d)",
561             (len - 9), plen);
562         return;
563     }
564     display_newpng(pnum, data + 9, plen, setnum);
565 }
566 
567 /**
568  * Helper for display_newpng, implements the caching of the image to disk.
569  */
cache_newpng(int face,guint8 * buf,int buflen,int setnum,Cache_Entry ** ce)570 static void cache_newpng(int face, guint8 *buf, int buflen, int setnum,
571                          Cache_Entry **ce) {
572     char filename[MAX_BUF], basename[MAX_BUF];
573     FILE *tmpfile;
574     guint32 i, csum;
575 
576     if (facetoname[face] == NULL) {
577         LOG(LOG_WARNING, "common::display_newpng",
578             "Caching images, but name for %ld not set", face);
579         /* Return to avoid null dereference. */
580         return;
581     }
582     /* Make necessary leading directories */
583     snprintf(filename, sizeof(filename), "%s/image-cache", cache_dir);
584     if (g_access(filename, R_OK | W_OK | X_OK) == -1) {
585         g_mkdir(filename, 0755);
586     }
587 
588     snprintf(filename, sizeof(filename), "%s/image-cache/%c%c",
589         cache_dir, facetoname[face][0], facetoname[face][1]);
590     if (access(filename, R_OK | W_OK | X_OK) == -1) {
591         g_mkdir(filename, 0755);
592     }
593 
594     /* If setnum is valid, and we have faceset information for it,
595      * put that prefix in.  This will make it easier later on to
596      * allow the client to switch image sets on the fly, as it can
597      * determine what set the image belongs to.
598      * We also append the number to it - there could be several versions
599      * of 'face.base.111.x' if different servers have different image
600      * values.
601      */
602     if (setnum >= 0 && setnum < MAX_FACE_SETS &&
603             face_info.facesets[setnum].prefix) {
604         snprintf(basename, sizeof(basename), "%s.%s", facetoname[face],
605                  face_info.facesets[setnum].prefix);
606     } else {
607         strcpy(basename, facetoname[face]);
608     }
609 
610     /* Decrease it by one since it will immediately get increased
611      * in the loop below.
612      */
613     setnum--;
614     do {
615         setnum++;
616         snprintf(filename, sizeof(filename), "%s/image-cache/%c%c/%s.%d",
617                 cache_dir, facetoname[face][0], facetoname[face][1], basename, setnum);
618     } while (g_access(filename, F_OK) == -0);
619 
620 #ifdef WIN32
621     if ((tmpfile = fopen(filename, "wb")) == NULL)
622 #else
623     if ((tmpfile = fopen(filename, "w")) == NULL)
624 #endif
625     {
626         LOG(LOG_WARNING, "common::display_newpng", "Can not open %s for writing",
627             filename);
628     } else {
629         /* found a file we can write to */
630 
631         fwrite(buf, buflen, 1, tmpfile);
632         fclose(tmpfile);
633         csum = 0;
634         for (i = 0; (int)i < buflen; i++) {
635             ROTATE_RIGHT(csum);
636             csum += buf[i];
637             csum &= 0xffffffff;
638         }
639         snprintf(filename, sizeof(filename), "%c%c/%s.%d", facetoname[face][0],
640                  facetoname[face][1],
641                  basename, setnum);
642         *ce = image_add_hash(facetoname[face], filename,  csum, 0);
643 
644         /* It may very well be more efficient to try to store these up
645          * and then write them as a bunch instead of constantly opening the
646          * file for appending.  OTOH, hopefully people will be using the
647          * built image archives, so only a few faces actually need to get
648          * downloaded.
649          */
650         snprintf(filename, sizeof(filename), "%s/image-cache/bmaps.client", cache_dir);
651         if ((tmpfile = fopen(filename, "a")) == NULL) {
652             LOG(LOG_WARNING, "common::display_newpng", "Can not open %s for appending",
653                 filename);
654         } else {
655             fprintf(tmpfile, "%s %u %c%c/%s.%d\n",
656                     facetoname[face], csum, facetoname[face][0],
657                     facetoname[face][1], basename, setnum);
658             fclose(tmpfile);
659         }
660     }
661 }
662 
663 
664 /**
665  * This function is called when the server has sent us the actual png data for
666  * an image.  If caching, we need to write this data to disk (this is handled
667  * in the function cache_newpng).
668  */
display_newpng(int face,guint8 * buf,int buflen,int setnum)669 void display_newpng(int face, guint8 *buf, int buflen, int setnum) {
670     guint8   *pngtmp;
671     guint32 width, height;
672     Cache_Entry *ce = NULL;
673 
674     if (use_config[CONFIG_CACHE]) {
675         cache_newpng(face, buf, buflen, setnum, &ce);
676     }
677 
678     pngtmp = png_to_data(buf, buflen, &width, &height);
679     if (!pngtmp) {
680         LOG(LOG_ERROR, "display_newpng", "error in PNG data; discarding");
681         return;
682     }
683 
684     if (create_and_rescale_image_from_data(ce, face, pngtmp, width, height)) {
685         LOG(LOG_WARNING, "common::display_newpng",
686             "create_and_rescale_image_from_data failed for face %ld", face);
687     }
688 
689     if (use_config[CONFIG_CACHE]) {
690         free(facetoname[face]);
691         facetoname[face] = NULL;
692     }
693     free(pngtmp);
694 }
695 
696 /**
697  * Takes the data from a replyinfo image_info and breaks it down.  The info
698  * contained is the checkums, number of images, and faceset information.  It
699  * stores this data into the face_info structure.
700  * Since we know data is null terminated, we can use the strchr operations
701  * with safety.
702  * In each block, we find the newline - if we find one, we presume the data is
703  * good, and update the face_info accordingly.  if we don't find a newline, we
704  * return.
705  */
get_image_info(guint8 * data,int len)706 void get_image_info(guint8 *data, int len) {
707     char *cp, *lp, *cps[7], buf[MAX_BUF];
708     int onset = 0, badline = 0, i;
709 
710     replyinfo_status |= RI_IMAGE_INFO;
711 
712     lp = (char *)data;
713     cp = strchr(lp, '\n');
714     if (!cp || (cp - lp) > len) {
715         return;
716     }
717     face_info.num_images = atoi(lp);
718 
719     lp = cp + 1;
720     cp = strchr(lp, '\n');
721     if (!cp || (cp - lp) > len) {
722         return;
723     }
724     face_info.bmaps_checksum = strtoul(lp, NULL,
725                                        10);	/* need unsigned, so no atoi */
726 
727     lp = cp + 1;
728     cp = strchr(lp, '\n');
729     while (cp && (cp - lp) <= len) {
730         *cp++ = '\0';
731 
732         /* The code below is pretty much the same as the code from the server
733          * which loads the original faceset file.
734          */
735         if (!(cps[0] = strtok(lp, ":"))) {
736             badline = 1;
737         }
738         for (i = 1; i < 7; i++) {
739             if (!(cps[i] = strtok(NULL, ":"))) {
740                 badline = 1;
741             }
742         }
743         if (badline) {
744             LOG(LOG_WARNING, "common::get_image_info", "bad data, ignoring line:/%s/", lp);
745         } else {
746             onset = atoi(cps[0]);
747             if (onset >= MAX_FACE_SETS) {
748                 LOG(LOG_WARNING, "common::get_image_info", "setnum is too high: %d > %d",
749                     onset, MAX_FACE_SETS);
750             }
751             face_info.facesets[onset].prefix = g_strdup(cps[1]);
752             face_info.facesets[onset].fullname = g_strdup(cps[2]);
753             face_info.facesets[onset].fallback = atoi(cps[3]);
754             face_info.facesets[onset].size = g_strdup(cps[4]);
755             face_info.facesets[onset].extension = g_strdup(cps[5]);
756             face_info.facesets[onset].comment = g_strdup(cps[6]);
757         }
758         lp = cp;
759         cp = strchr(lp, '\n');
760     }
761     face_info.have_faceset_info = 1;
762     /* if the user has requested a specific face set and that set
763      * is not numeric, try to find a matching set and send the
764      * relevent setup command.
765      */
766     if (face_info.want_faceset && atoi(face_info.want_faceset) == 0) {
767         for (onset = 0; onset < MAX_FACE_SETS; onset++) {
768             if (face_info.facesets[onset].prefix &&
769                     !g_ascii_strcasecmp(face_info.facesets[onset].prefix, face_info.want_faceset)) {
770                 break;
771             }
772             if (face_info.facesets[onset].fullname &&
773                     !g_ascii_strcasecmp(face_info.facesets[onset].fullname, face_info.want_faceset)) {
774                 break;
775             }
776         }
777         if (onset < MAX_FACE_SETS) { /* We found a match */
778             face_info.faceset = onset;
779             cs_print_string(csocket.fd, "setup faceset %d", onset);
780         } else {
781             snprintf(buf, sizeof(buf), "Unable to find match for faceset %s on the server",
782                      face_info.want_faceset);
783             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_CONFIG, buf);
784         }
785     }
786 
787 }
788 
789 /**
790  * This gets a block of checksums from the server.  This lets it prebuild the
791  * images or what not.  It would probably be nice to add a gui callback
792  * someplace that gives a little status display (18% done or whatever) - that
793  * probably needs to be done further up.
794  *
795  * The start and stop values are not meaningful - they are here because the
796  * semantics of the requestinfo/replyinfo is that replyinfo includes the same
797  * request data as the requestinfo (thus, if the request failed for some
798  * reason, the client would know which one failed and then try again).
799  * Currently, we don't have any logic in the function below to deal with
800  * failures.
801  */
get_image_sums(char * data,int len)802 void get_image_sums(char *data, int len) {
803     int stop, imagenum, slen, faceset;
804     guint32  checksum;
805     char *cp, *lp;
806 
807     cp = strchr((char *)data, ' ');
808     if (!cp || (cp - data) > len) {
809         return;
810     }
811 
812     while (isspace(*cp)) {
813         cp++;
814     }
815     lp = cp;
816     cp = strchr(lp, ' ');
817     if (!cp || (cp - data) > len) {
818         return;
819     }
820     stop = atoi(lp);
821 
822     replyinfo_last_face = stop;
823 
824     /* Can't use isspace here, because it matches with tab, ascii code
825      * 9 - this results in advancing too many spaces because
826      * starting at image 2304, the MSB of the image number will be
827      * 9.  Using a check against space will work until we get up to
828      * 8192 images.
829      */
830     while (*cp == ' ') {
831         cp++;
832     }
833     while ((cp - data) < len) {
834         imagenum = GetShort_String((guint8 *)cp);
835         cp += 2;
836         checksum = GetInt_String((guint8 *)cp);
837         cp += 4;
838         faceset = *cp;
839         cp++;
840         slen = *cp;
841         cp++;
842         /* Note that as is, this can break horribly if the client is missing a large number
843          * of images - that is because it will request a whole bunch which will overflow
844          * the servers output buffer, causing it to close the connection.
845          * What probably should be done is for the client to just request this checksum
846          * information in small batches so that even if the client has no local
847          * images, requesting the entire batch won't overflow the sockets buffer - this
848          * probably amounts to about 100 images at a time
849          */
850         finish_face_cmd(imagenum, checksum, 1, (char *)cp, faceset);
851         if (imagenum > stop) {
852             LOG(LOG_WARNING, "common::get_image_sums",
853                 "Received an image beyond our range? %d > %d", imagenum, stop);
854         }
855         cp += slen;
856     }
857 }
858 
859