Coverage Report

Created: 2025-06-20 06:45

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