Coverage Report

Created: 2025-08-26 06:37

/src/FreeRDP/libfreerdp/core/timezone.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Time Zone Redirection
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/assert.h>
24
#include <winpr/timezone.h>
25
26
#include "settings.h"
27
#include "timezone.h"
28
29
#include <freerdp/log.h>
30
#define TAG FREERDP_TAG("core.timezone")
31
32
#if !defined(WITH_DEBUG_TIMEZONE)
33
#define log_timezone(tzif, result)
34
#else
35
#define log_timezone(tzif, result) log_timezone_((tzif), (result), __FILE__, __func__, __LINE__)
36
static const char* weekday2str(WORD wDayOfWeek)
37
{
38
  switch (wDayOfWeek)
39
  {
40
    case 0:
41
      return "SUNDAY";
42
    case 1:
43
      return "MONDAY";
44
    case 2:
45
      return "TUESDAY";
46
    case 3:
47
      return "WEDNESDAY";
48
    case 4:
49
      return "THURSDAY";
50
    case 5:
51
      return "FRIDAY";
52
    case 6:
53
      return "SATURDAY";
54
    default:
55
      return "DAY-OF-MAGIC";
56
  }
57
}
58
59
static char* systemtime2str(const SYSTEMTIME* t, char* buffer, size_t len)
60
{
61
  const SYSTEMTIME empty = { 0 };
62
63
  if (memcmp(t, &empty, sizeof(SYSTEMTIME)) == 0)
64
    (void)_snprintf(buffer, len, "{ not set }");
65
  else
66
  {
67
    (void)_snprintf(buffer, len,
68
                    "{ %" PRIu16 "-%" PRIu16 "-%" PRIu16 " [%s] %" PRIu16 ":%" PRIu16
69
                    ":%" PRIu16 ".%" PRIu16 "}",
70
                    t->wYear, t->wMonth, t->wDay, weekday2str(t->wDayOfWeek), t->wHour,
71
                    t->wMinute, t->wSecond, t->wMilliseconds);
72
  }
73
  return buffer;
74
}
75
76
WINPR_ATTR_FORMAT_ARG(6, 7)
77
static void log_print(wLog* log, DWORD level, const char* file, const char* fkt, size_t line,
78
                      WINPR_FORMAT_ARG const char* fmt, ...)
79
{
80
  if (!WLog_IsLevelActive(log, level))
81
    return;
82
83
  va_list ap = { 0 };
84
  va_start(ap, fmt);
85
  WLog_PrintTextMessageVA(log, level, line, file, fkt, fmt, ap);
86
  va_end(ap);
87
}
88
89
static void log_timezone_(const TIME_ZONE_INFORMATION* tzif, DWORD result, const char* file,
90
                          const char* fkt, size_t line)
91
{
92
  WINPR_ASSERT(tzif);
93
94
  char buffer[64] = { 0 };
95
  DWORD level = WLOG_TRACE;
96
  wLog* log = WLog_Get(TIMEZONE_TAG);
97
  log_print(log, level, file, fkt, line, "TIME_ZONE_INFORMATION {");
98
  log_print(log, level, file, fkt, line, "  Bias=%" PRId32, tzif->Bias);
99
  (void)ConvertWCharNToUtf8(tzif->StandardName, ARRAYSIZE(tzif->StandardName), buffer,
100
                            ARRAYSIZE(buffer));
101
  log_print(log, level, file, fkt, line, "  StandardName=%s", buffer);
102
  log_print(log, level, file, fkt, line, "  StandardDate=%s",
103
            systemtime2str(&tzif->StandardDate, buffer, sizeof(buffer)));
104
  log_print(log, level, file, fkt, line, "  StandardBias=%" PRId32, tzif->StandardBias);
105
106
  (void)ConvertWCharNToUtf8(tzif->DaylightName, ARRAYSIZE(tzif->DaylightName), buffer,
107
                            ARRAYSIZE(buffer));
108
  log_print(log, level, file, fkt, line, "  DaylightName=%s", buffer);
109
  log_print(log, level, file, fkt, line, "  DaylightDate=%s",
110
            systemtime2str(&tzif->DaylightDate, buffer, sizeof(buffer)));
111
  log_print(log, level, file, fkt, line, "  DaylightBias=%" PRId32, tzif->DaylightBias);
112
113
  switch (result)
114
  {
115
    case TIME_ZONE_ID_DAYLIGHT:
116
      log_print(log, level, file, fkt, line, "  DaylightDate in use");
117
      break;
118
    case TIME_ZONE_ID_STANDARD:
119
      log_print(log, level, file, fkt, line, "  StandardDate in use");
120
      break;
121
    default:
122
      log_print(log, level, file, fkt, line, "  UnknownDate in use");
123
      break;
124
  }
125
  log_print(log, level, file, fkt, line, "}");
126
}
127
#endif
128
129
static BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time);
130
static BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time);
131
132
/**
133
 * Read SYSTEM_TIME structure (TS_SYSTEMTIME).
134
 * msdn{cc240478}
135
 * @param s stream
136
 * @param system_time system time structure
137
 */
138
139
BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time)
140
1.46k
{
141
1.46k
  WINPR_ASSERT(system_time);
142
143
1.46k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16ull))
144
0
    return FALSE;
145
146
1.46k
  Stream_Read_UINT16(s, system_time->wYear);         /* wYear, must be set to 0 */
147
1.46k
  Stream_Read_UINT16(s, system_time->wMonth);        /* wMonth */
148
1.46k
  Stream_Read_UINT16(s, system_time->wDayOfWeek);    /* wDayOfWeek */
149
1.46k
  Stream_Read_UINT16(s, system_time->wDay);          /* wDay */
150
1.46k
  Stream_Read_UINT16(s, system_time->wHour);         /* wHour */
151
1.46k
  Stream_Read_UINT16(s, system_time->wMinute);       /* wMinute */
152
1.46k
  Stream_Read_UINT16(s, system_time->wSecond);       /* wSecond */
153
1.46k
  Stream_Read_UINT16(s, system_time->wMilliseconds); /* wMilliseconds */
154
1.46k
  return TRUE;
155
1.46k
}
156
157
/**
158
 * Write SYSTEM_TIME structure (TS_SYSTEMTIME).
159
 * msdn{cc240478}
160
 * @param s stream
161
 * @param system_time system time structure
162
 */
163
164
BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time)
165
0
{
166
0
  WINPR_ASSERT(system_time);
167
0
  if (!Stream_EnsureRemainingCapacity(s, 16ull))
168
0
    return FALSE;
169
170
0
  Stream_Write_UINT16(s, system_time->wYear);         /* wYear, must be set to 0 */
171
0
  Stream_Write_UINT16(s, system_time->wMonth);        /* wMonth */
172
0
  Stream_Write_UINT16(s, system_time->wDayOfWeek);    /* wDayOfWeek */
173
0
  Stream_Write_UINT16(s, system_time->wDay);          /* wDay */
174
0
  Stream_Write_UINT16(s, system_time->wHour);         /* wHour */
175
0
  Stream_Write_UINT16(s, system_time->wMinute);       /* wMinute */
176
0
  Stream_Write_UINT16(s, system_time->wSecond);       /* wSecond */
177
0
  Stream_Write_UINT16(s, system_time->wMilliseconds); /* wMilliseconds */
178
0
  return TRUE;
179
0
}
180
181
/**
182
 * Read client time zone information (TS_TIME_ZONE_INFORMATION).
183
 * msdn{cc240477}
184
 * @param s stream
185
 * @param settings settings
186
 *
187
 * @return \b TRUE for success, \b FALSE otherwise
188
 */
189
190
BOOL rdp_read_client_time_zone(wStream* s, rdpSettings* settings)
191
755
{
192
755
  LPTIME_ZONE_INFORMATION tz = { 0 };
193
194
755
  if (!s || !settings)
195
0
    return FALSE;
196
197
755
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 172))
198
24
    return FALSE;
199
200
731
  tz = settings->ClientTimeZone;
201
202
731
  if (!tz)
203
0
    return FALSE;
204
205
731
  Stream_Read_INT32(s, tz->Bias); /* Bias */
206
  /* standardName (64 bytes) */
207
731
  Stream_Read(s, tz->StandardName, sizeof(tz->StandardName));
208
731
  if (!rdp_read_system_time(s, &tz->StandardDate)) /* StandardDate */
209
0
    return FALSE;
210
731
  Stream_Read_INT32(s, tz->StandardBias); /* StandardBias */
211
  /* daylightName (64 bytes) */
212
731
  Stream_Read(s, tz->DaylightName, sizeof(tz->DaylightName));
213
731
  if (!rdp_read_system_time(s, &tz->DaylightDate)) /* DaylightDate */
214
0
    return FALSE;
215
731
  Stream_Read_INT32(s, tz->DaylightBias); /* DaylightBias */
216
731
  log_timezone(tz, 0);
217
731
  return TRUE;
218
731
}
219
220
/**
221
 * Write client time zone information (TS_TIME_ZONE_INFORMATION).
222
 * msdn{cc240477}
223
 * @param s stream
224
 * @param settings settings
225
 *
226
 * @return \b TRUE for success, \b FALSE otherwise
227
 */
228
229
BOOL rdp_write_client_time_zone(wStream* s, rdpSettings* settings)
230
0
{
231
0
  WINPR_ASSERT(settings);
232
0
  const LPTIME_ZONE_INFORMATION tz = settings->ClientTimeZone;
233
234
0
  if (!tz)
235
0
    return FALSE;
236
237
0
  log_timezone(tz, 0);
238
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->StandardName)))
239
0
    return FALSE;
240
241
  /* Bias defined in windows headers as LONG
242
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
243
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
244
   * world.
245
   */
246
0
  Stream_Write_INT32(s, tz->Bias);
247
  /* standardName (64 bytes) */
248
0
  Stream_Write(s, tz->StandardName, sizeof(tz->StandardName));
249
  /* StandardDate */
250
0
  if (!rdp_write_system_time(s, &tz->StandardDate))
251
0
    return FALSE;
252
253
  /* Note that StandardBias is ignored if no valid standardDate is provided. */
254
  /* StandardBias */
255
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->DaylightName)))
256
0
    return FALSE;
257
258
  /* StandardBias defined in windows headers as LONG
259
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
260
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
261
   * world.
262
   */
263
0
  Stream_Write_INT32(s, tz->StandardBias);
264
265
  /* daylightName (64 bytes) */
266
0
  Stream_Write(s, tz->DaylightName, sizeof(tz->DaylightName));
267
  /* DaylightDate */
268
0
  if (!rdp_write_system_time(s, &tz->DaylightDate))
269
0
    return FALSE;
270
  /* Note that DaylightBias is ignored if no valid daylightDate is provided. */
271
  /* DaylightBias */
272
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull))
273
0
    return FALSE;
274
275
  /* DaylightBias defined in windows headers as LONG
276
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
277
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
278
   * world.
279
   */
280
0
  Stream_Write_INT32(s, tz->DaylightBias);
281
282
0
  return TRUE;
283
0
}