Coverage Report

Created: 2025-11-07 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/libcommon/topic_common.c
Line
Count
Source
1
/*
2
Copyright (c) 2009-2021 Roger Light <roger@atchoo.org>
3
4
All rights reserved. This program and the accompanying materials
5
are made available under the terms of the Eclipse Public License 2.0
6
and Eclipse Distribution License v1.0 which accompany this distribution.
7
8
The Eclipse Public License is available at
9
   https://www.eclipse.org/legal/epl-2.0/
10
and the Eclipse Distribution License is available at
11
  http://www.eclipse.org/org/documents/edl-v10.php.
12
13
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#include <string.h>
22
23
#ifdef WIN32
24
#  include <winsock2.h>
25
#  include <aclapi.h>
26
#  include <io.h>
27
#  include <lmcons.h>
28
#else
29
#  include <sys/stat.h>
30
#endif
31
32
#include "mosquitto.h"
33
34
35
/* Check that a topic used for publishing is valid.
36
 * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.
37
 * Also returns MOSQ_ERR_INVAL if the topic string is too long.
38
 * Returns MOSQ_ERR_SUCCESS if everything is fine.
39
 */
40
BROKER_EXPORT int mosquitto_pub_topic_check(const char *str)
41
2.16k
{
42
2.16k
  int len = 0;
43
2.16k
  int hier_count = 0;
44
45
2.16k
  if(str == NULL){
46
63
    return MOSQ_ERR_INVAL;
47
63
  }
48
49
3.72M
  while(str && str[0]){
50
3.72M
    if(str[0] == '+' || str[0] == '#'){
51
39
      return MOSQ_ERR_INVAL;
52
3.72M
    }else if(str[0] == '/'){
53
126k
      hier_count++;
54
126k
    }
55
3.72M
    len++;
56
3.72M
    str = &str[1];
57
3.72M
  }
58
2.06k
  if(len == 0 || len > 65535){
59
0
    return MOSQ_ERR_INVAL;
60
0
  }
61
2.06k
  if(hier_count > TOPIC_HIERARCHY_LIMIT){
62
34
    return MOSQ_ERR_INVAL;
63
34
  }
64
65
2.02k
  return MOSQ_ERR_SUCCESS;
66
2.06k
}
67
68
69
BROKER_EXPORT int mosquitto_pub_topic_check2(const char *str, size_t len)
70
565
{
71
565
  size_t i;
72
565
  int hier_count = 0;
73
74
565
  if(str == NULL || len == 0 || len > 65535){
75
24
    return MOSQ_ERR_INVAL;
76
24
  }
77
78
2.05M
  for(i=0; i<len; i++){
79
2.05M
    if(str[i] == '+' || str[i] == '#'){
80
28
      return MOSQ_ERR_INVAL;
81
2.05M
    }else if(str[i] == '/'){
82
1.32M
      hier_count++;
83
1.32M
    }
84
2.05M
  }
85
513
  if(hier_count > TOPIC_HIERARCHY_LIMIT){
86
36
    return MOSQ_ERR_INVAL;
87
36
  }
88
89
477
  return MOSQ_ERR_SUCCESS;
90
513
}
91
92
93
/* Check that a topic used for subscriptions is valid.
94
 * Search for + or # in a topic, check they aren't in invalid positions such as
95
 * foo/#/bar, foo/+bar or foo/bar#.
96
 * Return MOSQ_ERR_INVAL if invalid position found.
97
 * Also returns MOSQ_ERR_INVAL if the topic string is too long.
98
 * Returns MOSQ_ERR_SUCCESS if everything is fine.
99
 */
100
BROKER_EXPORT int mosquitto_sub_topic_check(const char *str)
101
7.45M
{
102
7.45M
  char c = '\0';
103
7.45M
  int len = 0;
104
7.45M
  int hier_count = 0;
105
106
7.45M
  if(str == NULL){
107
0
    return MOSQ_ERR_INVAL;
108
0
  }
109
110
26.3M
  while(str[0]){
111
18.8M
    if(str[0] == '+'){
112
734k
      if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){
113
83
        return MOSQ_ERR_INVAL;
114
83
      }
115
18.1M
    }else if(str[0] == '#'){
116
985k
      if((c != '\0' && c != '/')  || str[1] != '\0'){
117
68
        return MOSQ_ERR_INVAL;
118
68
      }
119
17.1M
    }else if(str[0] == '/'){
120
5.34M
      hier_count++;
121
5.34M
    }
122
18.8M
    len++;
123
18.8M
    c = str[0];
124
18.8M
    str = &str[1];
125
18.8M
  }
126
7.45M
  if(len == 0 || len > 65535){
127
14
    return MOSQ_ERR_INVAL;
128
14
  }
129
7.45M
  if(hier_count > TOPIC_HIERARCHY_LIMIT){
130
69
    return MOSQ_ERR_INVAL;
131
69
  }
132
133
7.45M
  return MOSQ_ERR_SUCCESS;
134
7.45M
}
135
136
137
BROKER_EXPORT int mosquitto_sub_topic_check2(const char *str, size_t len)
138
189
{
139
189
  char c = '\0';
140
189
  size_t i;
141
189
  int hier_count = 0;
142
143
189
  if(str == NULL || len == 0 || len > 65535){
144
12
    return MOSQ_ERR_INVAL;
145
12
  }
146
147
944k
  for(i=0; i<len; i++){
148
944k
    if(str[i] == '+'){
149
10.0k
      if((c != '\0' && c != '/') || (i<len-1 && str[i+1] != '/')){
150
39
        return MOSQ_ERR_INVAL;
151
39
      }
152
934k
    }else if(str[i] == '#'){
153
38
      if((c != '\0' && c != '/')  || i<len-1){
154
36
        return MOSQ_ERR_INVAL;
155
36
      }
156
934k
    }else if(str[i] == '/'){
157
652k
      hier_count++;
158
652k
    }
159
944k
    c = str[i];
160
944k
  }
161
102
  if(hier_count > TOPIC_HIERARCHY_LIMIT){
162
22
    return MOSQ_ERR_INVAL;
163
22
  }
164
165
80
  return MOSQ_ERR_SUCCESS;
166
102
}
167
168
169
static int topic_matches_sub(const char *sub, const char *topic, const char *clientid, const char *username, bool match_patterns, bool *result)
170
0
{
171
0
  size_t spos;
172
0
  const char *pattern_check;
173
0
  const char *lastchar = NULL;
174
175
0
  if(!result){
176
0
    return MOSQ_ERR_INVAL;
177
0
  }
178
0
  *result = false;
179
180
0
  if(!sub || !topic || sub[0] == 0 || topic[0] == 0){
181
0
    return MOSQ_ERR_INVAL;
182
0
  }
183
184
0
  if((sub[0] == '$' && topic[0] != '$')
185
0
      || (topic[0] == '$' && sub[0] != '$')){
186
187
0
    return MOSQ_ERR_SUCCESS;
188
0
  }
189
190
0
  spos = 0;
191
192
0
  while(sub[0] != 0){
193
0
    if(topic[0] == '+' || topic[0] == '#'){
194
0
      return MOSQ_ERR_INVAL;
195
0
    }
196
0
    if(match_patterns &&
197
0
        (lastchar == NULL || lastchar[0] == '/') &&
198
0
        sub[0] == '%' &&
199
0
        (sub[1] == 'c' || sub[1] == 'u') &&
200
0
        (sub[2] == '/' || sub[2] == '\0')
201
0
        ){
202
203
0
      if(sub[1] == 'c'){
204
0
        pattern_check = clientid;
205
0
      }else{
206
0
        pattern_check = username;
207
0
      }
208
0
      if(pattern_check == NULL || pattern_check[0] == '\0'){
209
0
        return MOSQ_ERR_SUCCESS;
210
0
      }
211
0
      spos += 2;
212
0
      sub += 2;
213
214
0
      while(pattern_check[0] != 0 && topic[0] != 0 && topic[0] != '/'){
215
0
        if(pattern_check[0] != topic[0]){
216
          /* Valid input, but no match */
217
0
          return MOSQ_ERR_SUCCESS;
218
0
        }
219
0
        pattern_check++;
220
0
        topic++;
221
0
      }
222
0
      if(pattern_check[0] != '\0'){
223
        /* substituted pattern part smaller than publish topic part, so fail */
224
0
        return MOSQ_ERR_SUCCESS;
225
0
      }
226
0
      if((sub[0] == '\0' && topic[0] == '\0') ||
227
0
          (sub[0] == '/' && sub[1] == '#' && sub[2] == '\0' && topic[0] == '\0')
228
0
          ){
229
230
0
        *result = true;
231
0
        return MOSQ_ERR_SUCCESS;
232
0
      }
233
0
    }
234
0
    if(sub[0] != topic[0] || topic[0] == 0){ /* Check for wildcard matches */
235
0
      if(sub[0] == '+'){
236
        /* Check for bad "+foo" or "a/+foo" subscription */
237
0
        if(spos > 0 && sub[-1] != '/'){
238
0
          return MOSQ_ERR_INVAL;
239
0
        }
240
        /* Check for bad "foo+" or "foo+/a" subscription */
241
0
        if(sub[1] != 0 && sub[1] != '/'){
242
0
          return MOSQ_ERR_INVAL;
243
0
        }
244
0
        spos++;
245
0
        sub++;
246
0
        while(topic[0] != 0 && topic[0] != '/'){
247
0
          if(topic[0] == '+' || topic[0] == '#'){
248
0
            return MOSQ_ERR_INVAL;
249
0
          }
250
0
          topic++;
251
0
        }
252
0
        if(topic[0] == 0 && sub[0] == 0){
253
0
          *result = true;
254
0
          return MOSQ_ERR_SUCCESS;
255
0
        }
256
0
      }else if(sub[0] == '#'){
257
        /* Check for bad "foo#" subscription */
258
0
        if(spos > 0 && sub[-1] != '/'){
259
0
          return MOSQ_ERR_INVAL;
260
0
        }
261
        /* Check for # not the final character of the sub, e.g. "#foo" */
262
0
        if(sub[1] != 0){
263
0
          return MOSQ_ERR_INVAL;
264
0
        }else{
265
0
          while(topic[0] != 0){
266
0
            if(topic[0] == '+' || topic[0] == '#'){
267
0
              return MOSQ_ERR_INVAL;
268
0
            }
269
0
            topic++;
270
0
          }
271
0
          *result = true;
272
0
          return MOSQ_ERR_SUCCESS;
273
0
        }
274
0
      }else{
275
        /* Check for e.g. foo/bar matching foo/+/# */
276
0
        if(topic[0] == 0
277
0
            && spos > 0
278
0
            && sub[-1] == '+'
279
0
            && sub[0] == '/'
280
0
            && sub[1] == '#'){
281
0
          *result = true;
282
0
          return MOSQ_ERR_SUCCESS;
283
0
        }
284
285
        /* There is no match at this point, but is the sub invalid? */
286
0
        while(sub[0] != 0){
287
0
          if(sub[0] == '#' && sub[1] != 0){
288
0
            return MOSQ_ERR_INVAL;
289
0
          }
290
0
          spos++;
291
0
          sub++;
292
0
        }
293
294
        /* Valid input, but no match */
295
0
        return MOSQ_ERR_SUCCESS;
296
0
      }
297
0
    }else{
298
      /* sub[spos] == topic[tpos] */
299
0
      if(topic[1] == 0){
300
        /* Check for e.g. foo matching foo/# */
301
0
        if(sub[1] == '/'
302
0
            && sub[2] == '#'
303
0
            && sub[3] == 0){
304
0
          *result = true;
305
0
          return MOSQ_ERR_SUCCESS;
306
0
        }
307
0
      }
308
0
      spos++;
309
0
      sub++;
310
0
      topic++;
311
0
      if(sub[0] == 0 && topic[0] == 0){
312
0
        *result = true;
313
0
        return MOSQ_ERR_SUCCESS;
314
0
      }else if(topic[0] == 0 && sub[0] == '+' && sub[1] == 0){
315
0
        if(spos > 0 && sub[-1] != '/'){
316
0
          return MOSQ_ERR_INVAL;
317
0
        }
318
0
        spos++;
319
0
        sub++;
320
0
        *result = true;
321
0
        return MOSQ_ERR_SUCCESS;
322
0
      }
323
0
    }
324
0
    lastchar = sub-1;
325
0
  }
326
0
  if((topic[0] != 0 || sub[0] != 0)){
327
0
    *result = false;
328
0
  }
329
0
  while(topic[0] != 0){
330
0
    if(topic[0] == '+' || topic[0] == '#'){
331
0
      return MOSQ_ERR_INVAL;
332
0
    }
333
0
    topic++;
334
0
  }
335
336
0
  return MOSQ_ERR_SUCCESS;
337
0
}
338
339
340
static int sub_matches_acl(const char *acl, const char *sub, const char *clientid, const char *username, bool match_patterns, bool *result)
341
0
{
342
0
  size_t apos;
343
0
  const char *pattern_check;
344
0
  const char *lastchar = NULL;
345
346
0
  *result = false;
347
348
0
  if(!acl || !sub || acl[0] == 0 || sub[0] == 0){
349
0
    return MOSQ_ERR_INVAL;
350
0
  }
351
352
0
  if((acl[0] == '$' && sub[0] != '$')
353
0
      || (sub[0] == '$' && acl[0] != '$')){
354
355
0
    return MOSQ_ERR_SUCCESS;
356
0
  }
357
358
0
  apos = 0;
359
360
0
  while(acl[0] != 0){
361
0
    if(match_patterns &&
362
0
        (lastchar == NULL || lastchar[0] == '/') &&
363
0
        acl[0] == '%' &&
364
0
        (acl[1] == 'c' || acl[1] == 'u') &&
365
0
        (acl[2] == '/' || acl[2] == '\0')
366
0
        ){
367
368
0
      if(acl[1] == 'c'){
369
0
        pattern_check = clientid;
370
0
      }else{
371
0
        pattern_check = username;
372
0
      }
373
0
      if(pattern_check == NULL || pattern_check[0] == '\0'){
374
        /* no match */
375
0
        return MOSQ_ERR_SUCCESS;
376
0
      }
377
0
      if(pattern_check[1] == '\0' &&
378
0
          (
379
0
            pattern_check[0] == '+' ||
380
0
            pattern_check[0] == '#' ||
381
0
            pattern_check[0] == '/')
382
0
          ){
383
384
        /* username/client id of just + / # not allowed */
385
0
        return MOSQ_ERR_SUCCESS;
386
0
      }
387
388
0
      apos +=2;
389
0
      acl += 2;
390
391
0
      while(pattern_check[0] != 0 && sub[0] != 0 && sub[0] != '/'){
392
0
        if(pattern_check[0] != sub[0]){
393
          /* Valid input, but no match */
394
0
          return MOSQ_ERR_SUCCESS;
395
0
        }
396
0
        pattern_check++;
397
0
        sub++;
398
0
      }
399
0
      if(pattern_check[0] != '\0'){
400
        /* substituted pattern part smaller than publish topic part, so fail */
401
0
        return MOSQ_ERR_SUCCESS;
402
0
      }
403
0
      if(sub[0] == '\0' && (acl[0] == '\0' ||
404
0
          (acl[0] == '/' && acl[1] == '#' && acl[2] == '\0'))
405
0
          ){
406
407
0
        *result = true;
408
0
        return MOSQ_ERR_SUCCESS;
409
0
      }
410
0
    }
411
0
    if(acl[0] != sub[0] || sub[0] == 0){ /* Check for wildcard matches */
412
0
      if(acl[0] == '+'){
413
0
        if(sub[0] == '#'){
414
          /* + doesn't match # */
415
0
          return MOSQ_ERR_SUCCESS;
416
0
        }
417
        /* Check for bad "+foo" or "a/+foo" subscription */
418
0
        if(apos > 0 && acl[-1] != '/'){
419
0
          return MOSQ_ERR_INVAL;
420
0
        }
421
        /* Check for bad "foo+" or "foo+/a" subscription */
422
0
        if(acl[1] != 0 && acl[1] != '/'){
423
0
          return MOSQ_ERR_INVAL;
424
0
        }
425
0
        apos++;
426
0
        acl++;
427
0
        while(sub[0] != 0 && sub[0] != '/'){
428
0
          sub++;
429
0
        }
430
0
        if(sub[0] == 0 && acl[0] == 0){
431
0
          *result = true;
432
0
          return MOSQ_ERR_SUCCESS;
433
0
        }
434
0
      }else if(acl[0] == '#'){
435
        /* Check for bad "foo#" subscription */
436
0
        if(apos > 0 && acl[-1] != '/'){
437
0
          return MOSQ_ERR_INVAL;
438
0
        }
439
        /* Check for # not the final character of the sub, e.g. "#foo" */
440
0
        if(acl[1] != 0){
441
0
          return MOSQ_ERR_INVAL;
442
0
        }else{
443
0
          *result = true;
444
0
          return MOSQ_ERR_SUCCESS;
445
0
        }
446
0
      }else{
447
        /* Check for e.g. foo/bar matching foo/+/# */
448
0
        if(sub[0] == 0
449
0
            && apos > 0
450
0
            && acl[-1] == '+'
451
0
            && acl[0] == '/'
452
0
            && acl[1] == '#'){
453
0
          *result = true;
454
0
          return MOSQ_ERR_SUCCESS;
455
0
        }
456
457
        /* There is no match at this point, but is the sub invalid? */
458
0
        while(acl[0] != 0){
459
0
          if(acl[0] == '#' && acl[1] != 0){
460
0
            return MOSQ_ERR_INVAL;
461
0
          }
462
0
          apos++;
463
0
          acl++;
464
0
        }
465
466
        /* Valid input, but no match */
467
0
        return MOSQ_ERR_SUCCESS;
468
0
      }
469
0
    }else{
470
      /* acl[apos] == sub[spos] */
471
0
      if(sub[1] == 0){
472
        /* Check for e.g. foo matching foo/# */
473
0
        if(acl[1] == '/'
474
0
            && acl[2] == '#'
475
0
            && acl[3] == 0){
476
477
0
          *result = true;
478
0
          return MOSQ_ERR_SUCCESS;
479
0
        }
480
0
      }
481
0
      apos++;
482
0
      acl++;
483
0
      sub++;
484
0
      if(acl[0] == 0 && sub[0] == 0){
485
0
        *result = true;
486
0
        return MOSQ_ERR_SUCCESS;
487
0
      }else if(sub[0] == 0 && acl[0] == '+' && acl[1] == 0){
488
0
        if(apos > 0 && acl[-1] != '/'){
489
0
          return MOSQ_ERR_INVAL;
490
0
        }
491
0
        apos++;
492
0
        acl++;
493
0
        *result = true;
494
0
        return MOSQ_ERR_SUCCESS;
495
0
      }
496
0
    }
497
0
    lastchar = acl-1;
498
0
  }
499
0
  if((sub[0] != 0 || acl[0] != 0)){
500
0
    return MOSQ_ERR_SUCCESS;
501
0
  }
502
0
  while(sub[0] != 0){
503
0
    if(sub[0] == '+' || sub[0] == '#'){
504
0
      return MOSQ_ERR_SUCCESS;
505
0
    }
506
0
    sub++;
507
0
  }
508
509
0
  *result = true;
510
0
  return MOSQ_ERR_SUCCESS;
511
0
}
512
513
514
BROKER_EXPORT int mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result)
515
0
{
516
0
  return sub_matches_acl(acl, sub, NULL, NULL, false, result);
517
0
}
518
519
520
BROKER_EXPORT int mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result)
521
0
{
522
0
  return sub_matches_acl(acl, sub, clientid, username, true, result);
523
0
}
524
525
526
BROKER_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)
527
0
{
528
0
  return topic_matches_sub(sub, topic, NULL, NULL, false, result);
529
0
}
530
531
532
BROKER_EXPORT int mosquitto_topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result)
533
0
{
534
0
  return topic_matches_sub(sub, topic, clientid, username, true, result);
535
0
}
536
537
538
/* Does a topic match a subscription? */
539
BROKER_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result)
540
0
{
541
0
  size_t spos, tpos;
542
543
0
  if(!result){
544
0
    return MOSQ_ERR_INVAL;
545
0
  }
546
0
  *result = false;
547
548
0
  if(!sub || !topic || !sublen || !topiclen){
549
0
    return MOSQ_ERR_INVAL;
550
0
  }
551
552
0
  if((sub[0] == '$' && topic[0] != '$')
553
0
      || (topic[0] == '$' && sub[0] != '$')){
554
555
0
    return MOSQ_ERR_SUCCESS;
556
0
  }
557
558
0
  spos = 0;
559
0
  tpos = 0;
560
561
0
  while(spos < sublen){
562
0
    if(tpos < topiclen && (topic[tpos] == '+' || topic[tpos] == '#')){
563
0
      return MOSQ_ERR_INVAL;
564
0
    }
565
0
    if(tpos == topiclen || sub[spos] != topic[tpos]){
566
0
      if(sub[spos] == '+'){
567
        /* Check for bad "+foo" or "a/+foo" subscription */
568
0
        if(spos > 0 && sub[spos-1] != '/'){
569
0
          return MOSQ_ERR_INVAL;
570
0
        }
571
        /* Check for bad "foo+" or "foo+/a" subscription */
572
0
        if(spos+1 < sublen && sub[spos+1] != '/'){
573
0
          return MOSQ_ERR_INVAL;
574
0
        }
575
0
        spos++;
576
0
        while(tpos < topiclen && topic[tpos] != '/'){
577
0
          if(topic[tpos] == '+' || topic[tpos] == '#'){
578
0
            return MOSQ_ERR_INVAL;
579
0
          }
580
0
          tpos++;
581
0
        }
582
0
        if(tpos == topiclen && spos == sublen){
583
0
          *result = true;
584
0
          return MOSQ_ERR_SUCCESS;
585
0
        }
586
0
      }else if(sub[spos] == '#'){
587
        /* Check for bad "foo#" subscription */
588
0
        if(spos > 0 && sub[spos-1] != '/'){
589
0
          return MOSQ_ERR_INVAL;
590
0
        }
591
        /* Check for # not the final character of the sub, e.g. "#foo" */
592
0
        if(spos+1 < sublen){
593
0
          return MOSQ_ERR_INVAL;
594
0
        }else{
595
0
          while(tpos < topiclen){
596
0
            if(topic[tpos] == '+' || topic[tpos] == '#'){
597
0
              return MOSQ_ERR_INVAL;
598
0
            }
599
0
            tpos++;
600
0
          }
601
0
          *result = true;
602
0
          return MOSQ_ERR_SUCCESS;
603
0
        }
604
0
      }else{
605
        /* Check for e.g. foo/bar matching foo/+/# */
606
0
        if(tpos == topiclen
607
0
            && spos > 0
608
0
            && sub[spos-1] == '+'
609
0
            && sub[spos] == '/'
610
0
            && spos+1 < sublen
611
0
            && sub[spos+1] == '#'){
612
0
          *result = true;
613
0
          return MOSQ_ERR_SUCCESS;
614
0
        }
615
616
        /* There is no match at this point, but is the sub invalid? */
617
0
        while(spos < sublen){
618
0
          if(sub[spos] == '#' && spos+1 < sublen){
619
0
            return MOSQ_ERR_INVAL;
620
0
          }
621
0
          spos++;
622
0
        }
623
624
        /* Valid input, but no match */
625
0
        return MOSQ_ERR_SUCCESS;
626
0
      }
627
0
    }else{
628
      /* sub[spos] == topic[tpos] */
629
0
      if(tpos+1 == topiclen){
630
        /* Check for e.g. foo matching foo/# */
631
0
        if(spos+3 == sublen
632
0
            && sub[spos+1] == '/'
633
0
            && sub[spos+2] == '#'){
634
0
          *result = true;
635
0
          return MOSQ_ERR_SUCCESS;
636
0
        }
637
0
      }
638
0
      spos++;
639
0
      tpos++;
640
0
      if(spos == sublen && tpos == topiclen){
641
0
        *result = true;
642
0
        return MOSQ_ERR_SUCCESS;
643
0
      }else if(tpos == topiclen && sub[spos] == '+' && spos+1 == sublen){
644
0
        if(spos > 0 && sub[spos-1] != '/'){
645
0
          return MOSQ_ERR_INVAL;
646
0
        }
647
0
        spos++;
648
0
        *result = true;
649
0
        return MOSQ_ERR_SUCCESS;
650
0
      }
651
0
    }
652
0
  }
653
0
  if(tpos < topiclen || spos < sublen){
654
0
    *result = false;
655
0
  }
656
0
  while(tpos < topiclen){
657
0
    if(topic[tpos] == '+' || topic[tpos] == '#'){
658
0
      return MOSQ_ERR_INVAL;
659
0
    }
660
0
    tpos++;
661
0
  }
662
663
0
  return MOSQ_ERR_SUCCESS;
664
0
}
665
666
667
int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count)
668
162
{
669
162
  size_t len;
670
162
  size_t hier_count = 1;
671
162
  size_t start, stop;
672
162
  size_t hier;
673
162
  size_t tlen;
674
162
  size_t i, j;
675
676
162
  if(!subtopic || !topics || !count){
677
0
    return MOSQ_ERR_INVAL;
678
0
  }
679
680
162
  len = strlen(subtopic);
681
682
13.2M
  for(i=0; i<len; i++){
683
13.2M
    if(subtopic[i] == '/'){
684
11.4M
      if(i > len-1){
685
        /* Separator at end of line */
686
11.4M
      }else{
687
11.4M
        hier_count++;
688
11.4M
      }
689
11.4M
    }
690
13.2M
  }
691
692
162
  (*topics) = mosquitto_calloc(hier_count, sizeof(char *));
693
162
  if(!(*topics)){
694
0
    return MOSQ_ERR_NOMEM;
695
0
  }
696
697
162
  start = 0;
698
162
  hier = 0;
699
700
13.2M
  for(i=0; i<len+1; i++){
701
13.2M
    if(subtopic[i] == '/' || subtopic[i] == '\0'){
702
11.4M
      stop = i;
703
11.4M
      if(start != stop){
704
776k
        tlen = stop-start + 1;
705
776k
        (*topics)[hier] = mosquitto_calloc(tlen, sizeof(char));
706
776k
        if(!(*topics)[hier]){
707
0
          for(j=0; j<hier; j++){
708
0
            mosquitto_FREE((*topics)[j]);
709
0
          }
710
0
          mosquitto_FREE((*topics));
711
0
          return MOSQ_ERR_NOMEM;
712
0
        }
713
2.63M
        for(j=start; j<stop; j++){
714
1.85M
          (*topics)[hier][j-start] = subtopic[j];
715
1.85M
        }
716
776k
      }
717
11.4M
      start = i+1;
718
11.4M
      hier++;
719
11.4M
    }
720
13.2M
  }
721
722
162
  *count = (int)hier_count;
723
724
162
  return MOSQ_ERR_SUCCESS;
725
162
}
726
727
728
int mosquitto_sub_topic_tokens_free(char ***topics, int count)
729
162
{
730
162
  int i;
731
732
162
  if(!topics || !(*topics) || count<1){
733
0
    return MOSQ_ERR_INVAL;
734
0
  }
735
736
11.4M
  for(i=0; i<count; i++){
737
11.4M
    mosquitto_FREE((*topics)[i]);
738
11.4M
  }
739
162
  mosquitto_FREE(*topics);
740
741
162
  return MOSQ_ERR_SUCCESS;
742
162
}