Coverage Report

Created: 2026-10-28 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/unicode-transform.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "unichar.h"
5
#include "unicode-data.h"
6
#include "unicode-transform.h"
7
8
0
#define HANGUL_FIRST 0xac00
9
0
#define HANGUL_LAST 0xd7a3
10
11
/*
12
 * Transform
13
 */
14
15
ssize_t uniform_transform_forward(
16
  struct unicode_transform *trans, const uint32_t *out,
17
  const struct unicode_code_point_data *const *out_data, size_t out_len,
18
  const char **error_r)
19
0
{
20
0
  struct unicode_transform_buffer buf_next;
21
0
  ssize_t sret;
22
23
0
  i_zero(&buf_next);
24
0
  buf_next.cp = out;
25
0
  buf_next.cp_data = out_data;
26
0
  buf_next.cp_count = out_len;
27
28
0
  i_assert(trans->next != NULL);
29
0
  i_assert(trans->next->def != NULL);
30
0
  i_assert(trans->next->def->input != NULL);
31
0
  sret = trans->next->def->input(trans->next, &buf_next, error_r);
32
33
0
  i_assert(sret >= 0 || *error_r != NULL);
34
0
  i_assert(sret <= (ssize_t)out_len);
35
0
  return sret;
36
0
}
37
38
ssize_t unicode_transform_input_buf(struct unicode_transform *trans,
39
            const struct unicode_transform_buffer *buf,
40
            const char **error_r)
41
0
{
42
0
  struct unicode_transform_buffer in_buf;
43
0
  size_t input_total = 0;
44
0
  ssize_t sret;
45
0
  bool flushed = FALSE;
46
0
  int ret;
47
48
0
  *error_r = NULL;
49
50
0
  in_buf = *buf;
51
52
0
  while (in_buf.cp_count > 0) {
53
0
    if (in_buf.cp_count > 0) {
54
0
      i_assert(trans->def->input != NULL);
55
0
      sret = trans->def->input(trans, &in_buf, error_r);
56
0
      if (sret < 0) {
57
0
        i_assert(*error_r != NULL);
58
0
        return -1;
59
0
      }
60
0
      if (sret > 0) {
61
0
        i_assert((size_t)sret <= in_buf.cp_count);
62
0
        in_buf.cp += sret;
63
0
        in_buf.cp_count -= sret;
64
0
        input_total += sret;
65
0
        flushed = FALSE;
66
0
        continue;
67
0
      }
68
0
      if (sret == 0 && flushed)
69
0
        break;
70
0
    }
71
72
0
    struct unicode_transform *tp = trans;
73
74
0
    while (tp->next != NULL) {
75
0
      if (tp->def->flush != NULL) {
76
0
        ret = tp->def->flush(tp, FALSE, error_r);
77
0
        if (ret < 0) {
78
0
          i_assert(*error_r != NULL);
79
0
          return -1;
80
0
        }
81
0
      }
82
0
      tp = tp->next;
83
0
    }
84
85
0
    flushed = TRUE;
86
0
  }
87
88
0
  return input_total;
89
0
}
90
91
int unicode_transform_flush(struct unicode_transform *trans,
92
          const char **error_r)
93
0
{
94
0
  int ret;
95
96
0
  *error_r = NULL;
97
98
0
  while (trans != NULL) {
99
0
    struct unicode_transform *tp = trans;
100
0
    bool progress = FALSE;
101
102
0
    while (tp != NULL) {
103
0
      if (tp->def->flush == NULL) {
104
0
        progress = TRUE;
105
0
        if (tp == trans)
106
0
          trans = trans->next;
107
0
      } else {
108
0
        ret = tp->def->flush(tp, (tp == trans), error_r);
109
0
        if (ret < 0) {
110
0
          i_assert(*error_r != NULL);
111
0
          return -1;
112
0
        }
113
0
        if (ret > 0) {
114
0
          progress = TRUE;
115
0
          if (tp == trans)
116
0
            trans = trans->next;
117
0
        }
118
0
      }
119
0
      tp = tp->next;
120
0
    }
121
0
    if (!progress)
122
0
      return 0;
123
0
  }
124
0
  return 1;
125
0
}
126
127
/* Buffer Sink */
128
129
static ssize_t
130
unicode_buffer_sink_input(struct unicode_transform *trans,
131
        const struct unicode_transform_buffer *buf,
132
        const char **error_r);
133
134
static const struct unicode_transform_def unicode_buffer_sink_def = {
135
  .input = unicode_buffer_sink_input,
136
};
137
138
void unicode_buffer_sink_init(struct unicode_buffer_sink *sink,
139
            buffer_t *buffer)
140
0
{
141
0
  i_zero(sink);
142
0
  unicode_transform_init(&sink->transform, &unicode_buffer_sink_def);
143
0
  sink->buffer = buffer;
144
0
}
145
146
static ssize_t
147
unicode_buffer_sink_input(struct unicode_transform *trans,
148
        const struct unicode_transform_buffer *buf,
149
        const char **error_r ATTR_UNUSED)
150
0
{
151
0
  struct unicode_buffer_sink *sink =
152
0
    container_of(trans, struct unicode_buffer_sink, transform);
153
154
0
  uni_ucs4_to_utf8(buf->cp, buf->cp_count, sink->buffer);
155
0
  return buf->cp_count;
156
0
}
157
158
/* Static Array Sink */
159
160
static ssize_t
161
unicode_static_array_sink_input(struct unicode_transform *trans,
162
        const struct unicode_transform_buffer *buf,
163
        const char **error_r);
164
165
static const struct unicode_transform_def unicode_static_array_sink_def = {
166
  .input = unicode_static_array_sink_input,
167
};
168
169
void unicode_static_array_sink_init(struct unicode_static_array_sink *sink,
170
            uint32_t *array, size_t array_size,
171
            size_t *array_pos)
172
0
{
173
0
  i_zero(sink);
174
0
  unicode_transform_init(&sink->transform,
175
0
             &unicode_static_array_sink_def);
176
0
  sink->array = array;
177
0
  sink->array_size = array_size;
178
0
  sink->array_pos = array_pos;
179
0
}
180
181
static ssize_t
182
unicode_static_array_sink_input(struct unicode_transform *trans,
183
        const struct unicode_transform_buffer *buf,
184
        const char **error_r)
185
0
{
186
0
  struct unicode_static_array_sink *sink =
187
0
    container_of(trans, struct unicode_static_array_sink,
188
0
           transform);
189
190
0
  if (*sink->array_pos + buf->cp_count > sink->array_size) {
191
0
    *error_r = "Output overflow";
192
0
    return -1;
193
0
  }
194
0
  memcpy(sink->array + *sink->array_pos, buf->cp,
195
0
         buf->cp_count * sizeof(*buf->cp));
196
0
  *sink->array_pos += buf->cp_count;
197
0
  return buf->cp_count;
198
0
}
199
200
/*
201
 * Hangul syllable (de)composition
202
 */
203
204
0
#define UNI_HANGUL_S_BASE 0xac00
205
0
#define UNI_HANGUL_L_BASE 0x1100
206
0
#define UNI_HANGUL_V_BASE 0x1161
207
0
#define UNI_HANGUL_T_BASE 0x11a7
208
0
#define UNI_HANGUL_L_COUNT 19
209
0
#define UNI_HANGUL_V_COUNT 21
210
0
#define UNI_HANGUL_T_COUNT 28
211
0
#define UNI_HANGUL_N_COUNT (UNI_HANGUL_V_COUNT * UNI_HANGUL_T_COUNT)
212
0
#define UNI_HANGUL_L_END (UNI_HANGUL_L_BASE + UNI_HANGUL_L_COUNT)
213
0
#define UNI_HANGUL_V_END (UNI_HANGUL_V_BASE + UNI_HANGUL_V_COUNT)
214
0
#define UNI_HANGUL_T_END (UNI_HANGUL_T_BASE + UNI_HANGUL_T_COUNT)
215
0
#define UNI_HANGUL_S_END 0xD7A4
216
217
static size_t unicode_hangul_decompose(uint32_t cp, uint32_t buf[3])
218
0
{
219
  /* The Unicode Standard, Section 3.12.2:
220
     Hangul Syllable Decomposition
221
   */
222
223
0
  size_t s_index = cp - UNI_HANGUL_S_BASE;
224
0
  size_t l_index = s_index / UNI_HANGUL_N_COUNT;
225
0
  size_t v_index = ((s_index % UNI_HANGUL_N_COUNT) / UNI_HANGUL_T_COUNT);
226
0
  size_t t_index = s_index % UNI_HANGUL_T_COUNT;
227
0
  uint32_t l_part = UNI_HANGUL_L_BASE + l_index;
228
0
  uint32_t v_part = UNI_HANGUL_V_BASE + v_index;
229
230
0
  if (t_index == 0) {
231
0
    buf[0] = l_part;
232
0
    buf[1] = v_part;
233
0
    return 2;
234
0
  }
235
236
0
  uint32_t t_part = UNI_HANGUL_T_BASE + t_index;
237
238
0
  buf[0] = l_part;
239
0
  buf[1] = v_part;
240
0
  buf[2] = t_part;
241
0
  return 3;
242
0
}
243
244
static uint32_t unicode_hangul_compose_pair(uint32_t l, uint32_t r)
245
0
{
246
  /* The Unicode Standard, Section 3.12.3:
247
     Hangul Syllable Composition
248
   */
249
250
  /* <LPart, VPart> */
251
0
  if (l >= UNI_HANGUL_L_BASE && l < UNI_HANGUL_L_END &&
252
0
      r >= UNI_HANGUL_V_BASE && r < UNI_HANGUL_V_END) {
253
0
    uint32_t l_part = l, v_part = r;
254
255
0
    size_t l_index = l_part - UNI_HANGUL_L_BASE;
256
0
    size_t v_index = v_part - UNI_HANGUL_V_BASE;
257
0
    size_t lv_index = l_index * UNI_HANGUL_N_COUNT +
258
0
          v_index * UNI_HANGUL_T_COUNT;
259
0
    return UNI_HANGUL_S_BASE + lv_index;
260
0
  }
261
  /* A sequence <LVPart, TPart> */
262
0
  if (l >= UNI_HANGUL_S_BASE && l < UNI_HANGUL_S_END &&
263
0
      r >= (UNI_HANGUL_T_BASE + 1u) && r < UNI_HANGUL_T_END &&
264
0
      ((l - UNI_HANGUL_S_BASE) % UNI_HANGUL_T_COUNT) == 0) {
265
0
    uint32_t lv_part = l, t_part = r;
266
267
0
    size_t t_index = t_part - UNI_HANGUL_T_BASE;
268
0
    return lv_part + t_index;
269
0
  }
270
0
  return 0x0000;
271
0
}
272
273
/*
274
 * Normalization transform: NFD, NFKD, NFC, NFKC
275
 */
276
277
/* The transform buffers the decomposed code points until they can't change
278
   anymore:
279
280
   - The Canonical Ordering Algorithm only reorders non-starters, and never
281
     across a starter. So everything up to and including the last starter in
282
     the buffer is in its final order, while the non-starters after it may
283
     still be reordered by the following input.
284
   - For NFC and NFKC, the Canonical Composition Algorithm is run
285
     incrementally on the code points that are in their final order. A later
286
     code point can only compose with the last starter, because any starter
287
     in between blocks it. So everything before the last starter is final.
288
289
   The Stream-Safe Text Process (UAX15-D4) limits the number of non-starters
290
   after the last starter to UNICODE_NF_STREAM_SAFE_NON_STARTER_LEN. So after
291
   the final code points are forwarded, the buffer contains at most a starter
292
   and the non-starters after it. UNICODE_NF_BUFFER_SIZE leaves space for a
293
   CGJ and the longest decomposition after them, so the next code point can
294
   always be accepted after the final code points are forwarded. */
295
296
0
#define UNICODE_COMBINING_GRAPHEME_JOINER 0x034f
297
298
static ssize_t
299
unicode_nf_input(struct unicode_transform *trans,
300
     const struct unicode_transform_buffer *buf,
301
     const char **error_r);
302
static int
303
unicode_nf_flush(struct unicode_transform *trans, bool finished,
304
     const char **error_r);
305
306
static const struct unicode_transform_def unicode_nf_def = {
307
  .input = unicode_nf_input,
308
  .flush = unicode_nf_flush,
309
};
310
311
void unicode_nf_init(struct unicode_nf_context *ctx_r,
312
         enum unicode_nf_type type)
313
0
{
314
  /* The buffers are written before they're read */
315
0
  memset(ctx_r, 0, offsetof(struct unicode_nf_context, cp_buffer));
316
0
  unicode_transform_init(&ctx_r->transform, &unicode_nf_def);
317
318
0
  switch (type) {
319
0
  case UNICODE_NFD:
320
0
    ctx_r->canonical = TRUE;
321
0
    break;
322
0
  case UNICODE_NFKD:
323
0
    break;
324
0
  case UNICODE_NFC:
325
0
    ctx_r->compose = TRUE;
326
0
    ctx_r->canonical = TRUE;
327
0
    break;
328
0
  case UNICODE_NFKC:
329
0
    ctx_r->compose = TRUE;
330
0
    break;
331
0
  }
332
0
}
333
334
void unicode_nf_reset(struct unicode_nf_context *ctx)
335
0
{
336
0
  enum unicode_nf_type type =
337
0
    (ctx->compose ? (ctx->canonical ? UNICODE_NFC : UNICODE_NFKC) :
338
0
        (ctx->canonical ? UNICODE_NFD : UNICODE_NFKD));
339
0
  struct unicode_transform *next = ctx->transform.next;
340
341
0
  unicode_nf_init(ctx, type);
342
0
  unicode_transform_chain(&ctx->transform, next);
343
0
}
344
345
static void
346
unicode_nf_buffer_delete(struct unicode_nf_context *ctx, size_t offset,
347
       size_t count)
348
0
{
349
0
  if (count == 0)
350
0
    return;
351
352
0
  i_assert(offset < ctx->buffer_len);
353
0
  i_assert(count <= ctx->buffer_len);
354
0
  i_assert(offset <= (ctx->buffer_len - count));
355
356
0
  if (count == ctx->buffer_len) {
357
0
    ctx->buffer_len = 0;
358
0
    return;
359
0
  }
360
361
0
  size_t trailer = ctx->buffer_len - (offset + count);
362
0
  if (trailer > 0) {
363
0
    memmove(&ctx->cp_buffer[offset],
364
0
      &ctx->cp_buffer[offset + count],
365
0
      trailer * sizeof(ctx->cp_buffer[0]));
366
0
    memmove(&ctx->cpd_buffer[offset],
367
0
      &ctx->cpd_buffer[offset + count],
368
0
      trailer * sizeof(ctx->cpd_buffer[0]));
369
0
  }
370
0
  ctx->buffer_len -= count;
371
0
}
372
373
static void
374
unicode_nf_buffer_delete_head(struct unicode_nf_context *ctx, size_t count)
375
0
{
376
0
  if (count == 0)
377
0
    return;
378
379
0
  i_assert(count <= ctx->buffer_output_max);
380
0
  unicode_nf_buffer_delete(ctx, 0, count);
381
382
0
  ctx->buffer_output_max -= count;
383
0
  ctx->run_pos -= count;
384
0
  if (ctx->compose)
385
0
    ctx->composed_pos -= count;
386
0
  if (ctx->have_starter) {
387
    /* The starter is forwarded only when flushing at the end */
388
0
    if (ctx->starter_pos >= count)
389
0
      ctx->starter_pos -= count;
390
0
    else
391
0
      ctx->have_starter = FALSE;
392
0
  }
393
0
}
394
395
static void
396
unicode_nf_buffer_append(struct unicode_nf_context *ctx, uint32_t cp,
397
       const struct unicode_code_point_data *cpd)
398
0
{
399
0
  uint8_t ccc = cpd->canonical_combining_class;
400
0
  size_t pos = ctx->buffer_len;
401
402
0
  i_assert(ctx->buffer_len < UNICODE_NF_BUFFER_SIZE);
403
0
  if (ccc != 0) {
404
    /* Apply the Canonical Ordering Algorithm (COA): Insert the
405
       non-starter after the preceding code points that have the
406
       same or a lower combining class. Starters have combining
407
       class 0, so a non-starter is never moved before a
408
       starter. */
409
0
    while (pos > 0 &&
410
0
           ctx->cpd_buffer[pos - 1]->canonical_combining_class > ccc) {
411
0
      ctx->cp_buffer[pos] = ctx->cp_buffer[pos - 1];
412
0
      ctx->cpd_buffer[pos] = ctx->cpd_buffer[pos - 1];
413
0
      pos--;
414
0
    }
415
0
  }
416
0
  ctx->cp_buffer[pos] = cp;
417
0
  ctx->cpd_buffer[pos] = cpd;
418
0
  ctx->buffer_len++;
419
0
  if (ccc == 0)
420
0
    ctx->run_pos = ctx->buffer_len;
421
0
}
422
423
static uint32_t
424
unicode_nf_compose_pair(uint32_t l, uint32_t r,
425
      const struct unicode_code_point_data *l_data)
426
0
{
427
0
  uint32_t comp = unicode_hangul_compose_pair(l, r);
428
429
0
  if (comp > 0x0000)
430
0
    return comp;
431
0
  return unicode_code_point_data_find_composition(l_data, r);
432
0
}
433
434
static void unicode_nf_compose(struct unicode_nf_context *ctx)
435
0
{
436
0
  size_t in_pos, out_pos = ctx->composed_pos;
437
438
  /* Apply the Canonical Composition Algorithm (UAX15-D117) to the code
439
     points whose order is final. */
440
0
  for (in_pos = ctx->composed_pos; in_pos < ctx->run_pos; in_pos++) {
441
0
    uint32_t cp = ctx->cp_buffer[in_pos];
442
0
    const struct unicode_code_point_data *cpd =
443
0
      ctx->cpd_buffer[in_pos];
444
0
    uint8_t ccc = cpd->canonical_combining_class;
445
446
    /* The code point isn't blocked from the last starter, if
447
       all the code points between them have a lower combining
448
       class. A starter is blocked by anything in between. */
449
0
    if (ctx->have_starter && ctx->last_ccc < (int)ccc) {
450
0
      size_t starter_pos = ctx->starter_pos;
451
0
      uint32_t comp = unicode_nf_compose_pair(
452
0
        ctx->cp_buffer[starter_pos], cp,
453
0
        ctx->cpd_buffer[starter_pos]);
454
455
0
      if (comp > 0x0000) {
456
0
        ctx->cp_buffer[starter_pos] = comp;
457
0
        ctx->cpd_buffer[starter_pos] =
458
0
          unicode_code_point_get_data(comp);
459
0
        continue;
460
0
      }
461
0
    }
462
0
    if (ccc == 0) {
463
0
      ctx->have_starter = TRUE;
464
0
      ctx->starter_pos = out_pos;
465
0
      ctx->last_ccc = -1;
466
0
    } else {
467
0
      ctx->last_ccc = ccc;
468
0
    }
469
0
    ctx->cp_buffer[out_pos] = cp;
470
0
    ctx->cpd_buffer[out_pos] = cpd;
471
0
    out_pos++;
472
0
  }
473
474
0
  if (in_pos > out_pos) {
475
    /* Remove the gap left by the composed code points */
476
0
    unicode_nf_buffer_delete(ctx, out_pos, (in_pos - out_pos));
477
0
    ctx->run_pos = out_pos;
478
0
  }
479
0
  ctx->composed_pos = out_pos;
480
0
}
481
482
static void unicode_nf_update_output_max(struct unicode_nf_context *ctx)
483
0
{
484
0
  if (!ctx->compose)
485
0
    ctx->buffer_output_max = ctx->run_pos;
486
0
  else {
487
0
    unicode_nf_compose(ctx);
488
0
    ctx->buffer_output_max = (ctx->have_starter ?
489
0
            ctx->starter_pos : ctx->composed_pos);
490
0
  }
491
0
}
492
493
static void
494
unicode_nf_get_ccc_counts(const uint32_t *decomp, size_t len,
495
        const struct unicode_code_point_data *const *decomp_cpd,
496
        size_t *lead_r, size_t *trail_r, bool *seen_starter_r)
497
0
{
498
0
  const struct unicode_code_point_data *cpd;
499
0
  size_t i;
500
501
0
  *lead_r = *trail_r = 0;
502
0
  *seen_starter_r = FALSE;
503
0
  for (i = 0; i < len; i++) {
504
0
    cpd = (decomp_cpd != NULL ? decomp_cpd[i] :
505
0
           unicode_code_point_get_data(decomp[i]));
506
0
    if (cpd->canonical_combining_class == 0) {
507
0
      *seen_starter_r = TRUE;
508
0
      *trail_r = 0;
509
0
    } else if (!*seen_starter_r)
510
0
      (*lead_r)++;
511
0
    else
512
0
      (*trail_r)++;
513
0
  }
514
0
}
515
516
static bool
517
unicode_nf_input_cp(struct unicode_nf_context *ctx, uint32_t cp,
518
        const struct unicode_code_point_data *cpd)
519
0
{
520
0
  const struct unicode_code_point_data *
521
0
    decomp_cpd[UNICODE_DECOMPOSITION_MAX_LENGTH];
522
0
  const uint32_t *decomp, *decomp_k;
523
0
  uint32_t decomp_hangul[3];
524
0
  size_t len, len_k, i;
525
526
  /*
527
   * Decompose the code point
528
   */
529
530
0
  if (cp >= HANGUL_FIRST && cp <= HANGUL_LAST) {
531
0
    len = len_k = unicode_hangul_decompose(cp, decomp_hangul);
532
0
    decomp = decomp_k = decomp_hangul;
533
0
    for (i = 0; i < len; i++)
534
0
      decomp_cpd[i] = unicode_code_point_get_data(decomp[i]);
535
0
  } else {
536
0
    if (cpd == NULL)
537
0
      cpd = unicode_code_point_get_data(cp);
538
0
    len = unicode_code_point_data_get_full_decomposition(
539
0
      cpd, ctx->canonical, &decomp);
540
0
    if (len == 0) {
541
0
      decomp = &cp;
542
0
      len = 1;
543
0
      decomp_cpd[0] = cpd;
544
0
    } else {
545
0
      for (i = 0; i < len; i++) {
546
0
        decomp_cpd[i] =
547
0
          unicode_code_point_get_data(decomp[i]);
548
0
      }
549
0
    }
550
0
    len_k = len;
551
0
    decomp_k = decomp;
552
0
    if (ctx->canonical) {
553
0
      len_k = unicode_code_point_data_get_full_decomposition(
554
0
        cpd, FALSE, &decomp_k);
555
0
      if (len_k == 0) {
556
0
        decomp_k = decomp;
557
0
        len_k = len;
558
0
      }
559
0
    }
560
0
  }
561
562
0
  i_assert(len <= UNICODE_DECOMPOSITION_MAX_LENGTH);
563
0
  i_assert(len_k <= UNICODE_DECOMPOSITION_MAX_LENGTH);
564
  /* decomp_cpd[] is reused below when decomp_k == decomp */
565
0
  i_assert(decomp_k != decomp || len_k == len);
566
567
  /* UAX15-D4: Stream-Safe Text Process is the process of producing a
568
     Unicode string in Stream-Safe Text Format by processing that string
569
     from start to finish, inserting U+034F COMBINING GRAPHEME JOINER
570
     (CGJ) within long sequences of non-starters. The exact position of
571
     the inserted CGJs are determined according to the following
572
     algorithm, which describes the generation of an output string from an
573
     input string:
574
575
     1. If the input string is empty, return an empty output string.
576
     2. Set nonStarterCount to zero.
577
     3. For each code point C in the input string:
578
    a. Produce the NFKD decomposition S.
579
    b. If nonStarterCount plus the number of initial non-starters in
580
       S is greater than 30, append a CGJ to the output string and
581
       set the nonStarterCount to zero.
582
    c. Append C to the output string.
583
    d. If there are no starters in S, increment nonStarterCount by
584
       the number of code points in S; otherwise, set
585
       nonStarterCount to the number of trailing non-starters in S
586
       (which may be zero).
587
     4. Return the output string.
588
   */
589
0
  size_t ns_lead, ns_trail;
590
0
  bool seen_starter, cgj;
591
592
0
  unicode_nf_get_ccc_counts(decomp_k, len_k,
593
0
          (decomp_k == decomp ? decomp_cpd : NULL),
594
0
          &ns_lead, &ns_trail, &seen_starter);
595
0
  cgj = (ctx->nonstarter_count + ns_lead >
596
0
         UNICODE_NF_STREAM_SAFE_NON_STARTER_LEN);
597
598
0
  if (ctx->buffer_len + len + (cgj ? 1 : 0) > UNICODE_NF_BUFFER_SIZE) {
599
    /* Buffer is full. The final code points need to be
600
       forwarded first. */
601
0
    return FALSE;
602
0
  }
603
604
0
  if (cgj) {
605
0
    unicode_nf_buffer_append(ctx, UNICODE_COMBINING_GRAPHEME_JOINER,
606
0
      unicode_code_point_get_data(
607
0
        UNICODE_COMBINING_GRAPHEME_JOINER));
608
0
    ctx->nonstarter_count = 0;
609
0
  }
610
0
  if (seen_starter)
611
0
    ctx->nonstarter_count = ns_trail;
612
0
  else
613
0
    ctx->nonstarter_count += len_k;
614
615
0
  for (i = 0; i < len; i++)
616
0
    unicode_nf_buffer_append(ctx, decomp[i], decomp_cpd[i]);
617
618
0
  unicode_nf_update_output_max(ctx);
619
0
  return TRUE;
620
0
}
621
622
static ssize_t
623
unicode_nf_forward(struct unicode_nf_context *ctx, const char **error_r)
624
0
{
625
0
  ssize_t sret;
626
627
0
  if (ctx->buffer_output_max == 0)
628
0
    return 0;
629
630
0
  sret = uniform_transform_forward(&ctx->transform, ctx->cp_buffer,
631
0
           ctx->cpd_buffer,
632
0
           ctx->buffer_output_max, error_r);
633
0
  if (sret < 0)
634
0
    return -1;
635
0
  unicode_nf_buffer_delete_head(ctx, sret);
636
0
  return sret;
637
0
}
638
639
static ssize_t
640
unicode_nf_input(struct unicode_transform *trans,
641
     const struct unicode_transform_buffer *buf,
642
     const char **error_r)
643
0
{
644
0
  struct unicode_nf_context *ctx =
645
0
    container_of(trans, struct unicode_nf_context, transform);
646
0
  ssize_t sret;
647
0
  size_t n = 0;
648
649
0
  while (n < buf->cp_count) {
650
0
    if (unicode_nf_input_cp(ctx, buf->cp[n],
651
0
          (buf->cp_data == NULL ?
652
0
           NULL : buf->cp_data[n]))) {
653
0
      n++;
654
0
      continue;
655
0
    }
656
657
    /* Only the final code points can fill the buffer */
658
0
    i_assert(ctx->buffer_output_max > 0);
659
0
    sret = unicode_nf_forward(ctx, error_r);
660
0
    if (sret < 0)
661
0
      return -1;
662
0
    if (sret == 0) {
663
      /* The next transform is full */
664
0
      break;
665
0
    }
666
0
  }
667
668
0
  if (unicode_nf_forward(ctx, error_r) < 0)
669
0
    return -1;
670
0
  return n;
671
0
}
672
673
static int
674
unicode_nf_flush(struct unicode_transform *trans, bool finished,
675
     const char **error_r)
676
0
{
677
0
  struct unicode_nf_context *ctx =
678
0
    container_of(trans, struct unicode_nf_context, transform);
679
680
0
  if (finished) {
681
    /* No more input, so everything is final */
682
0
    ctx->run_pos = ctx->buffer_len;
683
0
    if (ctx->compose)
684
0
      unicode_nf_compose(ctx);
685
0
    ctx->buffer_output_max = ctx->buffer_len;
686
0
  }
687
688
0
  if (unicode_nf_forward(ctx, error_r) < 0)
689
0
    return -1;
690
0
  return (ctx->buffer_output_max == 0 ? 1 : 0);
691
0
}
692
693
/*
694
 * Normalization check
695
 */
696
697
static ssize_t
698
unicode_nf_check_sink_input(struct unicode_transform *trans,
699
          const struct unicode_transform_buffer *buf,
700
          const char **error_r);
701
702
static const struct unicode_transform_def unicode_nf_check_sink_def = {
703
  .input = unicode_nf_check_sink_input,
704
};
705
706
void unicode_nf_checker_init(struct unicode_nf_checker *unc_r,
707
           enum unicode_nf_type type)
708
0
{
709
0
  i_zero(unc_r);
710
711
0
  switch (type) {
712
0
  case UNICODE_NFD:
713
0
    unc_r->canonical = TRUE;
714
0
    unc_r->nf_qc_mask = UNICODE_NFD_QUICK_CHECK_MASK;
715
0
    unc_r->nf_qc_yes = UNICODE_NFD_QUICK_CHECK_YES;
716
0
    unc_r->nf_qc_no = UNICODE_NFD_QUICK_CHECK_NO;
717
0
    break;
718
0
  case UNICODE_NFKD:
719
0
    unc_r->nf_qc_mask = UNICODE_NFKD_QUICK_CHECK_MASK;
720
0
    unc_r->nf_qc_yes = UNICODE_NFKD_QUICK_CHECK_YES;
721
0
    unc_r->nf_qc_no = UNICODE_NFKD_QUICK_CHECK_NO;
722
0
    break;
723
0
  case UNICODE_NFC:
724
0
    unc_r->compose = TRUE;
725
0
    unc_r->canonical = TRUE;
726
0
    unc_r->nf_qc_mask = UNICODE_NFC_QUICK_CHECK_MASK;
727
0
    unc_r->nf_qc_yes = UNICODE_NFC_QUICK_CHECK_YES;
728
0
    unc_r->nf_qc_no = UNICODE_NFC_QUICK_CHECK_NO;
729
0
    break;
730
0
  case UNICODE_NFKC:
731
0
    unc_r->compose = TRUE;
732
0
    unc_r->nf_qc_mask = UNICODE_NFKC_QUICK_CHECK_MASK;
733
0
    unc_r->nf_qc_yes = UNICODE_NFKC_QUICK_CHECK_YES;
734
0
    unc_r->nf_qc_no = UNICODE_NFKC_QUICK_CHECK_NO;
735
0
    break;
736
0
  }
737
738
0
  unicode_nf_init(&unc_r->nf, type);
739
0
  unicode_transform_init(&unc_r->sink, &unicode_nf_check_sink_def);
740
0
  unicode_transform_chain(&unc_r->nf.transform, &unc_r->sink);
741
0
}
742
743
void unicode_nf_checker_reset(struct unicode_nf_checker *unc)
744
0
{
745
0
  enum unicode_nf_type type =
746
0
    (unc->compose ? (unc->canonical ? UNICODE_NFC : UNICODE_NFKC) :
747
0
        (unc->canonical ? UNICODE_NFD : UNICODE_NFKD));
748
749
0
  unicode_nf_checker_init(unc, type);
750
0
}
751
752
static ssize_t
753
unicode_nf_check_sink_input(struct unicode_transform *trans,
754
          const struct unicode_transform_buffer *buf,
755
          const char **error_r)
756
0
{
757
0
  struct unicode_nf_checker *unc =
758
0
    container_of(trans, struct unicode_nf_checker, sink);
759
0
  size_t n;
760
761
0
  if (buf->cp_count > unc->buffer_len) {
762
0
    *error_r = "Not normalized";
763
0
    return -1;
764
0
  }
765
0
  for (n = 0; n < buf->cp_count; n++) {
766
0
    if (buf->cp[n] != unc->cp_buffer[n]) {
767
0
      *error_r = "Not normalized";
768
0
      return -1;
769
0
    }
770
0
  }
771
0
  unc->buffer_len -= buf->cp_count;
772
0
  memmove(&unc->cp_buffer[0], &unc->cp_buffer[buf->cp_count],
773
0
    unc->buffer_len * sizeof(unc->cp_buffer[0]));
774
0
  return buf->cp_count;
775
0
}
776
777
static int
778
unicode_nf_checker_nf_input(struct unicode_nf_checker *unc, uint32_t cp,
779
          const struct unicode_code_point_data *cp_data)
780
0
{
781
0
  struct unicode_transform_buffer buf;
782
0
  const char *error;
783
0
  ssize_t sret;
784
785
  /* Add the code point to the buffer first, since the normalization
786
     transform may output it immediately. */
787
0
  i_assert(unc->buffer_len < N_ELEMENTS(unc->cp_buffer));
788
0
  unc->cp_buffer[unc->buffer_len++] = cp;
789
0
  unc->nf_used = TRUE;
790
791
0
  i_zero(&buf);
792
0
  buf.cp = &cp;
793
0
  buf.cp_data = &cp_data;
794
0
  buf.cp_count = 1;
795
0
  sret = unicode_transform_input_buf(&unc->nf.transform, &buf, &error);
796
0
  if (sret < 0)
797
0
    return -1;
798
0
  i_assert(sret == 1);
799
0
  return 0;
800
0
}
801
802
static int unicode_nf_checker_nf_finish(struct unicode_nf_checker *unc)
803
0
{
804
0
  const char *error;
805
0
  int ret;
806
807
0
  if (!unc->nf_used)
808
0
    return 1;
809
810
0
  ret = unicode_transform_flush(&unc->nf.transform, &error);
811
0
  i_assert(ret != 0);
812
0
  if (ret < 0 || unc->buffer_len > 0)
813
0
    return 0;
814
0
  unicode_nf_reset(&unc->nf);
815
0
  unc->nf_used = FALSE;
816
0
  return 1;
817
0
}
818
819
int unicode_nf_checker_input(struct unicode_nf_checker *unc, uint32_t cp,
820
           const struct unicode_code_point_data **_cp_data)
821
0
{
822
0
  const struct unicode_code_point_data *cpd_last = unc->cpd_last;
823
824
0
  if (*_cp_data == NULL)
825
0
    *_cp_data = unicode_code_point_get_data(cp);
826
827
0
  const struct unicode_code_point_data *cp_data = *_cp_data;
828
0
  bool last_starter = unc->last_starter;
829
830
0
  unc->cpd_last = cp_data;
831
0
  unc->last_starter = FALSE;
832
833
0
  if (cp_data->general_category == UNICODE_GENERAL_CATEGORY_INVALID)
834
0
    return -1;
835
0
  if ((cp_data->nf_quick_check & unc->nf_qc_mask) == unc->nf_qc_no)
836
0
    return 0;
837
0
  if (cpd_last != NULL && cp_data->canonical_combining_class != 0 &&
838
0
      cpd_last->canonical_combining_class >
839
0
    cp_data->canonical_combining_class)
840
0
    return 0;
841
0
  if ((cp_data->nf_quick_check & unc->nf_qc_mask) == unc->nf_qc_yes &&
842
0
      cp_data->canonical_combining_class == 0) {
843
    /* Nothing after this starter interacts with anything before
844
       it. Finish checking the previous code points, and remember
845
       this one in case the next code point needs to be checked
846
       with the normalization transform. */
847
0
    if (unicode_nf_checker_nf_finish(unc) == 0)
848
0
      return 0;
849
0
    unc->last_cp = cp;
850
0
    unc->last_starter = TRUE;
851
0
    return 1;
852
0
  }
853
854
0
  if (last_starter &&
855
0
      unicode_nf_checker_nf_input(unc, unc->last_cp, cpd_last) < 0)
856
0
    return 0;
857
0
  if (unicode_nf_checker_nf_input(unc, cp, cp_data) < 0)
858
0
    return 0;
859
0
  return 1;
860
0
}
861
862
int unicode_nf_checker_finish(struct unicode_nf_checker *unc)
863
0
{
864
0
  return unicode_nf_checker_nf_finish(unc);
865
0
}
866
867
/*
868
 * Casemap Transform
869
 */
870
871
0
#define UNICODE_GREEK_CAPITAL_LETTER_SIGMA 0x03a3
872
0
#define UNICODE_GREEK_SMALL_LETTER_FINAL_SIGMA 0x03c2
873
#define UNICODE_GREEK_SMALL_LETTER_SIGMA 0x03c3
874
875
static size_t
876
unicode_casemap_uppercase_cp(const struct unicode_code_point_data *cp_data,
877
           const uint32_t **map_r);
878
static size_t
879
unicode_casemap_lowercase_cp(const struct unicode_code_point_data *cp_data,
880
           const uint32_t **map_r);
881
static size_t
882
unicode_casemap_casefold_cp(const struct unicode_code_point_data *cp_data,
883
          const uint32_t **map_r);
884
885
static ssize_t
886
unicode_casemap_input(struct unicode_transform *trans,
887
          const struct unicode_transform_buffer *buf,
888
          const char **error_r);
889
static int
890
unicode_casemap_flush(struct unicode_transform *trans, bool finished,
891
          const char **error_r);
892
893
static const struct unicode_transform_def unicode_casemap_def = {
894
  .input = unicode_casemap_input,
895
  .flush = unicode_casemap_flush,
896
};
897
898
void unicode_casemap_init_uppercase(struct unicode_casemap *map_r)
899
0
{
900
  /* The buffers are written before they're read */
901
0
  memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer));
902
0
  unicode_transform_init(&map_r->transform, &unicode_casemap_def);
903
0
  map_r->map = unicode_casemap_uppercase_cp;
904
0
}
905
906
void unicode_casemap_init_lowercase(struct unicode_casemap *map_r)
907
0
{
908
  /* The buffers are written before they're read */
909
0
  memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer));
910
0
  unicode_transform_init(&map_r->transform, &unicode_casemap_def);
911
0
  map_r->map = unicode_casemap_lowercase_cp;
912
0
}
913
914
void unicode_casemap_init_lowercase_final_sigma(struct unicode_casemap *map_r)
915
0
{
916
0
  unicode_casemap_init_lowercase(map_r);
917
0
  map_r->final_sigma = TRUE;
918
0
}
919
920
void unicode_casemap_init_casefold(struct unicode_casemap *map_r)
921
0
{
922
  /* The buffers are written before they're read */
923
0
  memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer));
924
0
  unicode_transform_init(&map_r->transform, &unicode_casemap_def);
925
0
  map_r->map = unicode_casemap_casefold_cp;
926
0
}
927
928
static size_t
929
unicode_casemap_uppercase_cp(const struct unicode_code_point_data *cp_data,
930
           const uint32_t **map_r)
931
0
{
932
0
  return unicode_code_point_data_get_uppercase_mapping(cp_data, map_r);
933
0
}
934
935
static size_t
936
unicode_casemap_lowercase_cp(const struct unicode_code_point_data *cp_data,
937
           const uint32_t **map_r)
938
0
{
939
0
  return unicode_code_point_data_get_lowercase_mapping(cp_data, map_r);
940
0
}
941
942
static size_t
943
unicode_casemap_casefold_cp(const struct unicode_code_point_data *cp_data,
944
          const uint32_t **map_r)
945
0
{
946
0
  return unicode_code_point_data_get_casefold_mapping(cp_data, map_r);
947
0
}
948
949
static void
950
unicode_casemap_sigma_decide(struct unicode_casemap *map,
951
           bool followed_by_cased)
952
0
{
953
0
  i_assert(map->sigma_pending);
954
0
  i_assert(map->cp_buffer[map->sigma_pos] ==
955
0
     UNICODE_GREEK_SMALL_LETTER_SIGMA);
956
957
0
  if (!followed_by_cased) {
958
0
    map->cp_buffer[map->sigma_pos] =
959
0
      UNICODE_GREEK_SMALL_LETTER_FINAL_SIGMA;
960
0
  }
961
0
  map->sigma_pending = FALSE;
962
0
}
963
964
static bool
965
unicode_casemap_input_cp(struct unicode_casemap *map, uint32_t cp,
966
       const struct unicode_code_point_data *cp_data)
967
0
{
968
0
  unsigned int pos = map->buffer_len;
969
0
  const uint32_t *map_cps;
970
0
  size_t i, map_cps_len;
971
972
0
  if (cp_data == NULL)
973
0
    cp_data = unicode_code_point_get_data(cp);
974
975
0
  if (map->sigma_pending && !cp_data->pb_c_case_ignorable) {
976
    /* This code point shows whether a cased letter follows the
977
       sigma */
978
0
    unicode_casemap_sigma_decide(map, cp_data->pb_c_cased);
979
0
  }
980
981
0
  map_cps_len = map->map(cp_data, &map_cps);
982
0
  if (map->buffer_len + I_MAX(map_cps_len, 1) >
983
0
      UNICODE_CASEMAP_BUFFER_SIZE) {
984
0
    if (map->sigma_pending && map->sigma_pos == 0) {
985
      /* Too many case-ignorable code points follow the
986
         sigma */
987
0
      unicode_casemap_sigma_decide(map, FALSE);
988
0
    }
989
0
    return FALSE;
990
0
  }
991
992
0
  if (map_cps_len == 0) {
993
0
    map->cp_buffer[map->buffer_len] = cp;
994
0
    map->cpd_buffer[map->buffer_len++] = cp_data;
995
0
  } else {
996
0
    for (i = 0; i < map_cps_len; i++) {
997
0
      map->cp_buffer[map->buffer_len] = map_cps[i];
998
0
      map->cpd_buffer[map->buffer_len++] = NULL;
999
0
    }
1000
0
  }
1001
1002
0
  if (map->final_sigma && !cp_data->pb_c_case_ignorable) {
1003
0
    if (cp == UNICODE_GREEK_CAPITAL_LETTER_SIGMA &&
1004
0
        map->preceded_by_cased) {
1005
      /* Keep the sigma until it's known whether a cased
1006
         letter follows it */
1007
0
      map->sigma_pending = TRUE;
1008
0
      map->sigma_pos = pos;
1009
0
    }
1010
0
    map->preceded_by_cased = cp_data->pb_c_cased;
1011
0
  }
1012
0
  return TRUE;
1013
0
}
1014
1015
static ssize_t
1016
unicode_casemap_forward(struct unicode_casemap *map, const char **error_r)
1017
0
{
1018
0
  unsigned int count = (map->sigma_pending ?
1019
0
            map->sigma_pos : map->buffer_len);
1020
0
  ssize_t sret;
1021
1022
0
  if (count == 0)
1023
0
    return 0;
1024
1025
0
  sret = uniform_transform_forward(&map->transform, map->cp_buffer,
1026
0
           map->cpd_buffer, count, error_r);
1027
0
  if (sret < 0)
1028
0
    return -1;
1029
1030
0
  map->buffer_len -= sret;
1031
0
  memmove(map->cp_buffer, &map->cp_buffer[sret],
1032
0
    map->buffer_len * sizeof(map->cp_buffer[0]));
1033
0
  memmove(map->cpd_buffer, &map->cpd_buffer[sret],
1034
0
    map->buffer_len * sizeof(map->cpd_buffer[0]));
1035
0
  if (map->sigma_pending)
1036
0
    map->sigma_pos -= sret;
1037
0
  return sret;
1038
0
}
1039
1040
static ssize_t
1041
unicode_casemap_input(struct unicode_transform *trans,
1042
          const struct unicode_transform_buffer *buf,
1043
          const char **error_r)
1044
0
{
1045
0
  struct unicode_casemap *map =
1046
0
    container_of(trans, struct unicode_casemap, transform);
1047
0
  ssize_t sret;
1048
0
  size_t n = 0;
1049
1050
0
  while (n < buf->cp_count) {
1051
0
    if (unicode_casemap_input_cp(map, buf->cp[n],
1052
0
               (buf->cp_data == NULL ?
1053
0
                NULL : buf->cp_data[n]))) {
1054
0
      n++;
1055
0
      continue;
1056
0
    }
1057
1058
    /* The buffer is full */
1059
0
    sret = unicode_casemap_forward(map, error_r);
1060
0
    if (sret < 0)
1061
0
      return -1;
1062
0
    if (sret == 0) {
1063
      /* The next transform is full */
1064
0
      break;
1065
0
    }
1066
0
  }
1067
1068
0
  if (unicode_casemap_forward(map, error_r) < 0)
1069
0
    return -1;
1070
0
  return n;
1071
0
}
1072
1073
static int
1074
unicode_casemap_flush(struct unicode_transform *trans, bool finished,
1075
          const char **error_r)
1076
0
{
1077
0
  struct unicode_casemap *map =
1078
0
    container_of(trans, struct unicode_casemap, transform);
1079
1080
0
  if (finished && map->sigma_pending) {
1081
    /* No more input, so no cased letter follows the sigma */
1082
0
    unicode_casemap_sigma_decide(map, FALSE);
1083
0
  }
1084
1085
0
  if (unicode_casemap_forward(map, error_r) < 0)
1086
0
    return -1;
1087
0
  if (map->buffer_len > 0)
1088
0
    return 0;
1089
0
  if (finished) {
1090
    /* The string ended */
1091
0
    map->preceded_by_cased = FALSE;
1092
0
  }
1093
0
  return 1;
1094
0
}
1095
1096
/*
1097
 * RFC 5051 - Simple Unicode Collation Algorithm
1098
 */
1099
1100
void unicode_rfc5051_init(struct unicode_rfc5051_context *ctx)
1101
0
{
1102
0
  i_zero(ctx);
1103
0
}
1104
1105
size_t unicode_rfc5051_normalize(struct unicode_rfc5051_context *ctx,
1106
         uint32_t cp, const uint32_t **norm_r)
1107
0
{
1108
0
  const struct unicode_code_point_data *cpd;
1109
0
  size_t len;
1110
1111
0
  cpd = unicode_code_point_get_data(cp);
1112
0
  if (cpd->simple_titlecase_mapping != 0x0000)
1113
0
    cp = cpd->simple_titlecase_mapping;
1114
1115
0
  if (cp >= HANGUL_FIRST && cp <= HANGUL_LAST) {
1116
0
    *norm_r = ctx->buffer;
1117
0
    return unicode_hangul_decompose(cp, ctx->buffer);
1118
0
  }
1119
1120
0
  len = unicode_code_point_get_full_decomposition(cp, FALSE, norm_r);
1121
0
  if (len == 0) {
1122
0
    ctx->buffer[0] = cp;
1123
0
    *norm_r = ctx->buffer;
1124
0
    return 1;
1125
0
  }
1126
0
  return len;
1127
0
}