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