]> jfr.im git - irc/gameservirc.git/blame_incremental - gameserv/tcpclient.cpp
Implemented the refreshperiod config file setting
[irc/gameservirc.git] / gameserv / tcpclient.cpp
... / ...
CommitLineData
1/*
2 * This file is provided for use with the unix-socket-faq. It is public
3 * domain, and may be copied freely. There is no copyright on it. The
4 * original work was by Vic Metcalfe (vic@brutus.tlug.org), and any
5 * modifications made to that work were made with the understanding that
6 * the finished work would be in the public domain.
7 *
8 * If you have found a bug, please pass it on to me at the above address
9 * acknowledging that there will be no copyright on your work.
10 *
11 * The most recent version of this file, and the unix-socket-faq can be
12 * found at http://www.interlog.com/~vic/sock-faq/.
13 */
14
15#include "sockhelp.h"
16#include "options.h"
17#include "list.h"
18#include "aClient.h"
19#include "extern.h"
20#include "flags.h"
21#include <stdio.h>
22#include <unistd.h>
23#include <string.h>
24#include <fstream>
25#include <stdlib.h>
26#include <fcntl.h>
27#include <signal.h>
28//#include <sys/types.h>
29//#include <sys/wait.h>
30//#include <errno.h>
31
32using std::ofstream;
33using std::ifstream;
34using std::cerr;
35using std::endl;
36
37char *PACKAGE = "GameServ";
38char *VERSION = "1.2.3 +devel";
39
40int sock;
41long lastrefresh;
42
43List<aClient> clients[U_TABLE_SIZE];
44
45void save_lastrefresh();
46void load_lastrefresh();
47void prettyIntro();
48void check_idles();
49
50// Make this a daemon
51int daemon(int nochdir, int noclose);
52
53// Close all file descriptors from >= fd
54void closeall(int fd);
55
56int main(int argc, char *argv[])
57{
58 char buffer[1024], buf[1024];
59 memset(buffer, 0, 1024);
60 memset(buf, 0, 1024);
61 int connected;
62 long lastidlecheck;
63 char *cmd, *source = NULL, *conf;
64 srand(time(NULL));
65 conf = new char[16];
66 strcpy(conf, "gameserv.conf");
67
68 /*
69 * This needs to be fixed to work for any number of arguments in any
70 * order
71 *
72 */
73 if (argc > 1)
74 {
75 if ( argc > 2 || stricmp(argv[1], "--help") == 0)
76 {
77 cout << "Usage: gameserv [options] [configfile]" << endl;
78 cout << "Options:" << endl;
79 cout << "--help Displays this help dialogue" << endl;
80 return 1;
81 }
82 delete []conf;
83 conf = argv[1];
84 }
85
86 prettyIntro();
87
88 if (load_config_file(conf))
89 {
90 cout << "Config file loaded ok...\n"
91 << "Turning into a daemon" << endl;
92 }
93 else
94 exit(2);
95
96 if (argc <= 1)
97 delete []conf;
98
99 // Turn into a daemon
100 if (daemon(1,0) < 0)
101 {
102 perror("Could not turn into a daemon");
103 exit(3);
104 }
105
106 init_masters();
107 load_gs_dbase();
108 loadNews(newsdata, todaysnews);
109
110 if (load_monsters() == false)
111 goto end;
112
113 shuttingdown = false;
114
115 char ignoreservers[32][256];
116 char *currentserver;
117 currentserver = strtok(ignoreserverslist, " ");
118 for (int server = 0; server < 32 && currentserver != NULL; server++)
119 {
120 strncpy(ignoreservers[server], currentserver, 255);
121 log("Placing %s on the server ignore list", currentserver);
122 currentserver = strtok(NULL, " ");
123 }
124
125 strcpy(boss.name, "Red Dragon");
126 strcpy(boss.weapon, "Breath of Unholy Fire");
127 boss.strength = 2500;
128 boss.gold = 2000000000;
129 boss.exp = 2000000000;
130 boss.maxhp = 6667;
131 boss.hp = 6667;
132 strcpy(boss.death, "You finally snuff out the deadly murderous "\
133 "dragon's dark flames. You have freed the land of its terror "\
134 "filled reign from above!");
135
136
137 // This loop will retry the connection 3 times
138 for (int retry = 0; retry < 3 && !shuttingdown; retry++)
139 {
140 connected = 1;
141 load_lastrefresh();
142
143
144 long int loadtime = time(NULL);
145 long int currentTime;
146 long int oldTime = loadtime;
147
148 lastidlecheck = loadtime;
149
150 #ifdef DEBUGMODE
151 log("Setting primary Idle Check timestamp: %ld", lastidlecheck);
152 #endif
153 bool loaded = false;
154
155 ignore_pipe();
156 sock = make_connection(remoteport, SOCK_STREAM, remoteserver);
157 if (sock == -1) {
158 fprintf(stderr,"make_connection failed.\n");
159 unload_config_file();
160 return -1;
161 }
162 log("%S socket connected.");
163
164#ifdef UNREAL
165 raw("PROTOCTL NICKv2 VHP");
166 raw("PASS :%s", remotepass);
167 raw("SERVER %s 1 :%s", servername, servername);
168 raw("NICK %S 1 %d %S %s %s %d +w%s %s :%s v%s", time(NULL), gshost,
169 servername, time(NULL), (isBOper() ? "o" : ""), gshost, PACKAGE, VERSION);
170 raw(":%S JOIN %s", c_Forest);
171 raw(":%S MODE %s +tn", c_Forest);
172#elif defined(BAHAMUT)
173 raw("PASS %s :TS", remotepass);
174 raw("SERVER %s 1 :%s", servername, servername);
175 raw("NICK %S 1 %d +w%s %s %s %s 0 :GameServ", time(NULL), (isBOper() ? "o" : ""),
176 gsident, gshost, servername);
177 raw(":%s SJOIN %d %d %s +nt :@%S", servername, time(NULL), time(NULL), c_Forest);
178#elif defined(HYBRID)
179 raw("PASS %s :TS", remotepass);
180 raw("SERVER %s 1 :%s", servername, servername);
181 raw("NICK %S 1 %d +w%s %s %s %s :GameServ", time(NULL), (isBOper() ? "o" : ""),
182 gsident, gshost, servername);
183 raw(":%s SJOIN %ld %s +nt :@%S", servername, time(NULL), c_Forest);
184#elif defined(ULTIMATE2)
185 raw("PASS %s :TS", remotepass);
186 raw("SERVER %s 1 :%s", servername, servername);
187 raw("NICK %S 1 %d %s %s %s 0 :GameServ",
188 time(NULL), gsident, gshost, servername);
189 if (isBOper())
190 raw(":%S mode %S +o");
191 raw(":%S JOIN %s", c_Forest);
192#elif defined(P10)
193 // Server numeric is: [] <-- must be unique
194 raw("PASS :%s", remotepass);
195 raw("SERVER %s 1 %d %d P10 []AAF :%s", servername, time(NULL), time(NULL), servername);
196 raw("[] N %S 1 %d %s %s %s DAqAoB %s :%S", time(NULL), gsident, gshost,
197 (isBOper() ? "+o" : ""), gsnum);
198 raw("[] B %s %d +tn %s:o", c_Forest, time(NULL) - 864000, gsnum);
199#endif
200
201#if defined(P10)
202 raw("%s T %s :%s", gsnum, c_Forest, c_ForestTopic);
203 raw("[] EB"); // End burst
204#else
205 #ifndef HYBRID
206 #if defined(ULTIMATE2)
207 raw(":%s MODE %s +o %S %ld", servername, c_Forest,
208 time(NULL));
209 #else
210 raw(":%S MODE %s +o %S", c_Forest);
211 #endif
212 #endif
213 raw(":%S TOPIC %s :%s", c_Forest, c_ForestTopic);
214#endif
215
216 sock_gets(sock,buffer,sizeof(buffer)-1); /* -1 added thanks to
217 David Duchene <dave@ltd.com> for pointing out the possible
218 buffer overflow resulting from the linefeed added below. */
219
220
221 #ifdef DEBUGMODE
222 log("Server: %s",buffer);
223 #endif
224
225 while (connected) {
226 if (sock_gets(sock,buffer,sizeof(buffer)) == -1) {
227 connected = 0;
228 }
229 strcpy(buf, buffer);
230
231 #if !defined(P10)
232 if (buffer[0] == ':')
233 {
234 source = strtok(buf, " ");
235 cmd = strtok(NULL, " ");
236 }
237 else
238 cmd = strtok(buf, " ");
239 #else
240 source = strtok(buf, " ");
241 cmd = strtok(NULL, " ");
242 #endif
243
244 #ifdef DEBUGMODE
245 log("Server: %s", buffer);
246 #endif
247
248 // Wait N seconds then we're loaded.
249 if (!loaded)
250 {
251 if (time(NULL) >= welcomedelay + loadtime)
252 {
253 loaded = true;
254 retry = 0; // Start the reconnection cycle over
255 }
256 }
257 else
258 {
259 long TIME = time(NULL);
260 if (TIME - lastidlecheck >= idlecheckperiod)
261 {
262 check_idles();
263 lastidlecheck = TIME;
264 }
265 }
266
267 // Refresh players and clear news if the time is up
268 if (loadtime - lastrefresh >= refreshperiod)
269 {
270 refreshall();
271 clearNews(todaysnews);
272 saveNews(newsdata, todaysnews);
273 save_lastrefresh();
274 }
275
276 // Save the player data every updateperiod seconds
277 currentTime = time(NULL);
278 if (currentTime - oldTime >= updateperiod)
279 {
280 oldTime = currentTime;
281 log("Saving to %s", playerdata);
282 save_gs_dbase();
283 saveNews(newsdata, todaysnews);
284 }
285
286
287 #if !defined(P10)
288 if (stricmp(cmd, "PING") == 0) {
289 char *timestamp;
290 timestamp = strtok(NULL, "");
291 raw("PONG %s", timestamp);
292 #else
293 if (stricmp(cmd, "G") == 0) {
294 char *timestamp;
295 timestamp = strtok(NULL, " ");
296 raw("[] Z [] %s 0 %s", timestamp + 1, timestamp);
297 #endif
298 #ifdef P10
299 } else if (stricmp(cmd, "EB") == 0) {
300 raw("[] EA");
301 #endif
302 } else if (stricmp(cmd, "VERSION") == 0) {
303 char *server;
304 server = strtok(NULL, " ");
305 server++;
306 raw(":%s 351 %s %s_%s. %s", servername, source+1, PACKAGE, VERSION, servername);
307 #if !defined(P10)
308 } else if (strncmp(cmd, "NICK", 4) == 0) {
309 if (buffer[0] == ':')
310 {
311 aClient *tempPtr;
312 if ((tempPtr = find((source + 1))))
313 {
314 char *nick;
315 unsigned long oldhv, newhv;
316 nick = strtok(NULL, " ");
317 oldhv = iHASH((unsigned char *) tempPtr->getNick());
318 newhv = iHASH((unsigned char *) nick);
319 tempPtr->setNick(nick);
320 clients[oldhv].remove(tempPtr);
321 clients[newhv].insertAtBack(tempPtr);
322 }
323 }
324 else
325 {
326 char *nick;
327 #else
328 } else if (stricmp(cmd, "N") == 0 && strlen(source) == 2) {
329 {
330 char *nick, *realnick;
331 realnick = strtok(NULL, " ");
332
333 for (int x = 0; x < 5; x++)
334 nick = strtok(NULL, " ");
335
336 if (nick[0] == '+')
337 {
338 #ifdef DEBUGMODE
339 log ("aClient has modes");
340 #endif
341
342 // Searching for the +r mode (extra parameter)
343 for (unsigned int count = 1; count < strlen(nick); count++)
344 {
345 if (nick[count] == 'r')
346 {
347 nick = strtok(NULL, " ");
348 break;
349 }
350 }
351 nick = strtok(NULL, " ");
352 }
353 #endif
354 aClient *newuser, *temp;
355
356 nick = strtok(NULL, " ");
357
358 #ifdef P10
359 newuser = new aClient(nick, realnick);
360 #else
361 newuser = new aClient(nick);
362 #endif
363
364
365 if (loaded)
366
367 if (isWelcome())
368 {
369 #ifdef P10
370 notice(s_GameServ, nick, welcomemsg, realnick);
371 #else
372 notice(s_GameServ, nick, welcomemsg, nick);
373 #endif
374 }
375 #ifdef P10
376 unsigned long hv = sHASH((unsigned char *) nick);
377 #else
378 unsigned long hv = iHASH((unsigned char *) nick);
379 #endif
380
381 temp = clients[hv].insertAtBack(newuser);
382
383 #if defined(HYBRID) || defined(BAHAMUT) || defined(ULTIMATE2)
384 char *nickserver;
385 strtok(NULL, " ");
386 strtok(NULL, " ");
387 nickserver = strtok(NULL, " ");
388 if (nickserver[0] == '+')
389 strtok(NULL, " ");
390 strtok(NULL, " ");
391 nickserver = strtok(NULL, " ");
392 for (int x = 0; x < 32; x++)
393 {
394 if (stricmp(ignoreservers[x], nickserver) == 0)
395 {
396 setIgnore(temp);
397 break;
398 }
399 }
400 #elif defined(UNREAL)
401 char *nickserver;
402 strtok(NULL, " ");
403 strtok(NULL, " ");
404 strtok(NULL, " ");
405 strtok(NULL, " ");
406 nickserver = strtok(NULL, " ");
407 for (int x = 0; x < 32; x++)
408 {
409 if (stricmp(ignoreservers[x], nickserver) == 0)
410 {
411 setIgnore(temp);
412 break;
413 }
414 }
415 #endif
416 delete newuser;
417 }
418 #if defined(P10)
419 } else if (stricmp(cmd, "Q") == 0) {
420// unsigned long hv = sHASH((unsigned char *) source);
421 #else
422 } else if (stricmp(cmd, "QUIT") == 0) {
423// unsigned long hv = iHASH((unsigned char *) source);
424 #endif
425 aClient *quitter;
426 char z = source[0];
427
428 if (z == ':')
429 source++;
430
431 if (!(quitter = find(source)))
432 {
433 log("Fatal Error: could not find %s in the "\
434 "clients list", source);
435 goto end;
436 }
437
438 logout(quitter);
439
440 if (z == ':')
441 source--;
442
443 /* Attempting to use the logout() function
444 if ((quitter = find(source)))
445 clients[hv].remove(quitter);
446 if ((quitter = findIRCplayer(source)))
447 {
448 if (player_fight(quitter))
449 {
450 // Stop the fight on the other client
451 aClient *otherplayer = quitter->stats->battle;
452 otherplayer->stats->battle = NULL;
453 notice(s_GameServ, otherplayer->getNick(), "%s "\
454 "has quit IRC. The fight stops here.",
455 quitter->stats->name);
456 }
457 quitter->stats->battle = NULL;
458 quitter->stats->fight = NULL;
459 quitter->stats->master = NULL;
460
461 quitter->setNick("Not Playing");
462 #ifdef P10
463 quitter->setRealNick("Not Playing");
464 #endif
465 quitter->stats->client = NULL; // Unidentify them
466 }
467 */
468
469 #if defined(P10)
470 } else if (stricmp(cmd, "P") == 0) {
471 char *rest, *dest;
472 char *longname;
473 longname = new char[strlen(s_GameServ) + strlen(servername) + 2];
474
475 sprintf(longname, "%S@%s", servername);
476
477 dest = strtok(NULL, " ");
478 rest = strtok(NULL, "");
479 if (stricmp(dest, gsnum) == 0 || stricmp(dest, longname) == 0)
480 {
481 delete [] longname;
482 gameserv(source, rest);
483 }
484 else if (stricmp(dest, c_Forest) == 0 && isListenOnCF())
485 {
486 delete [] longname;
487 forest(source, rest);
488 }
489 #else
490 } else if (stricmp(cmd, "PRIVMSG") == 0) {
491 char *rest, *dest;
492 dest = strtok(NULL, " ");
493 rest = strtok(NULL, "");
494 if (strnicmp(dest, s_GameServ, strlen(s_GameServ)) == 0)
495 gameserv(source, rest);
496 else if (stricmp(dest, c_Forest) == 0 && isListenOnCF())
497 forest(source, rest);
498 #endif
499 #if defined(P10)
500 } else if (stricmp(cmd, "J") == 0) {
501 #else
502 } else if (stricmp(cmd, "JOIN") == 0) {
503 #endif
504 char *channel;
505 aClient *joiner;
506 channel = strtok(NULL, " ");
507
508 char z = source[0];
509
510 if (z == ':')
511 source++;
512
513 joiner = find(source);
514
515 if (stricmp(channel, c_Forest) == 0 && is_playing(joiner))
516 {
517 #ifdef DEBUGMODE
518 log("Player %s (IRC: %s) joined %s",
519 joiner->stats->name,
520 #ifdef P10
521 joiner->getRealNick(),
522 #else
523 joiner->getNick(),
524 #endif
525 c_Forest);
526 #endif
527 raw(":%S MODE %s +v %s", c_Forest, (source));
528 }
529
530 if (z == ':')
531 source--;
532
533 #if defined(BAHAMUT)
534 } else if (stricmp(cmd, "SJOIN") == 0) {
535 char *channel, *nick, *tmp, *rest;
536 strtok(NULL, " "); // Ignore the TS
537#ifndef HYBRID
538 strtok(NULL, " "); // Ignore the TS
539#endif
540 channel = strtok(NULL, " ");
541 rest = strtok(NULL, "");
542 tmp = strchr(rest, ':');
543 tmp++;
544 nick = strtok(tmp, " ");
545 while (nick != NULL)
546 {
547 if (*nick == '@')
548 nick++;
549 if (*nick == '+')
550 nick++; // Assume for users set op and voice, they
551 // are never passed as +@nick
552 if (stricmp(channel, c_Forest) == 0 && is_playing(nick))
553 raw(":%S MODE %s +v %s", channel, nick);
554
555 nick = strtok(NULL, " ");
556 }
557#endif
558 } else {
559 #ifdef DEBUGMODE
560 log("Unrecognized Message: cmd = %s source = %s", cmd, source);
561 #endif
562 }
563 }
564
565 } // for loop for connection retry
566
567 end:
568
569 save_gs_dbase();
570 saveNews(newsdata, todaysnews);
571
572 delete_monsters();
573 delete_masters();
574
575 #ifdef DEBUGMODE
576 log("<CLOSED>");
577 #endif
578
579 close(sock);
580 unload_config_file();
581 return 0;
582}
583
584aClient *find(char *nick)
585{
586 return findbynick(nick);
587}
588
589aClient *find(const char *nick)
590{
591 return findbynick(nick);
592}
593
594#ifdef P10
595
596aClient *findbyrealnick(char *realnick)
597{
598 ListNode <aClient> *newPtr;
599 unsigned long hv = sHASH((unsigned char *) realnick);
600 newPtr = clients[hv].First();
601
602 aClient *client = NULL;
603
604 while (newPtr)
605 {
606 client = newPtr->getData();
607 if (stricmp(client->getRealNick(), realnick) == 0)
608 return client;
609 client = NULL;
610 newPtr = newPtr->Next();
611 }
612 return client;
613}
614
615#else
616
617aClient *findbyrealnick(char *realnick)
618{
619 return findbynick(realnick);
620}
621
622#endif
623
624aClient *findbynick(char *nick)
625{
626 ListNode <aClient> *newPtr;
627 #ifdef P10
628 unsigned long hv = sHASH((unsigned char *) nick);
629 #else
630 unsigned long hv = iHASH((unsigned char *) nick);
631 #endif
632
633 newPtr = clients[hv].First();
634
635 aClient *client = NULL;
636
637 while (newPtr)
638 {
639 client = newPtr->getData();
640 #ifdef P10
641 if (strcmp(client->getNick(), nick) == 0)
642 #else
643 if (stricmp(client->getNick(), nick) == 0)
644 #endif
645 return client;
646 client = NULL;
647 newPtr = newPtr->Next();
648 }
649 return client;
650}
651
652aClient *findIRCplayer(const char *nick)
653{
654 ListNode <aClient> *newPtr;
655 aClient *p = NULL;
656
657 p = find(nick);
658
659 if (!is_playing(p))
660 return NULL;
661
662 unsigned long hv = iHASH((unsigned char *) p->stats->name);
663
664 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
665 {
666 p = newPtr->getData();
667 #ifdef P10
668 if (strcmp(p->getNick(), nick) == 0)
669 #else
670 if (stricmp(p->getNick(), nick) == 0)
671 #endif
672 return p;
673 p = NULL;
674 }
675 return NULL;
676}
677
678aClient *findplayer(const char *name)
679{
680 ListNode <aClient> *newPtr;
681 Player *p = NULL;
682 unsigned long hv = iHASH((unsigned char *) name);
683 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
684 {
685 p = newPtr->getData()->stats;
686 if (stricmp(p->name, name) == 0)
687 return newPtr->getData();
688 p = NULL;
689 }
690 return NULL;
691}
692
693void check_idles()
694{
695 ListNode <aClient> *newPtr;
696 Player *p = NULL;
697
698 for (int x = 0; x < U_TABLE_SIZE; x++)
699 {
700 for (newPtr = players[x].First(); newPtr; newPtr = newPtr->Next())
701 {
702 p = newPtr->getData()->stats;
703 if (timedOut(p))
704 {
705 timeOutEvent(p);
706 }
707 }
708 }
709}
710
711aClient *findbynick(const char *nick)
712{
713 ListNode <aClient> *newPtr;
714 #ifdef P10
715 unsigned long hv = sHASH((unsigned char *) nick);
716 #else
717 unsigned long hv = iHASH((unsigned char *) nick);
718 #endif
719
720 newPtr = clients[hv].First();
721
722 aClient *client = NULL;
723
724 while (newPtr)
725 {
726 client = newPtr->getData();
727 #ifdef P10
728 if (strcmp(client->getNick(), nick) == 0)
729 #else
730 if (stricmp(client->getNick(), nick) == 0)
731 #endif
732 return client;
733 client = NULL;
734 newPtr = newPtr->Next();
735 }
736 return client;
737}
738
739/* daemon() - detach process from user and disappear into the background
740 * returns -1 on failure, but you can't do much except exit in that case
741 * since we may already have forked. This is based on the BSD version,
742 * so the caller is responsible for things like the umask, etc.
743 */
744
745/* believed to work on all Posix systems */
746
747int daemon(int nochdir, int noclose)
748{
749 pid_t pid;
750 switch (pid = fork())
751 {
752 case 0: break;
753 case -1: return -1;
754 default: _exit(0); /* exit the original process */
755 }
756
757 if (setsid() < 0) /* shoudn't fail */
758 return -1;
759
760 /* dyke out this switch if you want to acquire a control tty in */
761 /* the future -- not normally advisable for daemons */
762
763 switch (pid = fork())
764 {
765 case 0: break;
766 case -1: return -1;
767 default:
768 ofstream outfile;
769 outfile.open(pidfile);
770 if (outfile.fail())
771 cerr << "Unable to open " << pidfile << endl;
772 outfile << pid << endl;
773 outfile.close();
774
775 _exit(0);
776 }
777
778 if (!nochdir)
779 chdir("/");
780
781 if (!noclose)
782 {
783 closeall(0);
784 open("/dev/null",O_RDWR);
785 dup(0); dup(0);
786 }
787
788 return 0;
789}
790
791
792/* closeall() -- close all FDs >= a specified value */
793
794void closeall(int fd)
795{
796 int fdlimit = sysconf(_SC_OPEN_MAX);
797
798 while (fd < fdlimit)
799 close(fd++);
800}
801
802void prettyIntro()
803{
804cout << endl;
805cout << " GGGG AAA MM MM EEEEEEE SSSSS EEEEEEE RRRRRR VV VV " << endl;
806cout << " GG GG AAAAA MMM MMM EE SS EE RR RR VV VV " << endl;
807cout << "GG AA AA MM MM MM EEEEE SSSSS EEEEE RRRRRR VV VV " << endl;
808cout << "GG GGG AAAAAAA MM MM EE SS EE RR RR VV VV " << endl;
809cout << "G G AA AA MM MM EEEEEEE SSSSS EEEEEEE RR RR VVV" << endl;
810cout << " GGGGG V\n\n" << endl;
811cout << "Version: " << VERSION << endl;
812}
813
814void load_lastrefresh()
815{
816 ifstream infile;
817 infile.open(".gsrefresh");
818 if (infile.fail())
819 {
820 #ifdef DEBUGMODE
821 log("Error opening .gsrefresh");
822 #endif
823
824 generate:
825 #ifdef DEBUGMODE
826 log("Generating new refresh time");
827 #endif
828 lastrefresh = time(NULL);
829 refreshall();
830 save_lastrefresh();
831 return;
832 }
833 infile >> lastrefresh;
834 infile.close();
835 if (lastrefresh < 0)
836 goto generate;
837}
838
839void save_lastrefresh()
840{
841 ofstream outfile;
842
843 outfile.open(".gsrefresh");
844
845 if (outfile.fail())
846 {
847 log("Error creating new file .gsrefresh");
848 return;
849 }
850 outfile << lastrefresh << endl;
851
852 outfile.close();
853}