Coverage Report

Created: 2025-07-01 06:46

/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
static void log_print(wLog* log, DWORD level, const char* file, const char* fkt, size_t line, ...)
77
{
78
  if (!WLog_IsLevelActive(log, level))
79
    return;
80
81
  va_list ap = { 0 };
82
  va_start(ap, line);
83
  WLog_PrintMessageVA(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, ap);
84
  va_end(ap);
85
}
86
87
static void log_timezone_(const TIME_ZONE_INFORMATION* tzif, DWORD result, const char* file,
88
                          const char* fkt, size_t line)
89
{
90
  WINPR_ASSERT(tzif);
91
92
  char buffer[64] = { 0 };
93
  DWORD level = WLOG_TRACE;
94
  wLog* log = WLog_Get(TIMEZONE_TAG);
95
  log_print(log, level, file, fkt, line, "TIME_ZONE_INFORMATION {");
96
  log_print(log, level, file, fkt, line, "  Bias=%" PRId32, tzif->Bias);
97
  (void)ConvertWCharNToUtf8(tzif->StandardName, ARRAYSIZE(tzif->StandardName), buffer,
98
                            ARRAYSIZE(buffer));
99
  log_print(log, level, file, fkt, line, "  StandardName=%s", buffer);
100
  log_print(log, level, file, fkt, line, "  StandardDate=%s",
101
            systemtime2str(&tzif->StandardDate, buffer, sizeof(buffer)));
102
  log_print(log, level, file, fkt, line, "  StandardBias=%" PRId32, tzif->StandardBias);
103
104
  (void)ConvertWCharNToUtf8(tzif->DaylightName, ARRAYSIZE(tzif->DaylightName), buffer,
105
                            ARRAYSIZE(buffer));
106
  log_print(log, level, file, fkt, line, "  DaylightName=%s", buffer);
107
  log_print(log, level, file, fkt, line, "  DaylightDate=%s",
108
            systemtime2str(&tzif->DaylightDate, buffer, sizeof(buffer)));
109
  log_print(log, level, file, fkt, line, "  DaylightBias=%" PRId32, tzif->DaylightBias);
110
111
  switch (result)
112
  {
113
    case TIME_ZONE_ID_DAYLIGHT:
114
      log_print(log, level, file, fkt, line, "  DaylightDate in use");
115
      break;
116
    case TIME_ZONE_ID_STANDARD:
117
      log_print(log, level, file, fkt, line, "  StandardDate in use");
118
      break;
119
    default:
120
      log_print(log, level, file, fkt, line, "  UnknownDate in use");
121
      break;
122
  }
123
  log_print(log, level, file, fkt, line, "}");
124
}
125
#endif
126
127
static BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time);
128
static BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time);
129
130
/**
131
 * Read SYSTEM_TIME structure (TS_SYSTEMTIME).
132
 * msdn{cc240478}
133
 * @param s stream
134
 * @param system_time system time structure
135
 */
136
137
BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time)
138
0
{
139
0
  WINPR_ASSERT(system_time);
140
141
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16ull))
142
0
    return FALSE;
143
144
0
  Stream_Read_UINT16(s, system_time->wYear);         /* wYear, must be set to 0 */
145
0
  Stream_Read_UINT16(s, system_time->wMonth);        /* wMonth */
146
0
  Stream_Read_UINT16(s, system_time->wDayOfWeek);    /* wDayOfWeek */
147
0
  Stream_Read_UINT16(s, system_time->wDay);          /* wDay */
148
0
  Stream_Read_UINT16(s, system_time->wHour);         /* wHour */
149
0
  Stream_Read_UINT16(s, system_time->wMinute);       /* wMinute */
150
0
  Stream_Read_UINT16(s, system_time->wSecond);       /* wSecond */
151
0
  Stream_Read_UINT16(s, system_time->wMilliseconds); /* wMilliseconds */
152
0
  return TRUE;
153
0
}
154
155
/**
156
 * Write SYSTEM_TIME structure (TS_SYSTEMTIME).
157
 * msdn{cc240478}
158
 * @param s stream
159
 * @param system_time system time structure
160
 */
161
162
BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time)
163
0
{
164
0
  WINPR_ASSERT(system_time);
165
0
  if (!Stream_EnsureRemainingCapacity(s, 16ull))
166
0
    return FALSE;
167
168
0
  Stream_Write_UINT16(s, system_time->wYear);         /* wYear, must be set to 0 */
169
0
  Stream_Write_UINT16(s, system_time->wMonth);        /* wMonth */
170
0
  Stream_Write_UINT16(s, system_time->wDayOfWeek);    /* wDayOfWeek */
171
0
  Stream_Write_UINT16(s, system_time->wDay);          /* wDay */
172
0
  Stream_Write_UINT16(s, system_time->wHour);         /* wHour */
173
0
  Stream_Write_UINT16(s, system_time->wMinute);       /* wMinute */
174
0
  Stream_Write_UINT16(s, system_time->wSecond);       /* wSecond */
175
0
  Stream_Write_UINT16(s, system_time->wMilliseconds); /* wMilliseconds */
176
0
  return TRUE;
177
0
}
178
179
/**
180
 * Read client time zone information (TS_TIME_ZONE_INFORMATION).
181
 * msdn{cc240477}
182
 * @param s stream
183
 * @param settings settings
184
 *
185
 * @return \b TRUE for success, \b FALSE otherwise
186
 */
187
188
BOOL rdp_read_client_time_zone(wStream* s, rdpSettings* settings)
189
0
{
190
0
  LPTIME_ZONE_INFORMATION tz = { 0 };
191
192
0
  if (!s || !settings)
193
0
    return FALSE;
194
195
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 172))
196
0
    return FALSE;
197
198
0
  tz = settings->ClientTimeZone;
199
200
0
  if (!tz)
201
0
    return FALSE;
202
203
0
  Stream_Read_INT32(s, tz->Bias); /* Bias */
204
  /* standardName (64 bytes) */
205
0
  Stream_Read(s, tz->StandardName, sizeof(tz->StandardName));
206
0
  if (!rdp_read_system_time(s, &tz->StandardDate)) /* StandardDate */
207
0
    return FALSE;
208
0
  Stream_Read_INT32(s, tz->StandardBias); /* StandardBias */
209
  /* daylightName (64 bytes) */
210
0
  Stream_Read(s, tz->DaylightName, sizeof(tz->DaylightName));
211
0
  if (!rdp_read_system_time(s, &tz->DaylightDate)) /* DaylightDate */
212
0
    return FALSE;
213
0
  Stream_Read_INT32(s, tz->DaylightBias); /* DaylightBias */
214
0
  log_timezone(tz, 0);
215
0
  return TRUE;
216
0
}
217
218
/**
219
 * Write client time zone information (TS_TIME_ZONE_INFORMATION).
220
 * msdn{cc240477}
221
 * @param s stream
222
 * @param settings settings
223
 *
224
 * @return \b TRUE for success, \b FALSE otherwise
225
 */
226
227
BOOL rdp_write_client_time_zone(wStream* s, rdpSettings* settings)
228
0
{
229
0
  WINPR_ASSERT(settings);
230
0
  const LPTIME_ZONE_INFORMATION tz = settings->ClientTimeZone;
231
232
0
  if (!tz)
233
0
    return FALSE;
234
235
0
  log_timezone(tz, 0);
236
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->StandardName)))
237
0
    return FALSE;
238
239
  /* Bias defined in windows headers as LONG
240
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
241
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
242
   * world.
243
   */
244
0
  Stream_Write_INT32(s, tz->Bias);
245
  /* standardName (64 bytes) */
246
0
  Stream_Write(s, tz->StandardName, sizeof(tz->StandardName));
247
  /* StandardDate */
248
0
  if (!rdp_write_system_time(s, &tz->StandardDate))
249
0
    return FALSE;
250
251
  /* Note that StandardBias is ignored if no valid standardDate is provided. */
252
  /* StandardBias */
253
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->DaylightName)))
254
0
    return FALSE;
255
256
  /* StandardBias defined in windows headers as LONG
257
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
258
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
259
   * world.
260
   */
261
0
  Stream_Write_INT32(s, tz->StandardBias);
262
263
  /* daylightName (64 bytes) */
264
0
  Stream_Write(s, tz->DaylightName, sizeof(tz->DaylightName));
265
  /* DaylightDate */
266
0
  if (!rdp_write_system_time(s, &tz->DaylightDate))
267
0
    return FALSE;
268
  /* Note that DaylightBias is ignored if no valid daylightDate is provided. */
269
  /* DaylightBias */
270
0
  if (!Stream_EnsureRemainingCapacity(s, 4ull))
271
0
    return FALSE;
272
273
  /* DaylightBias defined in windows headers as LONG
274
   * but [MS-RDPBCGR] 2.2.1.11.1.1.1.1 Time Zone Information (TS_TIME_ZONE_INFORMATION) defines it
275
   * as unsigned.... assume the spec is buggy as an unsigned value only works on half of the
276
   * world.
277
   */
278
0
  Stream_Write_INT32(s, tz->DaylightBias);
279
280
0
  return TRUE;
281
0
}