Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/clipboard/clipboard.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Clipboard Functions
4
 *
5
 * Copyright 2014 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 <winpr/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/collections.h>
24
#include <winpr/wlog.h>
25
26
#include <winpr/clipboard.h>
27
28
#include "clipboard.h"
29
30
#include "synthetic_file.h"
31
32
#include "../log.h"
33
#define TAG WINPR_TAG("clipboard")
34
35
const char* const mime_text_plain = "text/plain";
36
37
/**
38
 * Clipboard (Windows):
39
 * msdn.microsoft.com/en-us/library/windows/desktop/ms648709/
40
 *
41
 * W3C Clipboard API and events:
42
 * http://www.w3.org/TR/clipboard-apis/
43
 */
44
45
static const char* CF_STANDARD_STRINGS[] = {
46
  "CF_RAW",          /* 0 */
47
  "CF_TEXT",         /* 1 */
48
  "CF_BITMAP",       /* 2 */
49
  "CF_METAFILEPICT", /* 3 */
50
  "CF_SYLK",         /* 4 */
51
  "CF_DIF",          /* 5 */
52
  "CF_TIFF",         /* 6 */
53
  "CF_OEMTEXT",      /* 7 */
54
  "CF_DIB",          /* 8 */
55
  "CF_PALETTE",      /* 9 */
56
  "CF_PENDATA",      /* 10 */
57
  "CF_RIFF",         /* 11 */
58
  "CF_WAVE",         /* 12 */
59
  "CF_UNICODETEXT",  /* 13 */
60
  "CF_ENHMETAFILE",  /* 14 */
61
  "CF_HDROP",        /* 15 */
62
  "CF_LOCALE",       /* 16 */
63
  "CF_DIBV5"         /* 17 */
64
};
65
66
const char* ClipboardGetFormatIdString(UINT32 formatId)
67
0
{
68
0
  if (formatId < ARRAYSIZE(CF_STANDARD_STRINGS))
69
0
    return CF_STANDARD_STRINGS[formatId];
70
0
  return "CF_REGISTERED_FORMAT";
71
0
}
72
73
static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
74
                                             const char* name)
75
0
{
76
0
  wClipboardFormat* format = NULL;
77
78
0
  if (!clipboard)
79
0
    return NULL;
80
81
0
  if (formatId)
82
0
  {
83
0
    for (UINT32 index = 0; index < clipboard->numFormats; index++)
84
0
    {
85
0
      wClipboardFormat* cformat = &clipboard->formats[index];
86
0
      if (formatId == cformat->formatId)
87
0
      {
88
0
        format = cformat;
89
0
        break;
90
0
      }
91
0
    }
92
0
  }
93
0
  else if (name)
94
0
  {
95
0
    for (UINT32 index = 0; index < clipboard->numFormats; index++)
96
0
    {
97
0
      wClipboardFormat* cformat = &clipboard->formats[index];
98
0
      if (!cformat->formatName)
99
0
        continue;
100
101
0
      if (strcmp(name, cformat->formatName) == 0)
102
0
      {
103
0
        format = cformat;
104
0
        break;
105
0
      }
106
0
    }
107
0
  }
108
0
  else
109
0
  {
110
    /* special "CF_RAW" case */
111
0
    if (clipboard->numFormats > 0)
112
0
    {
113
0
      format = &clipboard->formats[0];
114
115
0
      if (format->formatId)
116
0
        return NULL;
117
118
0
      if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
119
0
        return format;
120
0
    }
121
0
  }
122
123
0
  return format;
124
0
}
125
126
static wClipboardSynthesizer* ClipboardFindSynthesizer(wClipboardFormat* format, UINT32 formatId)
127
0
{
128
0
  if (!format)
129
0
    return NULL;
130
131
0
  for (UINT32 index = 0; index < format->numSynthesizers; index++)
132
0
  {
133
0
    wClipboardSynthesizer* synthesizer = &(format->synthesizers[index]);
134
135
0
    if (formatId == synthesizer->syntheticId)
136
0
      return synthesizer;
137
0
  }
138
139
0
  return NULL;
140
0
}
141
142
void ClipboardLock(wClipboard* clipboard)
143
0
{
144
0
  if (!clipboard)
145
0
    return;
146
147
0
  EnterCriticalSection(&(clipboard->lock));
148
0
}
149
150
void ClipboardUnlock(wClipboard* clipboard)
151
0
{
152
0
  if (!clipboard)
153
0
    return;
154
155
0
  LeaveCriticalSection(&(clipboard->lock));
156
0
}
157
158
BOOL ClipboardEmpty(wClipboard* clipboard)
159
0
{
160
0
  if (!clipboard)
161
0
    return FALSE;
162
163
0
  if (clipboard->data)
164
0
  {
165
0
    free(clipboard->data);
166
0
    clipboard->data = NULL;
167
0
  }
168
169
0
  clipboard->size = 0;
170
0
  clipboard->formatId = 0;
171
0
  clipboard->sequenceNumber++;
172
0
  return TRUE;
173
0
}
174
175
UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
176
0
{
177
0
  if (!clipboard)
178
0
    return 0;
179
180
0
  return clipboard->numFormats;
181
0
}
182
183
UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
184
0
{
185
0
  UINT32* pFormatIds = NULL;
186
0
  wClipboardFormat* format = NULL;
187
188
0
  if (!clipboard)
189
0
    return 0;
190
191
0
  if (!ppFormatIds)
192
0
    return 0;
193
194
0
  pFormatIds = *ppFormatIds;
195
196
0
  if (!pFormatIds)
197
0
  {
198
0
    pFormatIds = calloc(clipboard->numFormats, sizeof(UINT32));
199
200
0
    if (!pFormatIds)
201
0
      return 0;
202
203
0
    *ppFormatIds = pFormatIds;
204
0
  }
205
206
0
  for (UINT32 index = 0; index < clipboard->numFormats; index++)
207
0
  {
208
0
    format = &(clipboard->formats[index]);
209
0
    pFormatIds[index] = format->formatId;
210
0
  }
211
212
0
  return clipboard->numFormats;
213
0
}
214
215
UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name)
216
0
{
217
0
  wClipboardFormat* format = NULL;
218
219
0
  if (!clipboard)
220
0
    return 0;
221
222
0
  format = ClipboardFindFormat(clipboard, 0, name);
223
224
0
  if (format)
225
0
    return format->formatId;
226
227
0
  if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
228
0
  {
229
0
    UINT32 numFormats = clipboard->maxFormats * 2;
230
0
    wClipboardFormat* tmpFormat = NULL;
231
0
    tmpFormat =
232
0
        (wClipboardFormat*)realloc(clipboard->formats, numFormats * sizeof(wClipboardFormat));
233
234
0
    if (!tmpFormat)
235
0
      return 0;
236
237
0
    clipboard->formats = tmpFormat;
238
0
    clipboard->maxFormats = numFormats;
239
0
  }
240
241
0
  format = &(clipboard->formats[clipboard->numFormats]);
242
0
  ZeroMemory(format, sizeof(wClipboardFormat));
243
244
0
  if (name)
245
0
  {
246
0
    format->formatName = _strdup(name);
247
248
0
    if (!format->formatName)
249
0
      return 0;
250
0
  }
251
252
0
  format->formatId = clipboard->nextFormatId++;
253
0
  clipboard->numFormats++;
254
0
  return format->formatId;
255
0
}
256
257
BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
258
                                  CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
259
0
{
260
0
  UINT32 index = 0;
261
0
  wClipboardFormat* format = NULL;
262
0
  wClipboardSynthesizer* synthesizer = NULL;
263
264
0
  if (!clipboard)
265
0
    return FALSE;
266
267
0
  format = ClipboardFindFormat(clipboard, formatId, NULL);
268
269
0
  if (!format)
270
0
    return FALSE;
271
272
0
  if (format->formatId == syntheticId)
273
0
    return FALSE;
274
275
0
  synthesizer = ClipboardFindSynthesizer(format, formatId);
276
277
0
  if (!synthesizer)
278
0
  {
279
0
    wClipboardSynthesizer* tmpSynthesizer = NULL;
280
0
    UINT32 numSynthesizers = format->numSynthesizers + 1;
281
0
    tmpSynthesizer = (wClipboardSynthesizer*)realloc(
282
0
        format->synthesizers, numSynthesizers * sizeof(wClipboardSynthesizer));
283
284
0
    if (!tmpSynthesizer)
285
0
      return FALSE;
286
287
0
    format->synthesizers = tmpSynthesizer;
288
0
    format->numSynthesizers = numSynthesizers;
289
0
    index = numSynthesizers - 1;
290
0
    synthesizer = &(format->synthesizers[index]);
291
0
  }
292
293
0
  synthesizer->syntheticId = syntheticId;
294
0
  synthesizer->pfnSynthesize = pfnSynthesize;
295
0
  return TRUE;
296
0
}
297
298
UINT32 ClipboardCountFormats(wClipboard* clipboard)
299
0
{
300
0
  UINT32 count = 0;
301
0
  wClipboardFormat* format = NULL;
302
303
0
  if (!clipboard)
304
0
    return 0;
305
306
0
  format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
307
308
0
  if (!format)
309
0
    return 0;
310
311
0
  count = 1 + format->numSynthesizers;
312
0
  return count;
313
0
}
314
315
UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
316
0
{
317
0
  UINT32 count = 0;
318
0
  UINT32* pFormatIds = NULL;
319
0
  wClipboardFormat* format = NULL;
320
0
  wClipboardSynthesizer* synthesizer = NULL;
321
322
0
  if (!clipboard)
323
0
    return 0;
324
325
0
  format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
326
327
0
  if (!format)
328
0
    return 0;
329
330
0
  count = 1 + format->numSynthesizers;
331
332
0
  if (!ppFormatIds)
333
0
    return 0;
334
335
0
  pFormatIds = *ppFormatIds;
336
337
0
  if (!pFormatIds)
338
0
  {
339
0
    pFormatIds = calloc(count, sizeof(UINT32));
340
341
0
    if (!pFormatIds)
342
0
      return 0;
343
344
0
    *ppFormatIds = pFormatIds;
345
0
  }
346
347
0
  pFormatIds[0] = format->formatId;
348
349
0
  for (UINT32 index = 1; index < count; index++)
350
0
  {
351
0
    synthesizer = &(format->synthesizers[index - 1]);
352
0
    pFormatIds[index] = synthesizer->syntheticId;
353
0
  }
354
355
0
  return count;
356
0
}
357
358
static void ClipboardUninitFormats(wClipboard* clipboard)
359
0
{
360
0
  WINPR_ASSERT(clipboard);
361
0
  for (UINT32 formatId = 0; formatId < clipboard->numFormats; formatId++)
362
0
  {
363
0
    wClipboardFormat* format = &clipboard->formats[formatId];
364
0
    free(format->formatName);
365
0
    free(format->synthesizers);
366
0
    format->formatName = NULL;
367
0
    format->synthesizers = NULL;
368
0
  }
369
0
}
370
371
static BOOL ClipboardInitFormats(wClipboard* clipboard)
372
0
{
373
0
  UINT32 formatId = 0;
374
0
  wClipboardFormat* format = NULL;
375
376
0
  if (!clipboard)
377
0
    return FALSE;
378
379
0
  for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
380
0
  {
381
0
    format = &(clipboard->formats[clipboard->numFormats]);
382
0
    ZeroMemory(format, sizeof(wClipboardFormat));
383
0
    format->formatId = formatId;
384
0
    format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
385
386
0
    if (!format->formatName)
387
0
      goto error;
388
0
  }
389
390
0
  if (!ClipboardInitSynthesizers(clipboard))
391
0
    goto error;
392
393
0
  return TRUE;
394
0
error:
395
396
0
  ClipboardUninitFormats(clipboard);
397
0
  return FALSE;
398
0
}
399
400
UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name)
401
0
{
402
0
  wClipboardFormat* format = NULL;
403
404
0
  if (!clipboard)
405
0
    return 0;
406
407
0
  format = ClipboardFindFormat(clipboard, 0, name);
408
409
0
  if (!format)
410
0
    return 0;
411
412
0
  return format->formatId;
413
0
}
414
415
const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
416
0
{
417
0
  wClipboardFormat* format = NULL;
418
419
0
  if (!clipboard)
420
0
    return NULL;
421
422
0
  format = ClipboardFindFormat(clipboard, formatId, NULL);
423
424
0
  if (!format)
425
0
    return NULL;
426
427
0
  return format->formatName;
428
0
}
429
430
void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
431
0
{
432
0
  UINT32 SrcSize = 0;
433
0
  UINT32 DstSize = 0;
434
0
  void* pSrcData = NULL;
435
0
  void* pDstData = NULL;
436
0
  wClipboardFormat* format = NULL;
437
0
  wClipboardSynthesizer* synthesizer = NULL;
438
439
0
  if (!clipboard || !pSize)
440
0
  {
441
0
    WLog_ERR(TAG, "Invalid parameters clipboard=%p, pSize=%p", clipboard, pSize);
442
0
    return NULL;
443
0
  }
444
445
0
  *pSize = 0;
446
0
  format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
447
448
0
  if (!format)
449
0
  {
450
0
    WLog_ERR(TAG, "Format [0x%08" PRIx32 "] not found", clipboard->formatId);
451
0
    return NULL;
452
0
  }
453
454
0
  SrcSize = clipboard->size;
455
0
  pSrcData = clipboard->data;
456
457
0
  if (formatId == format->formatId)
458
0
  {
459
0
    DstSize = SrcSize;
460
0
    pDstData = malloc(DstSize);
461
462
0
    if (!pDstData)
463
0
      return NULL;
464
465
0
    CopyMemory(pDstData, pSrcData, SrcSize);
466
0
    *pSize = DstSize;
467
0
  }
468
0
  else
469
0
  {
470
0
    synthesizer = ClipboardFindSynthesizer(format, formatId);
471
472
0
    if (!synthesizer || !synthesizer->pfnSynthesize)
473
0
    {
474
0
      WLog_ERR(TAG, "No synthesizer for format %s [0x%08" PRIx32 "] --> %s [0x%08" PRIx32 "]",
475
0
               ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
476
0
               ClipboardGetFormatName(clipboard, formatId), formatId);
477
0
      return NULL;
478
0
    }
479
480
0
    DstSize = SrcSize;
481
0
    pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
482
0
    if (pDstData)
483
0
      *pSize = DstSize;
484
0
  }
485
486
0
  WLog_DBG(TAG, "getting formatId=%s [0x%08" PRIx32 "] data=%p, size=%" PRIu32,
487
0
           ClipboardGetFormatName(clipboard, formatId), formatId, pDstData, *pSize);
488
0
  return pDstData;
489
0
}
490
491
BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32 size)
492
0
{
493
0
  wClipboardFormat* format = NULL;
494
495
0
  WLog_DBG(TAG, "setting formatId=%s [0x%08" PRIx32 "], size=%" PRIu32,
496
0
           ClipboardGetFormatName(clipboard, formatId), formatId, size);
497
0
  if (!clipboard)
498
0
    return FALSE;
499
500
0
  format = ClipboardFindFormat(clipboard, formatId, NULL);
501
502
0
  if (!format)
503
0
    return FALSE;
504
505
0
  free(clipboard->data);
506
507
0
  clipboard->data = calloc(size + sizeof(WCHAR), sizeof(char));
508
509
0
  if (!clipboard->data)
510
0
    return FALSE;
511
512
0
  memcpy(clipboard->data, data, size);
513
514
  /* For string values we don“t know if they are '\0' terminated.
515
   * so set the size to the full length in bytes (e.g. string length + 1)
516
   */
517
0
  switch (formatId)
518
0
  {
519
0
    case CF_TEXT:
520
0
    case CF_OEMTEXT:
521
0
      clipboard->size = (UINT32)(strnlen(clipboard->data, size) + 1UL);
522
0
      break;
523
0
    case CF_UNICODETEXT:
524
0
      clipboard->size =
525
0
          (UINT32)((_wcsnlen(clipboard->data, size / sizeof(WCHAR)) + 1UL) * sizeof(WCHAR));
526
0
      break;
527
0
    default:
528
0
      clipboard->size = size;
529
0
      break;
530
0
  }
531
532
0
  clipboard->formatId = formatId;
533
0
  clipboard->sequenceNumber++;
534
0
  return TRUE;
535
0
}
536
537
UINT64 ClipboardGetOwner(wClipboard* clipboard)
538
0
{
539
0
  if (!clipboard)
540
0
    return 0;
541
542
0
  return clipboard->ownerId;
543
0
}
544
545
void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
546
0
{
547
0
  if (!clipboard)
548
0
    return;
549
550
0
  clipboard->ownerId = ownerId;
551
0
}
552
553
wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
554
0
{
555
0
  if (!clipboard)
556
0
    return NULL;
557
558
0
  return &clipboard->delegate;
559
0
}
560
561
static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
562
0
{
563
  /*
564
   * There can be only one local file subsystem active.
565
   * Return as soon as initialization succeeds.
566
   */
567
0
  if (ClipboardInitSyntheticFileSubsystem(clipboard))
568
0
  {
569
0
    WLog_DBG(TAG, "initialized synthetic local file subsystem");
570
0
    return;
571
0
  }
572
0
  else
573
0
  {
574
0
    WLog_WARN(TAG, "failed to initialize synthetic local file subsystem");
575
0
  }
576
577
0
  WLog_INFO(TAG, "failed to initialize local file subsystem, file transfer not available");
578
0
}
579
580
wClipboard* ClipboardCreate(void)
581
0
{
582
0
  wClipboard* clipboard = (wClipboard*)calloc(1, sizeof(wClipboard));
583
584
0
  if (!clipboard)
585
0
    return NULL;
586
587
0
  clipboard->nextFormatId = 0xC000;
588
0
  clipboard->sequenceNumber = 0;
589
590
0
  if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
591
0
    goto fail;
592
593
0
  clipboard->numFormats = 0;
594
0
  clipboard->maxFormats = 64;
595
0
  clipboard->formats = (wClipboardFormat*)calloc(clipboard->maxFormats, sizeof(wClipboardFormat));
596
597
0
  if (!clipboard->formats)
598
0
    goto fail;
599
600
0
  if (!ClipboardInitFormats(clipboard))
601
0
    goto fail;
602
603
0
  clipboard->delegate.clipboard = clipboard;
604
0
  ClipboardInitLocalFileSubsystem(clipboard);
605
0
  return clipboard;
606
0
fail:
607
0
  ClipboardDestroy(clipboard);
608
0
  return NULL;
609
0
}
610
611
void ClipboardDestroy(wClipboard* clipboard)
612
0
{
613
0
  if (!clipboard)
614
0
    return;
615
616
0
  ArrayList_Free(clipboard->localFiles);
617
0
  clipboard->localFiles = NULL;
618
619
0
  ClipboardUninitFormats(clipboard);
620
621
0
  free(clipboard->data);
622
0
  clipboard->data = NULL;
623
0
  clipboard->size = 0;
624
0
  clipboard->numFormats = 0;
625
0
  free(clipboard->formats);
626
0
  DeleteCriticalSection(&(clipboard->lock));
627
0
  free(clipboard);
628
0
}
629
630
static BOOL is_dos_drive(const char* path, size_t len)
631
0
{
632
0
  if (len < 2)
633
0
    return FALSE;
634
635
0
  WINPR_ASSERT(path);
636
0
  if (path[1] == ':' || path[1] == '|')
637
0
  {
638
0
    if (((path[0] >= 'A') && (path[0] <= 'Z')) || ((path[0] >= 'a') && (path[0] <= 'z')))
639
0
      return TRUE;
640
0
  }
641
0
  return FALSE;
642
0
}
643
644
char* parse_uri_to_local_file(const char* uri, size_t uri_len)
645
0
{
646
  // URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089
647
0
  const char prefix[] = "file:";
648
0
  const char prefixTraditional[] = "file://";
649
0
  const char* localName = NULL;
650
0
  size_t localLen = 0;
651
0
  char* buffer = NULL;
652
0
  const size_t prefixLen = strnlen(prefix, sizeof(prefix));
653
0
  const size_t prefixTraditionalLen = strnlen(prefixTraditional, sizeof(prefixTraditional));
654
655
0
  WINPR_ASSERT(uri || (uri_len == 0));
656
657
0
  WLog_VRB(TAG, "processing URI: %.*s", uri_len, uri);
658
659
0
  if ((uri_len <= prefixLen) || strncmp(uri, prefix, prefixLen) != 0)
660
0
  {
661
0
    WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
662
0
    return NULL;
663
0
  }
664
665
0
  do
666
0
  {
667
    /* https://datatracker.ietf.org/doc/html/rfc8089#appendix-F
668
     * - The minimal representation of a local file in a DOS- or Windows-
669
     *   based environment with no authority field and an absolute path
670
     *   that begins with a drive letter.
671
     *
672
     *   "file:c:/path/to/file"
673
     *
674
     * - Regular DOS or Windows file URIs with vertical line characters in
675
     *   the drive letter construct.
676
     *
677
     *   "file:c|/path/to/file"
678
     *
679
     */
680
0
    if (uri[prefixLen] != '/')
681
0
    {
682
683
0
      if (is_dos_drive(&uri[prefixLen], uri_len - prefixLen))
684
0
      {
685
        // Dos and Windows file URI
686
0
        localName = &uri[prefixLen];
687
0
        localLen = uri_len - prefixLen;
688
0
        break;
689
0
      }
690
0
      else
691
0
      {
692
0
        WLog_ERR(TAG, "URI format are not supported: %s", uri);
693
0
        return NULL;
694
0
      }
695
0
    }
696
697
    /*
698
     * - The minimal representation of a local file with no authority field
699
     *   and an absolute path that begins with a slash "/".  For example:
700
     *
701
     *   "file:/path/to/file"
702
     *
703
     */
704
0
    else if ((uri_len > prefixLen + 1) && (uri[prefixLen + 1] != '/'))
705
0
    {
706
0
      if (is_dos_drive(&uri[prefixLen + 1], uri_len - prefixLen - 1))
707
0
      {
708
        // Dos and Windows file URI
709
0
        localName = (uri + prefixLen + 1);
710
0
        localLen = uri_len - prefixLen - 1;
711
0
      }
712
0
      else
713
0
      {
714
0
        localName = &uri[prefixLen];
715
0
        localLen = uri_len - prefixLen;
716
0
      }
717
0
      break;
718
0
    }
719
720
    /*
721
     * - A traditional file URI for a local file with an empty authority.
722
     *
723
     *   "file:///path/to/file"
724
     */
725
0
    if ((uri_len < prefixTraditionalLen) ||
726
0
        strncmp(uri, prefixTraditional, prefixTraditionalLen) != 0)
727
0
    {
728
0
      WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
729
0
      return NULL;
730
0
    }
731
732
0
    localName = &uri[prefixTraditionalLen];
733
0
    localLen = uri_len - prefixTraditionalLen;
734
735
0
    if (localLen < 1)
736
0
    {
737
0
      WLog_ERR(TAG, "empty 'file:' URI schemes are not supported");
738
0
      return NULL;
739
0
    }
740
741
    /*
742
     * "file:///c:/path/to/file"
743
     * "file:///c|/path/to/file"
744
     */
745
0
    if (localName[0] != '/')
746
0
    {
747
0
      WLog_ERR(TAG, "URI format are not supported: %s", uri);
748
0
      return NULL;
749
0
    }
750
751
0
    if (is_dos_drive(&localName[1], localLen - 1))
752
0
    {
753
0
      localName++;
754
0
      localLen--;
755
0
    }
756
757
0
  } while (0);
758
759
0
  buffer = winpr_str_url_decode(localName, localLen);
760
0
  if (buffer)
761
0
  {
762
0
    if (buffer[1] == '|' &&
763
0
        ((buffer[0] >= 'A' && buffer[0] <= 'Z') || (buffer[0] >= 'a' && buffer[0] <= 'z')))
764
0
      buffer[1] = ':';
765
0
    return buffer;
766
0
  }
767
768
0
  return NULL;
769
0
}