1 /*
2  * Copyright (C) 1999  Rob Crittenden (rcrit@greyoak.com)
3  * Copyright (C) 1999,2000  Ross Combs (rocombs@cs.nmsu.edu)
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 #define BNETTIME_INTERNAL_ACCESS
20 #include "common/setup_before.h"
21 #include <stdio.h>
22 #ifdef HAVE_STDDEF_H
23 # include <stddef.h>
24 #else
25 # ifndef NULL
26 #  define NULL ((void *)0)
27 # endif
28 #endif
29 #ifdef HAVE_STRING_H
30 # include <string.h>
31 #else
32 # ifdef HAVE_STRINGS_H
33 #  include <strings.h>
34 # endif
35 #endif
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <errno.h>
40 #include "compat/strerror.h"
41 #ifdef TIME_WITH_SYS_TIME
42 # include <sys/time.h>
43 # include <time.h>
44 #else
45 # ifdef HAVE_SYS_TIME_H
46 #  include <sys/time.h>
47 # else
48 #  include <time.h>
49 # endif
50 #endif
51 #include "compat/gettimeofday.h"
52 #ifdef HAVE_SYS_TYPES_H
53 # include <sys/types.h>
54 #endif
55 #include "common/eventlog.h"
56 #include "common/bn_type.h"
57 #include "common/bnettime.h"
58 #include "common/setup_after.h"
59 
60 
61 /*
62  * By comparing the hex numbers from packet dumps with the times displayed
63  * on the login screen, bnettime seems to be in units of 10E-7 seconds. It
64  * is stored in a 64bit value, and represented as two distinct numbers when
65  * it is used as a string in the protocol.
66  *
67  * examples (client in GMT timezone):
68  * 11/04/99 05:16    0x01                  "29304451 3046165090"
69  * 01/03/99 11:19    0x01                  "29243146 3825346784"
70  * 08/02/99 21:28    0x01                  "29285677 4283311890"
71  * 12/12/99 01:55    0x01                  "29312068 151587081"
72  *
73  * The top number seems to be in units of about 7 minutes because the bottom
74  * number rolls over every 2^32 10E-7 seconds, which is:
75  * (2^32)*.0000001
76  * 429.4967296 // seconds
77  * 429/60
78  * 7.15827882666666666666 // minutes
79  *
80  * The epoch was determined using a binary search looking for Jan 1, 1970
81  * (GMT) to be displayed after setting the last game time to a pair of
82  * numbers. It was determined through all 64 bits by looking for the exact
83  * number where adding one pushes it over to 00:01. It was determined to be:
84  * "27111902 3577643008"
85  *
86  * It appears that this is the same format as is used in MS-DOS to store file
87  * times. The format is a 64 bit number telling how many 100 nanosecond
88  * intervals since Jan 1, 1601.
89  *
90  * If I was clever I should be able to do this without floating point math.
91  * I don't feel like being clever though :) (Refer to the function
92  * DOSFS_UnixTimeToFileTime() in dosfs.c in the WINE source code for an
93  * example that I wish I had seen before writing this).
94  *
95  * Update... from looking at the WINE sources VAP has determined that these
96  * are actually in MS-Windows FileTime format.
97  *
98  * "In the WINE listing, you can see that
99  *  FileTime = (UnixTime * 10 000 000) + 116 444 736 000 000 000 + remainder
100  *  These dates are represented from 1 Jan 1601."
101  */
102 
103 #define SEC_PER_USEC .0000001
104 
105 #define UNIX_EPOCH 11644473600. /* seconds the Unix epoch is from the bnettime epoch */
106 
107 #define SEC_PER_UPPER 429.4967296 /* (2^32)*10E-7 */
108 #define SEC_PER_LOWER .0000001
109 
110 #define UPPER_PER_SEC .00232830643653869628  /* 10E7/(2^32) */
111 #define LOWER_PER_SEC 10000000.
112 
113 
secs_to_bnettime(double secs)114 extern t_bnettime secs_to_bnettime(double secs)
115 {
116     t_bnettime temp;
117 
118     temp.u = (unsigned int)(secs*UPPER_PER_SEC);
119     temp.l = (unsigned int)(secs*LOWER_PER_SEC);
120 
121     return temp;
122 }
123 
124 
bnettime_to_secs(t_bnettime bntime)125 extern double bnettime_to_secs(t_bnettime bntime)
126 {
127     return ((double)bntime.u)*SEC_PER_UPPER+
128            ((double)bntime.l)*SEC_PER_LOWER;
129 }
130 
131 
time_to_bnettime(time_t stdtime,unsigned int usec)132 extern t_bnettime time_to_bnettime(time_t stdtime, unsigned int usec)
133 {
134     return secs_to_bnettime((double)stdtime+(double)usec*SEC_PER_USEC+UNIX_EPOCH);
135 }
136 
137 
bnettime_to_time(t_bnettime bntime)138 extern time_t bnettime_to_time(t_bnettime bntime)
139 {
140     return (time_t)(bnettime_to_secs(bntime)-UNIX_EPOCH);
141 }
142 
143 
144 /* return current time as bnettime */
bnettime(void)145 extern t_bnettime bnettime(void)
146 {
147     struct timeval tv;
148 
149     if (gettimeofday(&tv,NULL)<0)
150     {
151         eventlog(eventlog_level_error,__FUNCTION__,"could not get time (gettimeofday: %s)",pstrerror(errno));
152         return time_to_bnettime(time(NULL),0);
153     }
154     return time_to_bnettime((time_t)tv.tv_sec,tv.tv_usec);
155 }
156 
157 
158 /* FIXME: the string functions here should probably go in account_wrap */
bnettime_get_str(t_bnettime bntime)159 extern char const * bnettime_get_str(t_bnettime bntime)
160 {
161     static char temp[1024];
162 
163     sprintf(temp,"%u %u",bntime.u,bntime.l);
164 
165     return temp;
166 }
167 
168 
bnettime_set_str(t_bnettime * bntime,char const * timestr)169 extern int bnettime_set_str(t_bnettime * bntime, char const * timestr)
170 {
171     if (!bntime)
172     {
173 	eventlog(eventlog_level_error,__FUNCTION__,"got NULL bntime");
174 	return -1;
175     }
176     if (!timestr)
177     {
178 	eventlog(eventlog_level_error,__FUNCTION__,"got NULL timestr");
179 	return -1;
180     }
181 
182     if (sscanf(timestr,"%u %u",&bntime->u,&bntime->l)!=2)
183 	return -1;
184 
185     return 0;
186 }
187 
188 
bnettime_to_bn_long(t_bnettime in,bn_long * out)189 extern void bnettime_to_bn_long(t_bnettime in, bn_long * out)
190 {
191     if (!out)
192     {
193 	eventlog(eventlog_level_error,__FUNCTION__,"got NULL out");
194 	return;
195     }
196 
197     bn_long_set_a_b(out,in.u,in.l);
198 }
199 
200 
bn_long_to_bnettime(bn_long in,t_bnettime * out)201 extern void bn_long_to_bnettime(bn_long in, t_bnettime * out)
202 {
203     if (!out)
204     {
205 	eventlog(eventlog_level_error,__FUNCTION__,"got NULL out");
206 	return;
207     }
208 
209     out->u = bn_long_get_a(in);
210     out->l = bn_long_get_b(in);
211 }
212 
213 
local_tzbias(void)214 extern int local_tzbias(void) /* in minutes */
215 {
216 #ifdef HAVE_MKTIME
217     time_t      now;
218     time_t      test;
219     time_t      testloc;
220     struct tm * temp;
221 
222     now = time(NULL);
223 
224     if (!(temp = gmtime(&now)))
225 	return 0;
226     if ((test = mktime(temp))==(time_t)(-1))
227 	return 0;
228 
229     if (!(temp = localtime(&now)))
230 	return 0;
231     if ((testloc = mktime(temp))==(time_t)(-1))
232 	return 0;
233 
234     if (testloc>test) /* time_t is probably unsigned... */
235 	return -(int)(testloc-test)/60;
236     return (int)(test-testloc)/60;
237 #else
238     return 0; /* can't determine current offset */
239 #endif
240 }
241 
242 
bnettime_add_tzbias(t_bnettime bntime,int tzbias)243 extern t_bnettime bnettime_add_tzbias(t_bnettime bntime, int tzbias)
244 {
245     return secs_to_bnettime(bnettime_to_secs(bntime)-(double)tzbias*60.0);
246 }
247