Coverage Report

Created: 2023-06-07 06:10

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