LEFT | RIGHT |
1 #include "PluginStdAfx.h" | 1 #include "PluginStdAfx.h" |
2 | 2 |
3 #include "PluginFilter.h" | 3 #include "PluginFilter.h" |
4 | 4 |
5 #if (defined PRODUCT_ADBLOCKPLUS) | 5 #if (defined PRODUCT_ADBLOCKPLUS) |
6 #include "PluginSettings.h" | 6 #include "PluginSettings.h" |
7 #include "PluginClient.h" | 7 #include "PluginClient.h" |
8 #include "PluginClientFactory.h" | 8 #include "PluginClientFactory.h" |
9 #endif | 9 #endif |
10 | 10 |
11 #include "PluginMutex.h" | 11 #include "PluginMutex.h" |
12 #include "PluginHttpRequest.h" | 12 #include "PluginHttpRequest.h" |
13 #include "PluginSettings.h" | 13 #include "PluginSettings.h" |
14 #include "PluginSystem.h" | 14 #include "PluginSystem.h" |
15 #include "PluginClass.h" | 15 #include "PluginClass.h" |
16 #include "mlang.h" | 16 #include "mlang.h" |
17 | 17 |
| 18 #include "..\shared\CriticalSection.h" |
| 19 |
18 | 20 |
19 // The filters are described at http://adblockplus.org/en/filters | 21 // The filters are described at http://adblockplus.org/en/filters |
20 | 22 |
21 CComAutoCriticalSection CPluginFilter::s_criticalSectionFilterMap; | 23 static CriticalSection s_criticalSectionFilterMap; |
22 | |
23 | 24 |
24 // ============================================================================ | 25 // ============================================================================ |
25 // CFilterElementHideAttrSelector | 26 // CFilterElementHideAttrSelector |
26 // ============================================================================ | 27 // ============================================================================ |
27 | 28 |
28 CFilterElementHideAttrSelector::CFilterElementHideAttrSelector() : m_type(TYPE_N
ONE), m_pos(POS_NONE), m_bstrAttr(NULL) | 29 CFilterElementHideAttrSelector::CFilterElementHideAttrSelector() : m_type(TYPE_N
ONE), m_pos(POS_NONE), m_bstrAttr(NULL) |
29 { | 30 { |
30 } | 31 } |
31 | 32 |
32 CFilterElementHideAttrSelector::CFilterElementHideAttrSelector(const CFilterElem
entHideAttrSelector& filter) | 33 CFilterElementHideAttrSelector::CFilterElementHideAttrSelector(const CFilterElem
entHideAttrSelector& filter) |
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
419 } | 420 } |
420 | 421 |
421 | 422 |
422 | 423 |
423 // ============================================================================ | 424 // ============================================================================ |
424 // CPluginFilter | 425 // CPluginFilter |
425 // ============================================================================ | 426 // ============================================================================ |
426 | 427 |
427 CPluginFilter::CPluginFilter(const CString& dataPath) : m_dataPath(dataPath) | 428 CPluginFilter::CPluginFilter(const CString& dataPath) : m_dataPath(dataPath) |
428 { | 429 { |
429 m_contentMap["document"] = CFilter::contentTypeDocument; | |
430 m_contentMap["subdocument"] = CFilter::contentTypeSubdocument; | |
431 m_contentMap["sub-document"] = CFilter::contentTypeSubdocument; | |
432 m_contentMap["sub_document"] = CFilter::contentTypeSubdocument; | |
433 m_contentMap["other"] = CFilter::contentTypeOther; | |
434 m_contentMap["image"] = CFilter::contentTypeImage; | |
435 m_contentMap["script"] = CFilter::contentTypeScript; | |
436 m_contentMap["object"] = CFilter::contentTypeObject; | |
437 m_contentMap["object-subrequest"] = CFilter::contentTypeObjectSubrequest; | |
438 m_contentMap["object_subrequest"] = CFilter::contentTypeObjectSubrequest; | |
439 m_contentMap["xml-request"] = CFilter::contentTypeXmlHttpRequest; | |
440 m_contentMap["xml_request"] = CFilter::contentTypeXmlHttpRequest; | |
441 m_contentMap["xmlhttprequest"] = CFilter::contentTypeXmlHttpRequest; | |
442 m_contentMap["stylesheet"] = CFilter::contentTypeStyleSheet; | |
443 m_contentMap["background"] = CFilter::contentTypeBackground; | |
444 | |
445 m_contentMapText[CFilter::contentTypeDocument] = "DOCUMENT"; | 430 m_contentMapText[CFilter::contentTypeDocument] = "DOCUMENT"; |
446 m_contentMapText[CFilter::contentTypeObject] = "OBJECT"; | 431 m_contentMapText[CFilter::contentTypeObject] = "OBJECT"; |
447 m_contentMapText[CFilter::contentTypeImage] = "IMAGE"; | 432 m_contentMapText[CFilter::contentTypeImage] = "IMAGE"; |
448 m_contentMapText[CFilter::contentTypeScript] = "SCRIPT"; | 433 m_contentMapText[CFilter::contentTypeScript] = "SCRIPT"; |
449 m_contentMapText[CFilter::contentTypeOther] = "OTHER"; | 434 m_contentMapText[CFilter::contentTypeOther] = "OTHER"; |
450 m_contentMapText[CFilter::contentTypeUnknown] = "OTHER"; | 435 m_contentMapText[CFilter::contentTypeUnknown] = "OTHER"; |
451 m_contentMapText[CFilter::contentTypeSubdocument] = "SUBDOCUMENT"; | 436 m_contentMapText[CFilter::contentTypeSubdocument] = "SUBDOCUMENT"; |
452 m_contentMapText[CFilter::contentTypeStyleSheet] = "STYLESHEET"; | 437 m_contentMapText[CFilter::contentTypeStyleSheet] = "STYLESHEET"; |
| 438 m_contentMapText[CFilter::contentTypeXmlHttpRequest] = "XMLHTTPREQUEST"; |
453 | 439 |
454 ClearFilters(); | 440 ClearFilters(); |
455 } | 441 } |
456 | 442 |
457 | 443 |
458 bool CPluginFilter::AddFilterElementHide(CString filterText) | 444 bool CPluginFilter::AddFilterElementHide(CString filterText) |
459 { | 445 { |
460 | 446 |
461 | 447 |
462 DEBUG_FILTER("Input: " + filterText + " filterFile" + filterFile); | 448 DEBUG_FILTER("Input: " + filterText + " filterFile" + filterFile); |
463 | 449 |
464 s_criticalSectionFilterMap.Lock(); | 450 CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap); |
465 { | 451 { |
466 | 452 |
467 CString filterString = filterText; | 453 CString filterString = filterText; |
468 // Create filter descriptor | 454 // Create filter descriptor |
469 std::auto_ptr<CFilterElementHide> filter; | 455 std::auto_ptr<CFilterElementHide> filter; |
470 | 456 |
471 CString wholeFilterString = filterString; | 457 CString wholeFilterString = filterString; |
472 TCHAR separatorChar; | 458 TCHAR separatorChar; |
473 do | 459 do |
474 { | 460 { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 m_elementHideTagsClass.insert(std::make_pair(std::make_pair(filter->m_
tag, filter->m_tagClassName), *filter)); | 498 m_elementHideTagsClass.insert(std::make_pair(std::make_pair(filter->m_
tag, filter->m_tagClassName), *filter)); |
513 } | 499 } |
514 else | 500 else |
515 { | 501 { |
516 std::pair<CString, CFilterElementHide> pair = std::make_pair(filter->m
_tag, *filter); | 502 std::pair<CString, CFilterElementHide> pair = std::make_pair(filter->m
_tag, *filter); |
517 m_elementHideTags.insert(pair); | 503 m_elementHideTags.insert(pair); |
518 } | 504 } |
519 } | 505 } |
520 } while (separatorChar != '\0'); | 506 } while (separatorChar != '\0'); |
521 } | 507 } |
522 s_criticalSectionFilterMap.Unlock(); | |
523 | 508 |
524 return true; | 509 return true; |
525 } | 510 } |
526 | 511 |
527 bool CPluginFilter::IsElementHidden(const CString& tag, IHTMLElement* pEl, const
CString& domain, const CString& indent) const | 512 bool CPluginFilter::IsElementHidden(const CString& tag, IHTMLElement* pEl, const
CString& domain, const CString& indent) const |
528 { | 513 { |
529 CString id; | 514 CString id; |
530 CComBSTR bstrId; | 515 CComBSTR bstrId; |
531 if (SUCCEEDED(pEl->get_id(&bstrId)) && bstrId) | 516 if (SUCCEEDED(pEl->get_id(&bstrId)) && bstrId) |
532 { | 517 { |
533 id = bstrId; | 518 id = bstrId; |
534 } | 519 } |
535 | 520 |
536 CString classNames; | 521 CString classNames; |
537 CComBSTR bstrClassNames; | 522 CComBSTR bstrClassNames; |
538 if (SUCCEEDED(pEl->get_className(&bstrClassNames)) && bstrClassNames) | 523 if (SUCCEEDED(pEl->get_className(&bstrClassNames)) && bstrClassNames) |
539 { | 524 { |
540 classNames = bstrClassNames; | 525 classNames = bstrClassNames; |
541 } | 526 } |
542 | 527 |
543 s_criticalSectionFilterMap.Lock(); | 528 CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap); |
544 { | 529 { |
545 CString domainTest = domain; | 530 CString domainTest = domain; |
546 | 531 |
547 // Search tag/id filters | 532 // Search tag/id filters |
548 if (!id.IsEmpty()) | 533 if (!id.IsEmpty()) |
549 { | 534 { |
550 std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHideT
agsNamed::const_iterator> idItEnum = | 535 std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHideT
agsNamed::const_iterator> idItEnum = |
551 m_elementHideTagsId.equal_range(std::make_pair(tag, id)); | 536 m_elementHideTagsId.equal_range(std::make_pair(tag, id)); |
552 for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; id
It != idItEnum.second; idIt ++) | 537 for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; id
It != idItEnum.second; idIt ++) |
553 { | 538 { |
554 if (idIt->second.IsMatchFilterElementHide(pEl)) | 539 if (idIt->second.IsMatchFilterElementHide(pEl)) |
555 { | 540 { |
556 #ifdef ENABLE_DEBUG_RESULT | 541 #ifdef ENABLE_DEBUG_RESULT |
557 DEBUG_HIDE_EL(indent + "HideEl::Found (tag/id) filter:" + idIt->second
.m_filterText) | 542 DEBUG_HIDE_EL(indent + "HideEl::Found (tag/id) filter:" + idIt->second
.m_filterText) |
558 CPluginDebug::DebugResultHiding(tag, "id:" + id, idIt->second.m_filt
erText); | 543 CPluginDebug::DebugResultHiding(tag, "id:" + id, idIt->second.m_filt
erText); |
559 #endif | 544 #endif |
560 s_criticalSectionFilterMap.Unlock(); | |
561 return true; | 545 return true; |
562 } | 546 } |
563 } | 547 } |
564 | 548 |
565 // Search general id | 549 // Search general id |
566 idItEnum = m_elementHideTagsId.equal_range(std::make_pair("", id)); | 550 idItEnum = m_elementHideTagsId.equal_range(std::make_pair("", id)); |
567 for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; id
It != idItEnum.second; idIt ++) | 551 for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; id
It != idItEnum.second; idIt ++) |
568 { | 552 { |
569 if (idIt->second.IsMatchFilterElementHide(pEl)) | 553 if (idIt->second.IsMatchFilterElementHide(pEl)) |
570 { | 554 { |
571 #ifdef ENABLE_DEBUG_RESULT | 555 #ifdef ENABLE_DEBUG_RESULT |
572 DEBUG_HIDE_EL(indent + "HideEl::Found (?/id) filter:" + idIt->second.m
_filterText) | 556 DEBUG_HIDE_EL(indent + "HideEl::Found (?/id) filter:" + idIt->second.m
_filterText) |
573 CPluginDebug::DebugResultHiding(tag, "id:" + id, idIt->second.m_filt
erText); | 557 CPluginDebug::DebugResultHiding(tag, "id:" + id, idIt->second.m_filt
erText); |
574 #endif | 558 #endif |
575 s_criticalSectionFilterMap.Unlock(); | |
576 return true; | 559 return true; |
577 } | 560 } |
578 } | 561 } |
579 } | 562 } |
580 | 563 |
581 // Search tag/className filters | 564 // Search tag/className filters |
582 if (!classNames.IsEmpty()) | 565 if (!classNames.IsEmpty()) |
583 { | 566 { |
584 int pos = 0; | 567 int pos = 0; |
585 CString className = classNames.Tokenize(L" \t\n\r", pos); | 568 CString className = classNames.Tokenize(L" \t\n\r", pos); |
586 while (pos >= 0) | 569 while (pos >= 0) |
587 { | 570 { |
588 std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHid
eTagsNamed::const_iterator> classItEnum = | 571 std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHid
eTagsNamed::const_iterator> classItEnum = |
589 m_elementHideTagsClass.equal_range(std::make_pair(tag, className)); | 572 m_elementHideTagsClass.equal_range(std::make_pair(tag, className)); |
590 | 573 |
591 for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.f
irst; classIt != classItEnum.second; ++classIt) | 574 for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.f
irst; classIt != classItEnum.second; ++classIt) |
592 { | 575 { |
593 if (classIt->second.IsMatchFilterElementHide(pEl)) | 576 if (classIt->second.IsMatchFilterElementHide(pEl)) |
594 { | 577 { |
595 #ifdef ENABLE_DEBUG_RESULT | 578 #ifdef ENABLE_DEBUG_RESULT |
596 DEBUG_HIDE_EL(indent + "HideEl::Found (tag/class) filter:" + classIt
->second.m_filterText) | 579 DEBUG_HIDE_EL(indent + "HideEl::Found (tag/class) filter:" + classIt
->second.m_filterText) |
597 CPluginDebug::DebugResultHiding(tag, "class:" + className, classIt
->second.m_filterText); | 580 CPluginDebug::DebugResultHiding(tag, "class:" + className, classIt
->second.m_filterText); |
598 #endif | 581 #endif |
599 s_criticalSectionFilterMap.Unlock(); | |
600 return true; | 582 return true; |
601 } | 583 } |
602 } | 584 } |
603 | 585 |
604 // Search general class name | 586 // Search general class name |
605 classItEnum = m_elementHideTagsClass.equal_range(std::make_pair("", clas
sName)); | 587 classItEnum = m_elementHideTagsClass.equal_range(std::make_pair("", clas
sName)); |
606 for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.f
irst; classIt != classItEnum.second; ++ classIt) | 588 for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.f
irst; classIt != classItEnum.second; ++ classIt) |
607 { | 589 { |
608 if (classIt->second.IsMatchFilterElementHide(pEl)) | 590 if (classIt->second.IsMatchFilterElementHide(pEl)) |
609 { | 591 { |
610 #ifdef ENABLE_DEBUG_RESULT | 592 #ifdef ENABLE_DEBUG_RESULT |
611 DEBUG_HIDE_EL(indent + "HideEl::Found (?/class) filter:" + classIt->
second.m_filterText) | 593 DEBUG_HIDE_EL(indent + "HideEl::Found (?/class) filter:" + classIt->
second.m_filterText) |
612 CPluginDebug::DebugResultHiding(tag, "class:" + className, classIt
->second.m_filterText); | 594 CPluginDebug::DebugResultHiding(tag, "class:" + className, classIt
->second.m_filterText); |
613 #endif | 595 #endif |
614 s_criticalSectionFilterMap.Unlock(); | |
615 return true; | 596 return true; |
616 } | 597 } |
617 } | 598 } |
618 | 599 |
619 // Next class name | 600 // Next class name |
620 className = classNames.Tokenize(L" \t\n\r", pos); | 601 className = classNames.Tokenize(L" \t\n\r", pos); |
621 } | 602 } |
622 } | 603 } |
623 | 604 |
624 // Search tag filters | 605 // Search tag filters |
625 std::pair<TFilterElementHideTags::const_iterator, TFilterElementHideTags::co
nst_iterator> tagItEnum | 606 std::pair<TFilterElementHideTags::const_iterator, TFilterElementHideTags::co
nst_iterator> tagItEnum |
626 = m_elementHideTags.equal_range(tag); | 607 = m_elementHideTags.equal_range(tag); |
627 for (TFilterElementHideTags::const_iterator tagIt = tagItEnum.first; tagIt !
= tagItEnum.second; ++ tagIt) | 608 for (TFilterElementHideTags::const_iterator tagIt = tagItEnum.first; tagIt !
= tagItEnum.second; ++ tagIt) |
628 { | 609 { |
629 if (tagIt->second.IsMatchFilterElementHide(pEl)) | 610 if (tagIt->second.IsMatchFilterElementHide(pEl)) |
630 { | 611 { |
631 #ifdef ENABLE_DEBUG_RESULT | 612 #ifdef ENABLE_DEBUG_RESULT |
632 DEBUG_HIDE_EL(indent + "HideEl::Found (tag) filter:" + tagIt->second.m_f
ilterText) | 613 DEBUG_HIDE_EL(indent + "HideEl::Found (tag) filter:" + tagIt->second.m_f
ilterText) |
633 CPluginDebug::DebugResultHiding(tag, "-", tagIt->second.m_filterText); | 614 CPluginDebug::DebugResultHiding(tag, "-", tagIt->second.m_filterText); |
634 #endif | 615 #endif |
635 s_criticalSectionFilterMap.Unlock(); | |
636 return true; | 616 return true; |
637 } | 617 } |
638 } | 618 } |
639 } | 619 } |
640 s_criticalSectionFilterMap.Unlock(); | |
641 | 620 |
642 return false; | 621 return false; |
643 } | 622 } |
644 | 623 |
645 bool CPluginFilter::LoadHideFilters(std::vector<std::string> filters) | 624 bool CPluginFilter::LoadHideFilters(std::vector<std::string> filters) |
646 { | 625 { |
647 | 626 |
648 ClearFilters(); | 627 ClearFilters(); |
649 | 628 |
650 bool isRead = false; | 629 bool isRead = false; |
651 | 630 |
652 #ifdef PRODUCT_ADBLOCKPLUS | 631 #ifdef PRODUCT_ADBLOCKPLUS |
653 CPluginClient* client = CPluginClient::GetInstance(); | 632 CPluginClient* client = CPluginClient::GetInstance(); |
654 #endif | 633 #endif |
655 | 634 |
656 // Parse hide string | 635 // Parse hide string |
657 int pos = 0; | 636 int pos = 0; |
658 | 637 |
659 s_criticalSectionFilterMap.Lock(); | 638 CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap); |
660 { | 639 { |
661 for (std::vector<std::string>::iterator it = filters.begin(); it < filters.e
nd(); ++it) | 640 for (std::vector<std::string>::iterator it = filters.begin(); it < filters.e
nd(); ++it) |
662 { | 641 { |
663 CString filter((*it).c_str()); | 642 CString filter((*it).c_str()); |
664 // If the line is not commented out | 643 // If the line is not commented out |
665 if (!filter.Trim().IsEmpty() && filter.GetAt(0) != '!' && filter.GetAt(0)
!= '[') | 644 if (!filter.Trim().IsEmpty() && filter.GetAt(0) != '!' && filter.GetAt(0)
!= '[') |
666 { | 645 { |
667 int filterType = 0; | 646 int filterType = 0; |
668 | 647 |
669 // See http://adblockplus.org/en/filters for further documentation | 648 // See http://adblockplus.org/en/filters for further documentation |
670 | 649 |
671 try | 650 try |
672 { | 651 { |
673 AddFilterElementHide(filter); | 652 AddFilterElementHide(filter); |
674 } | 653 } |
675 catch(...) | 654 catch(...) |
676 { | 655 { |
677 #ifdef ENABLE_DEBUG_RESULT | 656 #ifdef ENABLE_DEBUG_RESULT |
678 CPluginDebug::DebugResult(L"Error loading hide filter: " + filter); | 657 CPluginDebug::DebugResult(L"Error loading hide filter: " + filter); |
679 #endif | 658 #endif |
680 } | 659 } |
681 } | 660 } |
682 } | 661 } |
683 } | 662 } |
684 s_criticalSectionFilterMap.Unlock(); | |
685 | 663 |
686 return isRead; | 664 return isRead; |
687 } | 665 } |
688 | 666 |
689 void CPluginFilter::ClearFilters() | 667 void CPluginFilter::ClearFilters() |
690 { | 668 { |
691 // Clear filter maps | 669 // Clear filter maps |
692 s_criticalSectionFilterMap.Lock(); | 670 CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap); |
693 { | 671 { |
694 for (int i = 0; i < 2; i++) | 672 for (int i = 0; i < 2; i++) |
695 { | 673 { |
696 for (int j = 0; j < 2; j++) | 674 for (int j = 0; j < 2; j++) |
697 { | 675 { |
698 m_filterMap[i][j].clear(); | 676 m_filterMap[i][j].clear(); |
699 } | 677 } |
700 m_filterMapDefault[i].clear(); | 678 m_filterMapDefault[i].clear(); |
701 } | 679 } |
702 | 680 |
703 m_elementHideTags.clear(); | 681 m_elementHideTags.clear(); |
704 m_elementHideTagsId.clear(); | 682 m_elementHideTagsId.clear(); |
705 m_elementHideTagsClass.clear(); | 683 m_elementHideTagsClass.clear(); |
706 } | 684 } |
707 s_criticalSectionFilterMap.Unlock(); | |
708 } | 685 } |
709 | 686 |
710 | 687 |
711 bool CPluginFilter::ShouldWhiteList(CString src) const | 688 bool CPluginFilter::ShouldWhiteList(CString src) const |
712 { | 689 { |
713 // We should not block the empty string, so all filtering does not make sense | 690 // We should not block the empty string, so all filtering does not make sense |
714 // Therefore we just return | 691 // Therefore we just return |
715 if (src.Trim().IsEmpty()) | 692 if (src.Trim().IsEmpty()) |
716 { | 693 { |
717 return false; | 694 return false; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
754 | 731 |
755 std::string domainMb = CT2CA(domain); | 732 std::string domainMb = CT2CA(domain); |
756 | 733 |
757 if (client->Matches(url, contentTypeString, domainMb)) | 734 if (client->Matches(url, contentTypeString, domainMb)) |
758 { | 735 { |
759 if (addDebug) | 736 if (addDebug) |
760 { | 737 { |
761 DEBUG_FILTER("Filter::ShouldBlock " + type + " YES") | 738 DEBUG_FILTER("Filter::ShouldBlock " + type + " YES") |
762 | 739 |
763 #ifdef ENABLE_DEBUG_RESULT | 740 #ifdef ENABLE_DEBUG_RESULT |
764 CPluginDebug::DebugResultBlocking(type, src); | 741 CPluginDebug::DebugResultBlocking(type, src, domain); |
765 #endif | 742 #endif |
766 } | 743 } |
767 return true; | 744 return true; |
768 } | 745 } |
769 return false; | 746 return false; |
770 } | 747 } |
771 | 748 |
772 int CPluginFilter::FindMatch(const CString& src, CString filterPart, int srcStar
tPos) const | 749 int CPluginFilter::FindMatch(const CString& src, CString filterPart, int srcStar
tPos) const |
773 { | 750 { |
774 int filterCurrentPos = filterPart.Find('^'); | 751 int filterCurrentPos = filterPart.Find('^'); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
913 if (pos > 0 && domain.GetLength() + pos == subdomain.GetLength()) | 890 if (pos > 0 && domain.GetLength() + pos == subdomain.GetLength()) |
914 { | 891 { |
915 if (subdomain.GetAt(pos - 1) == '.') | 892 if (subdomain.GetAt(pos - 1) == '.') |
916 { | 893 { |
917 return true; | 894 return true; |
918 } | 895 } |
919 } | 896 } |
920 | 897 |
921 return false; | 898 return false; |
922 } | 899 } |
LEFT | RIGHT |