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 tags->simple_destructor = (cx_destructor_func)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, tags->size + 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 taglist->simple_destructor = (cx_destructor_func)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 char *value = dav_xml_getstring(c->children); 189 if(value) { 190 name = value; 191 } 192 } 193 if(!strcmp(c->name, "color")) { 194 char *value = dav_xml_getstring(c->children); 195 if(value) { 196 color = value; 197 } 198 } 199 } 200 } 201 } 202 c = c->next; 203 } 204 205 DavTag *tag = NULL; 206 if(name) { 207 tag = malloc(sizeof(DavTag)); 208 tag->name = strdup(name); 209 tag->color = color ? strdup(color) : NULL; 210 } 211 return tag; 212 } 213 214 CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) { 215 CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); 216 tags->simple_destructor = (cx_destructor_func)free_dav_tag; 217 218 DavXmlNode *node = taglistnode; 219 while(node) { 220 if(node->type == DAV_XML_ELEMENT) { 221 if(!strcmp(node->namespace, DAV_PROPS_NS) && !strcmp(node->name, "tag")) { 222 DavTag *tag = parse_xml_dav_tag(node); 223 if(tag) { 224 cxListAdd(tags, tag); 225 } 226 } 227 } 228 node = node->next; 229 } 230 231 return tags; 232 } 233 234 DavXmlNode* create_xml_taglist(CxList *tags) { 235 DavXmlNode *tag1 = NULL; 236 DavXmlNode *lasttag = NULL; 237 CxIterator i = cxListIterator(tags); 238 cx_foreach(DavTag*, tag, i) { 239 DavXmlNode *tagelm = dav_xml_createnode(DAV_PROPS_NS, "tag"); 240 DavXmlNode *tagname = dav_xml_createnode_with_text(DAV_PROPS_NS, "name", tag->name); 241 tagelm->children = tagname; 242 if(tag->color) { 243 DavXmlNode *tagcolor = dav_xml_createnode_with_text(DAV_PROPS_NS, "color", tag->color); 244 tagname->next = tagcolor; 245 } 246 247 if(lasttag) { 248 lasttag->next = tagelm; 249 tagelm->prev = lasttag; 250 } else { 251 tag1 = tagelm; 252 } 253 lasttag = tagelm; 254 } 255 return tag1; 256 } 257 258 259 #ifdef __APPLE__ 260 static DavTag* tagstr2davtag(const char *str) { 261 const char *name = str; 262 const char *color = NULL; 263 size_t len = strlen(str); 264 size_t namelen = len; 265 266 if(len == 0) { 267 return NULL; 268 } 269 270 // start with 1 because the first char should not be a linebreak 271 for(int i=1;i<len;i++) { 272 if(str[i] == '\n') { 273 if(!color) { 274 color = str + i + 1; 275 namelen = i; 276 } 277 } 278 } 279 int colorlen = len - namelen - 1; 280 281 DavTag *tag = malloc(sizeof(DavTag)); 282 tag->name = malloc(namelen + 1); 283 memcpy(tag->name, name, namelen); 284 tag->name[namelen] = 0; 285 if(colorlen > 0) { 286 tag->color = malloc(colorlen + 1); 287 memcpy(tag->color, color, colorlen); 288 tag->color[colorlen] = 0; 289 } else { 290 tag->color = NULL; 291 } 292 293 return tag; 294 } 295 296 CxList* parse_macos_taglist(const char *buf, size_t length) { 297 CxList *taglist = cxLinkedListCreateSimple(CX_STORE_POINTERS); 298 taglist->simple_destructor = (cx_destructor_func)free_dav_tag; 299 300 CFDataRef data = CFDataCreateWithBytesNoCopy( 301 kCFAllocatorDefault, 302 (const UInt8*)buf, 303 length, 304 kCFAllocatorNull); 305 CFPropertyListRef propertylist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, 0, NULL, NULL); 306 CFArrayRef array = propertylist; 307 int count = CFArrayGetCount(array); 308 for(int i=0;i<count;i++) { 309 CFStringRef str = CFArrayGetValueAtIndex(array, i); 310 int slen = CFStringGetLength(str); 311 size_t cstrbuflen = slen * 4 + 4; 312 char *cstr = malloc(cstrbuflen); 313 if(CFStringGetCString(str, cstr, cstrbuflen, kCFStringEncodingUTF8)) { 314 DavTag *tag = tagstr2davtag(cstr); 315 if(tag) { 316 cxListAdd(taglist, tag); 317 } 318 } 319 free(cstr); 320 } 321 322 CFRelease(propertylist); 323 CFRelease(data); 324 325 return taglist; 326 } 327 328 CxBuffer* create_macos_taglist(CxList *tags) { 329 size_t count = tags->size; 330 if(count == 0) { 331 return NULL; 332 } 333 334 CFStringRef *strings = calloc(sizeof(CFStringRef), count); 335 int i = 0; 336 CxIterator iter = cxListIterator(tags); 337 cx_foreach(DavTag*, tag, iter) { 338 CFStringRef str = NULL; 339 if(tag->color) { 340 cxmutstr s = cx_strcat(3, cx_mutstr(tag->name), CX_STR("\n"), cx_str(tag->color)); 341 str = CFStringCreateWithCString(kCFAllocatorDefault, s.ptr, kCFStringEncodingUTF8); 342 free(s.ptr); 343 } else { 344 str = CFStringCreateWithCString(kCFAllocatorDefault, tag->name, kCFStringEncodingUTF8); 345 } 346 strings[i] = str; 347 i++; 348 } 349 350 CFPropertyListRef array = CFArrayCreate(kCFAllocatorDefault, (const void**)strings, count, &kCFTypeArrayCallBacks); 351 CFDataRef data = CFPropertyListCreateData(kCFAllocatorDefault, array, kCFPropertyListBinaryFormat_v1_0, 0, NULL); 352 353 CxBuffer *buf = NULL; 354 if(data) { 355 int datalen = CFDataGetLength(data); 356 CFRange range; 357 range.location = 0; 358 range.length = datalen; 359 buf = cxBufferCreate(NULL, datalen, cxDefaultAllocator, 0); 360 CFDataGetBytes(data, range, (UInt8*)buf->space); 361 buf->size = datalen; 362 CFRelease(data); 363 } 364 365 for(int i=0;i<count;i++) { 366 CFRelease(strings[i]); 367 } 368 CFRelease(array); 369 370 return buf; 371 } 372 373 #else 374 CxList* parse_macos_taglist(const char *buf, size_t length) { 375 fprintf(stderr, "Error: macos tags not supported on this platform.\n"); 376 return NULL; 377 } 378 CxBuffer* create_macos_taglist(CxList *tags) { 379 fprintf(stderr, "Error: macos tags not supported on this platform.\n"); 380 return NULL; 381 } 382 #endif 383 384 385 int compare_taglists(CxList *tags1, CxList *tags2) { 386 if(!tags1) { 387 return tags2 ? 0 : 1; 388 } 389 if(!tags2) { 390 return tags1 ? 0 : 1; 391 } 392 393 CxMap *map1 = taglist2map(tags1); 394 395 int equal = 1; 396 int i = 0; 397 CxIterator iter = cxListIterator(tags2); 398 cx_foreach(DavTag*, t, iter) { 399 if(!cxMapGet(map1, cx_hash_key_str(t->name))) { 400 equal = 0; 401 break; 402 } 403 i++; 404 } 405 406 if(i != map1->size) { 407 equal = 0; 408 } 409 cxMapDestroy(map1); 410 return equal; 411 } 412 413 char* create_tags_hash(CxList *tags) { 414 if(!tags) { 415 return NULL; 416 } 417 CxBuffer *buf = create_text_taglist(tags); 418 char *hash = dav_create_hash(buf->space, buf->size); 419 cxBufferDestroy(buf); 420 return hash; 421 } 422 423 CxList* merge_tags(CxList *tags1, CxList *tags2) { 424 // this map is used to check the existence of tags 425 CxMap *tag_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); 426 // merged taglist 427 CxList *new_tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); 428 new_tags->simple_destructor = (cx_destructor_func)free_dav_tag; 429 430 // add all local tags 431 if(tags1) { 432 CxIterator iter = cxListIterator(tags1); 433 cx_foreach(DavTag*, t, iter) { 434 cxMapPut(tag_map, cx_hash_key_str(t->name), t); 435 DavTag *newt = calloc(1, sizeof(DavTag)); 436 newt->color = t->color ? strdup(t->color) : NULL; 437 newt->name = strdup(t->name); 438 cxListAdd(new_tags, newt); 439 } 440 } 441 442 // check if a remote tag is already in the map 443 // and if not add it to the new taglist 444 if(tags2) { 445 CxIterator iter = cxListIterator(tags2); 446 cx_foreach(DavTag*, t, iter) { 447 if(!cxMapGet(tag_map, cx_hash_key_str(t->name))) { 448 DavTag *newt = calloc(1, sizeof(DavTag)); 449 newt->color = t->color ? strdup(t->color) : NULL; 450 newt->name = strdup(t->name); 451 cxListAdd(new_tags, newt); 452 } 453 } 454 } 455 456 cxMapDestroy(tag_map); 457 458 return new_tags; 459 } 460 461 void add_tag_colors(CxList *taglist, CxList *colored) { 462 CxMap *tagmap = taglist2map(taglist); 463 464 CxIterator i = cxListIterator(colored); 465 cx_foreach(DavTag*, colored_tag, i) { 466 if(colored_tag->color) { 467 DavTag *tag = cxMapGet(tagmap, cx_hash_key_str(colored_tag->name)); 468 if(tag && !tag->color) { 469 tag->color = strdup(colored_tag->color); 470 } 471 } 472 } 473 474 cxMapDestroy(tagmap); 475 } 476 477 /* ----------- ----------- tag filter ---------------------- */ 478 479 static size_t rtrimskip(cxstring str, size_t skip) { 480 while (skip < str.length && isspace(str.ptr[skip])) skip++; 481 return skip; 482 } 483 484 static size_t parse_tagfilter_taglist(cxstring fs, SyncTagFilter* tagfilter) { 485 size_t csvlen; 486 for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) { 487 if (fs.ptr[csvlen] == ')') break; 488 } 489 fs.length = csvlen; 490 491 tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length); 492 493 return csvlen; 494 } 495 496 static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* tagfilter); 497 498 static size_t parse_tagfilter_filter(cxstring fs, SyncTagFilter* tagfilter) { 499 500 size_t consumed = rtrimskip(fs, 0); 501 fs = cx_strsubs(fs, consumed); 502 503 if (fs.length == 0) { 504 return consumed; 505 } else { 506 507 // optional operator 508 int hasop = 0; 509 if (fs.ptr[0] == '&') { 510 tagfilter->mode = DAV_SYNC_TAGFILTER_AND; 511 hasop = 1; 512 } else if (fs.ptr[0] == '|') { 513 tagfilter->mode = DAV_SYNC_TAGFILTER_OR; 514 hasop = 1; 515 } else if (fs.ptr[0] == '0') { 516 tagfilter->mode = DAV_SYNC_TAGFILTER_NONE; 517 hasop = 1; 518 } else if (fs.ptr[0] == '1') { 519 tagfilter->mode = DAV_SYNC_TAGFILTER_ONE; 520 hasop = 1; 521 } else { 522 // default operator is AND 523 tagfilter->mode = DAV_SYNC_TAGFILTER_AND; 524 } 525 526 if (hasop) { 527 size_t skip = rtrimskip(fs, 1); 528 consumed += skip; 529 fs = cx_strsubs(fs, skip); 530 } 531 532 if (fs.length > 0 && fs.ptr[0] == '(') { 533 size_t c = parse_tagfilter_subfilters(fs, tagfilter); 534 if (c) { 535 return consumed + c; 536 } else { 537 return 0; 538 } 539 } else { 540 tagfilter->subfilter_count = 0; 541 tagfilter->subfilters = NULL; 542 return consumed + parse_tagfilter_taglist(fs, tagfilter); 543 } 544 } 545 } 546 547 /* 548 * Parses: ( "(" , filter , ")" )+ 549 */ 550 static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* f) { 551 552 // strategy: allocate much and give back later (instead of reallocs in loop) 553 size_t subfilter_cap = 8; 554 f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*)); 555 f->subfilter_count = 0; 556 557 size_t total_consumed = 0; 558 size_t c; 559 do { 560 // skip leading parenthesis (and white spaces) 561 c = rtrimskip(fs, 1); 562 fs = cx_strsubs(fs, c); 563 total_consumed += c; 564 565 // increase array capacity, if necessary 566 if (f->subfilter_count >= subfilter_cap) { 567 subfilter_cap *= 2; 568 SyncTagFilter** newarr = realloc(f->subfilters, 569 subfilter_cap * sizeof(SyncTagFilter*)); 570 if (newarr) { 571 f->subfilters = newarr; 572 } else { 573 abort(); // no error handling reachable, so we are fucked 574 } 575 } 576 577 // allocate space for a new filter 578 SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter)); 579 580 // parse that filter 581 c = parse_tagfilter_filter(fs, subf); 582 583 // sanity check: we must end with a closing parenthesis 584 if (c > 0 && fs.ptr[c] == ')') { 585 f->subfilters[f->subfilter_count++] = subf; 586 587 // consume ')' and find the next parenthesis or the end-of-string 588 c = rtrimskip(fs, 1+c); 589 fs = cx_strsubs(fs, c); 590 total_consumed += c; 591 592 if (fs.length == 0 || fs.ptr[0] == ')') { 593 // our job is done 594 break; 595 } else if (fs.ptr[0] != '(') { 596 // anything else than a parenthesis or end-of-string is an error 597 return 0; 598 } 599 } else { 600 free(subf); 601 break; 602 } 603 604 } while(1); 605 606 // try to shrink the array 607 if (f->subfilter_count > 0) { 608 SyncTagFilter** shrinked_array = realloc(f->subfilters, 609 f->subfilter_count * sizeof(SyncTagFilter*)); 610 if (shrinked_array) { 611 f->subfilters = shrinked_array; 612 } 613 } else { 614 free(f->subfilters); 615 f->subfilters = NULL; 616 } 617 618 return total_consumed; 619 } 620 621 SyncTagFilter* parse_tagfilter_string(const char* filterstring, int scope) { 622 SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter)); 623 tagfilter->scope = scope; 624 if (!filterstring) { 625 return tagfilter; 626 } 627 628 cxstring fs = cx_str(filterstring); 629 size_t consumed = parse_tagfilter_filter(fs, tagfilter); 630 if (!consumed) { 631 free_tagfilter(tagfilter); 632 return NULL; 633 } 634 635 // consume trailing white spaces 636 consumed = rtrimskip(fs, consumed); 637 638 // sanity check: have we consumed the whole string? 639 if (consumed != fs.length) { 640 free_tagfilter(tagfilter); 641 return NULL; 642 } 643 644 return tagfilter; 645 } 646 647 void free_tagfilter(SyncTagFilter* filter) { 648 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 649 free_tagfilter(filter->subfilters[i]); 650 } 651 free(filter->subfilters); 652 free(filter); 653 } 654 655 656 static int matches_tags_and(CxList *dav_tags, CxList *tags, int ignorecase) { 657 // ignorecase not supported yet 658 int ret = 1; 659 CxMap *tagmap = taglist2map(dav_tags); 660 CxIterator i = cxListIterator(tags); 661 cx_foreach(DavTag *, tag, i) { 662 if (cxMapGet(tagmap, cx_hash_key_str(tag->name)) == NULL) { 663 ret = 0; 664 break; 665 } 666 } 667 cxMapDestroy(tagmap); 668 return ret; 669 } 670 671 static int matches_tags_or(CxList *dav_tags, CxList *tags, int ignorecase) { 672 // ignorecase not supported yet 673 int ret = 0; 674 CxMap *tagmap = taglist2map(dav_tags); 675 CxIterator i = cxListIterator(tags); 676 cx_foreach(DavTag *, tag, i) { 677 if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { 678 ret = 1; 679 break; 680 } 681 } 682 cxMapDestroy(tagmap); 683 return ret; 684 } 685 686 static int matches_tags_one(CxList *dav_tags, CxList *tags, int ignorecase) { 687 int matches_exactly_one = 0; 688 CxMap *tagmap = taglist2map(dav_tags); 689 CxIterator i = cxListIterator(tags); 690 cx_foreach(DavTag *, tag, i) { 691 if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { 692 if (matches_exactly_one) { 693 cxMapDestroy(tagmap); 694 return 0; 695 } else { 696 matches_exactly_one = 1; 697 } 698 } 699 } 700 cxMapDestroy(tagmap); 701 return matches_exactly_one; 702 } 703 704 static int matches_subfilters_and(CxList *dav_tags, SyncTagFilter *filter) { 705 int ret = 1; 706 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 707 ret &= matches_tagfilter(dav_tags, filter->subfilters[i]); 708 } 709 return ret; 710 } 711 712 static int matches_subfilters_or(CxList *dav_tags, SyncTagFilter *filter) { 713 int ret = 0; 714 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 715 ret |= matches_tagfilter(dav_tags, filter->subfilters[i]); 716 } 717 return ret; 718 } 719 720 static int matches_subfilters_one(CxList *dav_tags, SyncTagFilter *filter) { 721 int one = 0; 722 for (size_t i = 0 ; i < filter->subfilter_count ; i++) { 723 if (matches_tagfilter(dav_tags, filter->subfilters[i])) { 724 if (one) { 725 return 0; 726 } else { 727 one = 1; 728 } 729 } 730 } 731 return one; 732 } 733 734 int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter) { 735 736 if (tagfilter->subfilter_count > 0) { 737 switch (tagfilter->mode) { 738 case DAV_SYNC_TAGFILTER_OR: 739 return matches_subfilters_or(dav_tags, tagfilter); 740 case DAV_SYNC_TAGFILTER_AND: 741 return matches_subfilters_and(dav_tags, tagfilter); 742 case DAV_SYNC_TAGFILTER_NONE: 743 return !matches_subfilters_or(dav_tags, tagfilter); 744 case DAV_SYNC_TAGFILTER_ONE: 745 return matches_subfilters_one(dav_tags, tagfilter); 746 default: 747 abort(); 748 } 749 } else { 750 int ignorecase = 0; // TODO: maybe add support later 751 switch (tagfilter->mode) { 752 case DAV_SYNC_TAGFILTER_OR: 753 return matches_tags_or(dav_tags, tagfilter->tags, ignorecase); 754 case DAV_SYNC_TAGFILTER_AND: 755 return matches_tags_and(dav_tags, tagfilter->tags, ignorecase); 756 case DAV_SYNC_TAGFILTER_NONE: 757 return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase); 758 case DAV_SYNC_TAGFILTER_ONE: 759 return matches_tags_one(dav_tags, tagfilter->tags, ignorecase); 760 default: 761 abort(); 762 } 763 } 764 } 765 766