UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2018 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <ctype.h> 33 34 #include <cx/string.h> 35 #include <cx/utils.h> 36 #include <cx/printf.h> 37 #include <cx/hash_map.h> 38 39 #include <libidav/crypto.h> 40 41 #include "libxattr.h" 42 43 #include "tags.h" 44 45 #ifdef __APPLE__ 46 #include <CoreFoundation/CoreFoundation.h> 47 #endif 48 49 void free_dav_tag(DavTag* tag) { 50 free(tag->name); 51 if(tag->color) { 52 free(tag->color); 53 } 54 free(tag); 55 } 56 57 void free_taglist(CxList *list) { 58 if(!list) { 59 return; 60 } 61 cxListDestroy(list); 62 } 63 64 int compare_tagname(DavTag* left, DavTag* right, void* ignorecase) { 65 cxstring leftname = cx_str(left->name); 66 cxstring rightname = cx_str(right->name); 67 if (ignorecase && *((int*) ignorecase)) { 68 return cx_strcasecmp(leftname, rightname); 69 } else { 70 return cx_strcmp(leftname, rightname); 71 } 72 } 73 74 CxList* parse_text_taglist(const char *buf, size_t length) { 75 CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); 76 cxDefineDestructor(tags, free_dav_tag); 77 78 int line_start = 0; 79 for(int i=0;i<length;i++) { 80 if(buf[i] == '\n' || i == length-1) { 81 cxstring line = cx_strtrim(cx_strn((char*)buf + line_start, i - line_start)); 82 if(line.length > 0) { 83 DavTag *tag = calloc(1, sizeof(DavTag)); 84 cxstring color = cx_strchr(line, '#'); 85 if(color.length>0) { 86 cxstring name = line; 87 name.length = (int)(color.ptr-line.ptr); 88 if(name.length != 0) { 89 tag->name = cx_strdup(name).ptr; 90 color.ptr++; 91 color.length--; 92 if(color.length > 0) { 93 tag->color = cx_strdup(color).ptr; 94 } 95 } else { 96 free(tag); 97 } 98 } else { 99 tag->name = cx_strdup(line).ptr; 100 tag->color = NULL; 101 } 102 103 cxListAdd(tags, tag); 104 } 105 line_start = i+1; 106 } 107 } 108 109 return tags; 110 } 111 112 CxMap* taglist2map(CxList *tags) { 113 if(!tags) { 114 return cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); 115 } 116 117 CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, cxListSize(tags) + 8); 118 CxIterator iter = cxListIterator(tags); 119 cx_foreach(DavTag*, t, iter) { 120 cxMapPut(map, cx_hash_key_str(t->name), t); 121 } 122 return map; 123 } 124 125 CxBuffer* create_text_taglist(CxList *tags) { 126 if(!tags) { 127 return NULL; 128 } 129 130 CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 131 CxIterator i = cxListIterator(tags); 132 cx_foreach(DavTag *, tag, i) { 133 if(tag->color) { 134 cx_bprintf(buf, "%s#%s\n", tag->name, tag->color); 135 } else { 136 cx_bprintf(buf, "%s\n", tag->name); 137 } 138 } 139 return buf; 140 } 141 142 143 CxList* parse_csv_taglist(const char *buf, size_t length) { 144 CxList *taglist = cxLinkedListCreateSimple(CX_STORE_POINTERS); 145 cxDefineDestructor(taglist, free_dav_tag); 146 147 cxstring str = cx_strn(buf, length); 148 CxStrtokCtx tags = cx_strtok(str, CX_STR(","), INT_MAX); 149 cxstring tagstr; 150 while(cx_strtok_next(&tags, &tagstr)) { 151 cxstring trimmed_tag = cx_strtrim(tagstr); 152 if (trimmed_tag.length > 0) { 153 DavTag *tag = malloc(sizeof(DavTag)); 154 tag->name = cx_strdup(trimmed_tag).ptr; 155 tag->color = NULL; 156 cxListAdd(taglist, tag); 157 } 158 } 159 return taglist; 160 } 161 162 CxBuffer* create_csv_taglist(CxList *tags) { 163 CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 164 int insertsep = 0; 165 CxIterator i = cxListIterator(tags); 166 cx_foreach(DavTag*, tag, i) { 167 if(insertsep) { 168 cxBufferPut(buf, ','); 169 } 170 cxBufferPutString(buf, tag->name); 171 insertsep = 1; 172 } 173 return buf; 174 } 175 176 177 static DavTag* parse_xml_dav_tag(DavXmlNode *node) { 178 char *name = NULL; 179 char *color = NULL; 180 181 DavXmlNode *c = node->children; 182 while(c) { 183 if(c->type == DAV_XML_ELEMENT) { 184 char *value = dav_xml_getstring(c->children); 185 if(value) { 186 if(!strcmp(c->namespace, DAV_PROPS_NS)) { 187 if(!strcmp(c->name, "name")) { 188 name = value; 189 } 190 if(!strcmp(c->name, "color")) { 191 color = value; 192 } 193 } 194 } 195 } 196 c = c->next; 197 } 198 199 DavTag *tag = NULL; 200 if(name) { 201 tag = malloc(sizeof(DavTag)); 202 tag->name = strdup(name); 203 tag->color = color ? strdup(color) : NULL; 204 } 205 return tag; 206 } 207 208 CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) { 209 CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); 210 cxDefineDestructor(tags, free_dav_tag); 211 212 DavXmlNode *node = taglistnode; 213 while(node) { 214 if(node->type == DAV_XML_ELEMENT) { 215 if(!strcmp(node->namespace, DAV_PROPS_NS) && !strcmp(node->name, "tag")) { 216 DavTag *tag = parse_xml_dav_tag(node); 217 if(tag) { 218 cxListAdd(tags, tag); 219 } 220 } 221 } 222 node = node->next; 223 } 224 225 return tags; 226 } 227 228 DavXmlNode* create_xml_taglist(CxList *tags) { 229 DavXmlNode *tag1 = NULL; 230 DavXmlNode *lasttag = NULL; 231 CxIterator i = cxListIterator(tags); 232 cx_foreach(DavTag*, tag, i) { 233 DavXmlNode *tagelm = dav_xml_createnode(DAV_PROPS_NS, "tag"); 234 DavXmlNode *tagname = dav_xml_createnode_with_text(DAV_PROPS_NS, "name", tag->name); 235 tagelm->children = tagname; 236 if(tag->color) { 237 DavXmlNode *tagcolor = dav_xml_createnode_with_text(DAV_PROPS_NS, "color", tag->color); 238 tagname->next = tagcolor; 239 } 240 241 if(lasttag) { 242 lasttag->next = tagelm; 243 tagelm->prev = lasttag; 244 } else { 245 tag1 = tagelm; 246 } 247 lasttag = tagelm; 248 } 249 return tag1; 250 } 251 252 253 #ifdef __APPLE__ 254 static DavTag* tagstr2davtag(const char *str) { 255 const char *name = str; 256 const char *color = NULL; 257 size_t len = strlen(str); 258 size_t namelen = len; 259 260 if(len == 0) { 261 return NULL; 262 } 263 264 // start with 1 because the first char should not be a linebreak 265 for(int i=1;i<len;i++) { 266 if(str[i] == '\n') { 267 if(!color) { 268 color = str + i + 1; 269 namelen = i; 270 } 271 } 272 } 273 int colorlen = len - namelen - 1; 274 275 DavTag *tag = malloc(sizeof(DavTag)); 276 tag->name = malloc(namelen + 1); 277 memcpy(tag->name, name, namelen); 278 tag->name[namelen] = 0; 279 if(colorlen > 0) { 280 tag->color = malloc(colorlen + 1); 281 memcpy(tag->color, color, colorlen); 282 tag->color[colorlen] = 0; 283 } else { 284 tag->color = NULL; 285 } 286 287 return tag; 288 } 289 290 CxList* parse_macos_taglist(const char *buf, size_t length) { 291 CxList *taglist = cxLinkedListCreateSimple(CX_STORE_POINTERS); 292 taglist->simple_destructor = (cx_destructor_func)free_dav_tag; 293 294 CFDataRef data = CFDataCreateWithBytesNoCopy( 295 kCFAllocatorDefault, 296 (const UInt8*)buf, 297 length, 298 kCFAllocatorNull); 299 CFPropertyListRef propertylist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, 0, NULL, NULL); 300 CFArrayRef array = propertylist; 301 int count = CFArrayGetCount(array); 302 for(int i=0;i<count;i++) { 303 CFStringRef str = CFArrayGetValueAtIndex(array, i); 304 int slen = CFStringGetLength(str); 305 size_t cstrbuflen = slen * 4 + 4; 306 char *cstr = malloc(cstrbuflen); 307 if(CFStringGetCString(str, cstr, cstrbuflen, kCFStringEncodingUTF8)) { 308 DavTag *tag = tagstr2davtag(cstr); 309 if(tag) { 310 cxListAdd(taglist, tag); 311 } 312 } 313 free(cstr); 314 } 315 316 CFRelease(propertylist); 317 CFRelease(data); 318 319 return taglist; 320 } 321 322 CxBuffer* create_macos_taglist(CxList *tags) { 323 size_t count = tags->size; 324 if(count == 0) { 325 return NULL; 326 } 327 328 CFStringRef *strings = calloc(sizeof(CFStringRef), count); 329 int i = 0; 330 CxIterator iter = cxListIterator(tags); 331 cx_foreach(DavTag*, tag, iter) { 332 CFStringRef str = NULL; 333 if(tag->color) { 334 cxmutstr s = cx_strcat(3, cx_mutstr(tag->name), CX_STR("\n"), cx_str(tag->color)); 335 str = CFStringCreateWithCString(kCFAllocatorDefault, s.ptr, kCFStringEncodingUTF8); 336 free(s.ptr); 337 } else { 338 str = CFStringCreateWithCString(kCFAllocatorDefault, tag->name, kCFStringEncodingUTF8); 339 } 340 strings[i] = str; 341 i++; 342 } 343 344 CFPropertyListRef array = CFArrayCreate(kCFAllocatorDefault, (const void**)strings, count, &kCFTypeArrayCallBacks); 345 CFDataRef data = CFPropertyListCreateData(kCFAllocatorDefault, array, kCFPropertyListBinaryFormat_v1_0, 0, NULL); 346 347 CxBuffer *buf = NULL; 348 if(data) { 349 int datalen = CFDataGetLength(data); 350 CFRange range; 351 range.location = 0; 352 range.length = datalen; 353 buf = cxBufferCreate(NULL, datalen, cxDefaultAllocator, 0); 354 CFDataGetBytes(data, range, (UInt8*)buf->space); 355 buf->size = datalen; 356 CFRelease(data); 357 } 358 359 for(int i=0;i<count;i++) { 360 CFRelease(strings[i]); 361 } 362 CFRelease(array); 363 364 return buf; 365 } 366 367 #else 368 CxList* parse_macos_taglist(const char *buf, size_t length) { 369 fprintf(stderr, "Error: macos tags not supported on this platform.\n"); 370 return NULL; 371 } 372 CxBuffer* create_macos_taglist(CxList *tags) { 373 fprintf(stderr, "Error: macos tags not supported on this platform.\n"); 374 return NULL; 375 } 376 #endif 377 378 379 int compare_taglists(CxList *tags1, CxList *tags2) { 380 if(!tags1) { 381 return tags2 ? 0 : 1; 382 } 383 if(!tags2) { 384 return tags1 ? 0 : 1; 385 } 386 387 CxMap *map1 = taglist2map(tags1); 388 389 int equal = 1; 390 int i = 0; 391 CxIterator iter = cxListIterator(tags2); 392 cx_foreach(DavTag*, t, iter) { 393 if(!cxMapGet(map1, cx_hash_key_str(t->name))) { 394 equal = 0; 395 break; 396 } 397 i++; 398 } 399 400 if(i != cxMapSize(map1)) { 401 equal = 0; 402 } 403 cxMapDestroy(map1); 404 return equal; 405 } 406 407 char* create_tags_hash(CxList *tags) { 408 if(!tags) { 409 return NULL; 410 } 411 CxBuffer *buf = create_text_taglist(tags); 412 char *hash = dav_create_hash(buf->space, buf->size); 413 cxBufferDestroy(buf); 414 return hash; 415 } 416 417 CxList* merge_tags(CxList *tags1, CxList *tags2) { 418 // this map is used to check the existence of tags 419 CxMap *tag_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); 420 // merged taglist 421 CxList *new_tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); 422 cxDefineDestructor(new_tags, free_dav_tag); 423 424 // add all local tags 425 if(tags1) { 426 CxIterator iter = cxListIterator(tags1); 427 cx_foreach(DavTag*, t, iter) { 428 cxMapPut(tag_map, cx_hash_key_str(t->name), t); 429 DavTag *newt = calloc(1, sizeof(DavTag)); 430 newt->color = t->color ? strdup(t->color) : NULL; 431 newt->name = strdup(t->name); 432 cxListAdd(new_tags, newt); 433 } 434 } 435 436 // check if a remote tag is already in the map 437 // and if not add it to the new taglist 438 if(tags2) { 439 CxIterator iter = cxListIterator(tags2); 440 cx_foreach(DavTag*, t, iter) { 441 if(!cxMapGet(tag_map, cx_hash_key_str(t->name))) { 442 DavTag *newt = calloc(1, sizeof(DavTag)); 443 newt->color = t->color ? strdup(t->color) : NULL; 444 newt->name = strdup(t->name); 445 cxListAdd(new_tags, newt); 446 } 447 } 448 } 449 450 cxMapDestroy(tag_map); 451 452 return new_tags; 453 } 454 455 void add_tag_colors(CxList *taglist, CxList *colored) { 456 CxMap *tagmap = taglist2map(taglist); 457 458 CxIterator i = cxListIterator(colored); 459 cx_foreach(DavTag*, colored_tag, i) { 460 if(colored_tag->color) { 461 DavTag *tag = cxMapGet(tagmap, cx_hash_key_str(colored_tag->name)); 462 if(tag && !tag->color) { 463 tag->color = strdup(colored_tag->color); 464 } 465 } 466 } 467 468 cxMapDestroy(tagmap); 469 } 470 471 /* ----------- ----------- tag filter ---------------------- */ 472 473 static size_t rtrimskip(cxstring str, size_t skip) { 474 while (skip < str.length && isspace(str.ptr[skip])) skip++; 475 return skip; 476 } 477 478 static size_t parse_tagfilter_taglist(cxstring fs, SyncTagFilter* tagfilter) { 479 size_t csvlen; 480 for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) { 481 if (fs.ptr[csvlen] == ')') break; 482 } 483 fs.length = csvlen; 484 485 tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length); 486 487 return csvlen; 488 } 489 490 static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* tagfilter); 491 492 static size_t parse_tagfilter_filter(cxstring fs, SyncTagFilter* tagfilter) { 493 494 size_t consumed = rtrimskip(fs, 0); 495 fs = cx_strsubs(fs, consumed); 496 497 if (fs.length == 0) { 498 return consumed; 499 } else { 500 501 // optional operator 502 int hasop = 0; 503 if (fs.ptr[0] == '&') { 504 tagfilter->mode = DAV_SYNC_TAGFILTER_AND; 505 hasop = 1; 506 } else if (fs.ptr[0] == '|') { 507 tagfilter->mode = DAV_SYNC_TAGFILTER_OR; 508 hasop = 1; 509 } else if (fs.ptr[0] == '0') { 510 tagfilter->mode = DAV_SYNC_TAGFILTER_NONE; 511 hasop = 1; 512 } else if (fs.ptr[0] == '1') { 513 tagfilter->mode = DAV_SYNC_TAGFILTER_ONE; 514 hasop = 1; 515 } else { 516 // default operator is AND 517 tagfilter->mode = DAV_SYNC_TAGFILTER_AND; 518 } 519 520 if (hasop) { 521 size_t skip = rtrimskip(fs, 1); 522 consumed += skip; 523 fs = cx_strsubs(fs, skip); 524 } 525 526 if (fs.length > 0 && fs.ptr[0] == '(') { 527 size_t c = parse_tagfilter_subfilters(fs, tagfilter); 528 if (c) { 529 return consumed + c; 530 } else { 531 return 0; 532 } 533 } else { 534 tagfilter->subfilter_count = 0; 535 tagfilter->subfilters = NULL; 536 return consumed + parse_tagfilter_taglist(fs, tagfilter); 537 } 538 } 539 } 540 541 /* 542 * Parses: ( "(" , filter , ")" )+ 543 */ 544 static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* f) { 545 546 // strategy: allocate much and give back later (instead of reallocs in loop) 547 size_t subfilter_cap = 8; 548 f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*)); 549 f->subfilter_count = 0; 550 551 size_t total_consumed = 0; 552 size_t c; 553 do { 554 // skip leading parenthesis (and white spaces) 555 c = rtrimskip(fs, 1); 556 fs = cx_strsubs(fs, c); 557 total_consumed += c; 558 559 // increase array capacity, if necessary 560 if (f->subfilter_count >= subfilter_cap) { 561 subfilter_cap *= 2; 562 SyncTagFilter** newarr = realloc(f->subfilters, 563 subfilter_cap * sizeof(SyncTagFilter*)); 564 if (newarr) { 565 f->subfilters = newarr; 566 } else { 567 abort(); // no error handling reachable, so we are fucked 568 } 569 } 570 571 // allocate space for a new filter 572 SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter)); 573 574 // parse that filter 575 c = parse_tagfilter_filter(fs, subf); 576 577 // sanity check: we must end with a closing parenthesis 578 if (c > 0 && fs.ptr[c] == ')') { 579 f->subfilters[f->subfilter_count++] = subf; 580 581 // consume ')' and find the next parenthesis or the end-of-string 582 c = rtrimskip(fs, 1+c); 583 fs = cx_strsubs(fs, c); 584 total_consumed += c; 585 586 if (fs.length == 0 || fs.ptr[0] == ')') { 587 // our job is done 588 break; 589 } else if (fs.ptr[0] != '(') { 590 // anything else than a parenthesis or end-of-string is an error 591 return 0; 592 } 593 } else { 594 free(subf); 595 break; 596 } 597 598 } while(1); 599 600 // try to shrink the array 601 if (f->subfilter_count > 0) { 602 SyncTagFilter** shrinked_array = realloc(f->subfilters, 603 f->subfilter_count * sizeof(SyncTagFilter*)); 604 if (shrinked_array) { 605 f->subfilters = shrinked_array; 606 } 607 } else { 608 free(f->subfilters); 609 f->subfilters = NULL; 610 } 611 612 return total_consumed; 613 } 614 615 SyncTagFilter* parse_tagfilter_string(const char* filterstring, int scope) { 616 SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter)); 617 tagfilter->scope = scope; 618 if (!filterstring) { 619 return tagfilter; 620 } 621 622 cxstring fs = cx_str(filterstring); 623 size_t consumed = parse_tagfilter_filter(fs, tagfilter); 624 if (!consumed) { 625 free_tagfilter(tagfilter); 626 return NULL; 627 } 628 629 // consume trailing white spaces 630 consumed = rtrimskip(fs, consumed); 631 632 // sanity check: have we consumed the whole string? 633 if (consumed != fs.length) { 634 free_tagfilter(tagfilter); 635 return NULL; 636 } 637 638 return tagfilter; 639 } 640 641 void free_tagfilter(SyncTagFilter* filter) { 642 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 643 free_tagfilter(filter->subfilters[i]); 644 } 645 free(filter->subfilters); 646 free(filter); 647 } 648 649 650 static int matches_tags_and(CxList *dav_tags, CxList *tags, int ignorecase) { 651 // ignorecase not supported yet 652 int ret = 1; 653 CxMap *tagmap = taglist2map(dav_tags); 654 CxIterator i = cxListIterator(tags); 655 cx_foreach(DavTag *, tag, i) { 656 if (cxMapGet(tagmap, cx_hash_key_str(tag->name)) == NULL) { 657 ret = 0; 658 break; 659 } 660 } 661 cxMapDestroy(tagmap); 662 return ret; 663 } 664 665 static int matches_tags_or(CxList *dav_tags, CxList *tags, int ignorecase) { 666 // ignorecase not supported yet 667 int ret = 0; 668 CxMap *tagmap = taglist2map(dav_tags); 669 CxIterator i = cxListIterator(tags); 670 cx_foreach(DavTag *, tag, i) { 671 if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { 672 ret = 1; 673 break; 674 } 675 } 676 cxMapDestroy(tagmap); 677 return ret; 678 } 679 680 static int matches_tags_one(CxList *dav_tags, CxList *tags, int ignorecase) { 681 int matches_exactly_one = 0; 682 CxMap *tagmap = taglist2map(dav_tags); 683 CxIterator i = cxListIterator(tags); 684 cx_foreach(DavTag *, tag, i) { 685 if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { 686 if (matches_exactly_one) { 687 cxMapDestroy(tagmap); 688 return 0; 689 } else { 690 matches_exactly_one = 1; 691 } 692 } 693 } 694 cxMapDestroy(tagmap); 695 return matches_exactly_one; 696 } 697 698 static int matches_subfilters_and(CxList *dav_tags, SyncTagFilter *filter) { 699 int ret = 1; 700 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 701 ret &= matches_tagfilter(dav_tags, filter->subfilters[i]); 702 } 703 return ret; 704 } 705 706 static int matches_subfilters_or(CxList *dav_tags, SyncTagFilter *filter) { 707 int ret = 0; 708 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 709 ret |= matches_tagfilter(dav_tags, filter->subfilters[i]); 710 } 711 return ret; 712 } 713 714 static int matches_subfilters_one(CxList *dav_tags, SyncTagFilter *filter) { 715 int one = 0; 716 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 717 if (matches_tagfilter(dav_tags, filter->subfilters[i])) { 718 if (one) { 719 return 0; 720 } else { 721 one = 1; 722 } 723 } 724 } 725 return one; 726 } 727 728 int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter) { 729 730 if (tagfilter->subfilter_count > 0) { 731 switch (tagfilter->mode) { 732 case DAV_SYNC_TAGFILTER_OR: 733 return matches_subfilters_or(dav_tags, tagfilter); 734 case DAV_SYNC_TAGFILTER_AND: 735 return matches_subfilters_and(dav_tags, tagfilter); 736 case DAV_SYNC_TAGFILTER_NONE: 737 return !matches_subfilters_or(dav_tags, tagfilter); 738 case DAV_SYNC_TAGFILTER_ONE: 739 return matches_subfilters_one(dav_tags, tagfilter); 740 default: 741 abort(); 742 } 743 } else { 744 int ignorecase = 0; // TODO: maybe add support later 745 switch (tagfilter->mode) { 746 case DAV_SYNC_TAGFILTER_OR: 747 return matches_tags_or(dav_tags, tagfilter->tags, ignorecase); 748 case DAV_SYNC_TAGFILTER_AND: 749 return matches_tags_and(dav_tags, tagfilter->tags, ignorecase); 750 case DAV_SYNC_TAGFILTER_NONE: 751 return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase); 752 case DAV_SYNC_TAGFILTER_ONE: 753 return matches_tags_one(dav_tags, tagfilter->tags, ignorecase); 754 default: 755 abort(); 756 } 757 } 758 } 759 760