diff --git a/modules/core-module/src/main/java/org/simplejavamail/internal/util/MiscUtil.java b/modules/core-module/src/main/java/org/simplejavamail/internal/util/MiscUtil.java index cae174b5..45910674 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/internal/util/MiscUtil.java +++ b/modules/core-module/src/main/java/org/simplejavamail/internal/util/MiscUtil.java @@ -27,13 +27,8 @@ import java.lang.reflect.Method; import java.net.URL; import java.nio.charset.Charset; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -465,4 +460,10 @@ public static List asInternetAddresses(@NotNull List public static InternetAddress asInternetAddress(@NotNull Recipient recipient, @NotNull Charset charset) { return new InternetAddress(recipient.getAddress(), recipient.getName(), charset.name()); } + + @NotNull + public static Optional findFirstMatch(@NotNull Pattern pattern, @NotNull String input) { + Matcher matcher = pattern.matcher(input); + return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty(); + } } \ No newline at end of file diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java index be5290dc..655d933e 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java @@ -31,8 +31,7 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Optional.ofNullable; -import static org.simplejavamail.internal.util.MiscUtil.extractCID; -import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; +import static org.simplejavamail.internal.util.MiscUtil.*; import static org.slf4j.LoggerFactory.getLogger; /** @@ -43,6 +42,8 @@ public final class MimeMessageParser { private static final Logger LOGGER = getLogger(MimeMessageParser.class); + private static final Pattern CONTENT_TYPE_METHOD_PATTERN = Pattern.compile("method=\"?(\\w+)"); + private static final Pattern CALENDAR_BODY_METHOD_PATTERN = Pattern.compile("(?i)^METHOD:(\\w+)", Pattern.MULTILINE); static { MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); @@ -90,7 +91,7 @@ private static void parseMimePartTree(@NotNull final MimePart currentPart, @NotN checkContentTransferEncoding(currentPart, parsedComponents); } else if (isMimeType(currentPart, "text/calendar") && parsedComponents.calendarContent == null && !Part.ATTACHMENT.equalsIgnoreCase(disposition)) { parsedComponents.calendarContent = parseCalendarContent(currentPart); - parsedComponents.calendarMethod = parseCalendarMethod(currentPart); + parsedComponents.calendarMethod = parseCalendarMethod(currentPart, parsedComponents.calendarContent); checkContentTransferEncoding(currentPart, parsedComponents); } else if (isMimeType(currentPart, "multipart/*")) { final Multipart mp = parseContent(currentPart); @@ -165,6 +166,7 @@ private static boolean isEmailHeader(DecodedHeader header, String emailHeaderNam } @SuppressWarnings("WeakerAccess") + @NotNull public static String parseFileName(@NotNull final Part currentPart) { try { if (currentPart.getFileName() != null) { @@ -184,6 +186,7 @@ public static String parseFileName(@NotNull final Part currentPart) { /** * @return Returns the "content" part as String from the Calendar content type */ + @NotNull public static String parseCalendarContent(@NotNull MimePart currentPart) { Object content = parseContent(currentPart); if (content instanceof InputStream) { @@ -201,18 +204,18 @@ public static String parseCalendarContent(@NotNull MimePart currentPart) { * @return Returns the "method" part from the Calendar content type (such as "{@code text/calendar; charset="UTF-8"; method="REQUEST"}"). */ @SuppressWarnings("WeakerAccess") - public static String parseCalendarMethod(@NotNull MimePart currentPart) { - Pattern compile = Pattern.compile("method=\"?(\\w+)"); - final String contentType; + public static String parseCalendarMethod(@NotNull MimePart currentPart, @NotNull String calendarContent) { + final String contentType; try { contentType = currentPart.getDataHandler().getContentType(); } catch (final MessagingException e) { throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_CALENDAR_CONTENTTYPE, e); } - Matcher matcher = compile.matcher(contentType); - Preconditions.assumeTrue(matcher.find(), "Calendar METHOD not found in bodypart content type"); - return matcher.group(1); - } + + return findFirstMatch(CONTENT_TYPE_METHOD_PATTERN, contentType) + .orElseGet(() -> findFirstMatch(CALENDAR_BODY_METHOD_PATTERN, calendarContent) + .orElseThrow(() -> new IllegalArgumentException("Calendar METHOD not found in bodypart's content type or calendar content itself"))); + } @SuppressWarnings("WeakerAccess") @Nullable diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java index a6575302..46c404b6 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java @@ -1,6 +1,10 @@ package org.simplejavamail.converter; import jakarta.mail.util.ByteArrayDataSource; +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Calendar; +import net.fortuna.ical4j.model.Property; import org.assertj.core.api.Condition; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -17,9 +21,11 @@ import java.io.File; import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; import static demo.ResourceFolderHelper.determineResourceFolder; @@ -419,6 +425,56 @@ public void testGithub551_ContentTransferEncodingEndsWithSpaceBug() { assertThat(emailMime.getContentTransferEncoding()).isEqualTo(BIT7); } + @Test + public void testGithub552_BrokenCalendarMethod() throws ParserException, IOException { + Email emailMime = EmailConverter.emlToEmail(new File(RESOURCE_TEST_MESSAGES + "/#552 broken calendar method.eml")); + + assertThat(emailMime.getCalendarMethod()).isEqualTo(CalendarMethod.REQUEST); + assertThat(emailMime.getCalendarText()).isNotEmpty(); + + Calendar calendar = new CalendarBuilder() + .build(new StringReader(emailMime.getCalendarText())); + + assertThat(getPropertyValue(calendar, "SUMMARY")).contains("TestYandex"); + assertThat(getPropertyValue(calendar, "DTSTART")).contains("20240813T170000"); + assertThat(getPropertyValue(calendar, "DTEND")).contains("20240813T173000"); + assertThat(getPropertyValue(calendar, "UID")).contains("141zhi60x8914s7bzxzq27i0syandex.ru"); + assertThat(getPropertyValue(calendar, "SEQUENCE")).contains("0"); + assertThat(getPropertyValue(calendar, "DTSTAMP")).contains("20240813T135030Z"); + assertThat(getPropertyValue(calendar, "CREATED")).contains("20240813T135030Z"); + assertThat(getPropertyValue(calendar, "LAST-MODIFIED")).contains("20240813T135030Z"); + assertThat(getPropertyValue(calendar, "ORGANIZER")) + .hasValueSatisfying(org -> assertThat(org).contains("mailto:")) + .hasValueSatisfying(org -> assertThat(org).contains("ipopov")); + assertThat(calendar.getComponent("VEVENT") + .map(e -> e.getProperties("ATTENDEE"))) + .hasValueSatisfying( + attendees -> assertThat(attendees).satisfiesExactlyInAnyOrder( + attendeeProp -> assertThat(attendeeProp.getValue()).satisfies(attendee -> { + assertThat(attendee).contains("mailto:"); + assertThat(attendee).contains("ipopov"); + }), + attendeeProp -> assertThat(attendeeProp.getValue()).satisfies(attendee -> { + assertThat(attendee).contains("mailto:"); + assertThat(attendee).contains("skyvv1sp"); + }) + ) + ); + assertThat(getPropertyValue(calendar, "URL")).contains("https://calendar.yandex.ru/event?event_id=2182739972"); + assertThat(getPropertyValue(calendar, "TRANSP")).contains("OPAQUE"); + assertThat(getPropertyValue(calendar, "CATEGORIES")).contains("Мои события"); + assertThat(getPropertyValue(calendar, "CLASS")).contains("PRIVATE"); + assertThat(getPropertyValue(calendar, "DESCRIPTION")).contains(""); + assertThat(getPropertyValue(calendar, "LOCATION")).contains(""); + } + + private static @NotNull Optional getPropertyValue(Calendar calendar, String propertyName) { + return calendar + .getComponent("VEVENT") + .flatMap(e -> e.getProperty(propertyName)) + .map(Property::getValue); + } + @NotNull private List asList(AttachmentResource attachment) { List collectionAttachment = new ArrayList<>(); diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserParseCalendarMethodTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserParseCalendarMethodTest.java new file mode 100644 index 00000000..b49c17e8 --- /dev/null +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserParseCalendarMethodTest.java @@ -0,0 +1,88 @@ +package org.simplejavamail.converter.internal.mimemessage; + +import jakarta.activation.DataHandler; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimePart; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MimeMessageParserParseCalendarMethodTest { + + @Test + public void testMethodFoundInContentType() throws Exception { + MimePart mockMimePart = mock(MimePart.class); + DataHandler mockDataHandler = mock(DataHandler.class); + + // Mock Content-Type with METHOD + when(mockMimePart.getDataHandler()).thenReturn(mockDataHandler); + when(mockDataHandler.getContentType()).thenReturn("text/calendar; method=REQUEST; charset=UTF-8"); + + String calendarContent = "BEGIN:VCALENDAR\nMETHOD:REQUEST\nEND:VCALENDAR"; + + assertThat(MimeMessageParser.parseCalendarMethod(mockMimePart, calendarContent)).isEqualTo("REQUEST"); + } + + @Test + public void testMethodFoundInBody() throws Exception { + MimePart mockMimePart = mock(MimePart.class); + DataHandler mockDataHandler = mock(DataHandler.class); + + // Mock Content-Type without METHOD + when(mockMimePart.getDataHandler()).thenReturn(mockDataHandler); + when(mockDataHandler.getContentType()).thenReturn("text/calendar; charset=UTF-8"); + + // Method only in calendar content + String calendarContent = "BEGIN:VCALENDAR\nMETHOD:REQUEST\nEND:VCALENDAR"; + + assertThat(MimeMessageParser.parseCalendarMethod(mockMimePart, calendarContent)).isEqualTo("REQUEST"); + } + + @Test + public void testMethodNotFoundThrowsException() throws Exception { + MimePart mockMimePart = mock(MimePart.class); + DataHandler mockDataHandler = mock(DataHandler.class); + + // Mock Content-Type and Body without METHOD + when(mockMimePart.getDataHandler()).thenReturn(mockDataHandler); + when(mockDataHandler.getContentType()).thenReturn("text/calendar; charset=UTF-8"); + + // No method in the calendar body + String calendarContent = "BEGIN:VCALENDAR\nEND:VCALENDAR"; + + assertThatThrownBy(() -> MimeMessageParser.parseCalendarMethod(mockMimePart, calendarContent)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Calendar METHOD not found"); + } + + @Test + public void testMessagingExceptionThrown() throws Exception { + MimePart mockMimePart = mock(MimePart.class); + + when(mockMimePart.getDataHandler()).thenThrow(new MessagingException("Failed to retrieve content type")); + + String calendarContent = "BEGIN:VCALENDAR\nMETHOD=REQUEST\nEND:VCALENDAR"; + + assertThatThrownBy(() -> MimeMessageParser.parseCalendarMethod(mockMimePart, calendarContent)) + .isInstanceOf(MimeMessageParseException.class) + .hasMessageContaining(MimeMessageParseException.ERROR_GETTING_CALENDAR_CONTENTTYPE); + } + + @Test + public void testMethodInBothContentTypeAndBody_ContentTypeTakesPriority() throws Exception { + MimePart mockMimePart = mock(MimePart.class); + DataHandler mockDataHandler = mock(DataHandler.class); + + // Mock Content-Type with METHOD + when(mockMimePart.getDataHandler()).thenReturn(mockDataHandler); + when(mockDataHandler.getContentType()).thenReturn("text/calendar; method=REQUEST; charset=UTF-8"); + + // METHOD also present in calendar content, but different from Content-Type + String calendarContent = "BEGIN:VCALENDAR\nMETHOD:CANCEL\nEND:VCALENDAR"; + + assertThat(MimeMessageParser.parseCalendarMethod(mockMimePart, calendarContent)).isEqualTo("REQUEST"); + } +} \ No newline at end of file diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/internal/util/MiscUtilTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/internal/util/MiscUtilTest.java index 17f6c22b..d321fb80 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/internal/util/MiscUtilTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/internal/util/MiscUtilTest.java @@ -13,8 +13,10 @@ import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.regex.Pattern.compile; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.simplejavamail.internal.util.MiscUtil.findFirstMatch; public class MiscUtilTest { @@ -234,4 +236,14 @@ public void testAddRecipientByInternetAddress() { // next one is unparsable by InternetAddress#parse(), so it should be taken as is assertThat(MiscUtil.interpretRecipient(null, false, " \" m oo \" a@b.com ", null)).isEqualTo(new Recipient(null, " \" m oo \" a@b.com ", null)); } + + @Test + public void testFindFirstMatch() { + assertThat(findFirstMatch(compile("method=(\\w+)"), "Content-Type: text/calendar; method=REQUEST; charset=UTF-8")).hasValue("REQUEST"); + assertThat(findFirstMatch(compile("method=(\\w+)"), "Content-Type: text/calendar; charset=UTF-8")).isEmpty(); + assertThat(findFirstMatch(compile("method=(\\w+)"), "")).isEmpty(); + assertThat(findFirstMatch(compile("method=(\\w+)"), "Content-Type: text/calendar; method=RE$QUEST; charset=UTF-8")).isNotEmpty(); + assertThat(findFirstMatch(compile("method=(\\w+)"), "method=REJECT; method=REQUEST")).hasValue("REJECT"); + assertThat(findFirstMatch(compile("(?i)method=(\\w+)"), "Content-Type: text/calendar; METHOD=REQUEST; charset=UTF-8")).hasValue("REQUEST"); + } } \ No newline at end of file diff --git a/modules/simple-java-mail/src/test/resources/test-messages/#552 broken calendar method.eml b/modules/simple-java-mail/src/test/resources/test-messages/#552 broken calendar method.eml new file mode 100644 index 00000000..9b191ca6 --- /dev/null +++ b/modules/simple-java-mail/src/test/resources/test-messages/#552 broken calendar method.eml @@ -0,0 +1,217 @@ +Received: from postback5b.mail.yandex.net (postback5b.mail.yandex.net [2a02:6b8:c02:900:1:45:d181:da05]) + by v5nxqzqbmwqqbvvs.sas.yp-c.yandex.net (notsolitesrv/Yandex) with LMTPS id VO02EiIuvKy0-AJ4vJzMz + for ; Tue, 13 Aug 2024 16:50:31 +0300 +Received: from mail-nwsmtp-yaback-production-main-40.myt.yp-c.yandex.net (mail-nwsmtp-yaback-production-main-40.myt.yp-c.yandex.net [IPv6:2a02:6b8:c12:4a20:0:640:e2e3:0]) + by postback5b.mail.yandex.net (Yandex) with ESMTPS id 7AF9A60021 + for ; Tue, 13 Aug 2024 16:50:31 +0300 (MSK) +Received: from calendar-public-production-worker-69.sas.yp-c.yandex.net (calendar-public-production-worker-69.sas.yp-c.yandex.net [2a02:6b8:c37:7429:0:4601:454c:0]) + by mail-nwsmtp-yaback-production-main-40.myt.yp-c.yandex.net (yaback/Yandex) with ESMTP id VojWVD72OKo0-lzOxjGlf; + Tue, 13 Aug 2024 16:50:31 +0300 +X-Yandex-Fwd: 1 +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=calendar.yandex.ru; + s=mail; t=1723557031; + bh=CEyW8sOohhXuv5RbVsE81QgjCrs4IWyKm0jOoFJ9Jrc=; + h=Date:Subject:Message-Id:Reply-To:To:From; + b=rdu2g963MOecGWqlshWEJIWKZYYE4Ox48A0s5YlB5qOYeEcdLBbsiSLCxPhhbmxvj + CNnnY/LRwYDdh/jEeaj0CGNgHTVTyf9yYk8+P7vsjM/bvndCnd52fUnMdvrta9tZz7 + LgmfeJxcUn0CamA59w+Q0/RISVasoh/MmfZ/avEU= +Authentication-Results: mail-nwsmtp-yaback-production-main-40.myt.yp-c.yandex.net; dkim=pass header.i=@calendar.yandex.ru +X-Yandex-Spam: 1 +Date: Tue, 13 Aug 2024 13:50:30 +0000 (UTC) +From: =?utf-8?B?0J/QvtC/0L7QsiDQmNCy0LDQvQ==?= +Reply-To: =?utf-8?B?0J/QvtC/0L7QsiDQmNCy0LDQvQ==?= +To: ipopov@yandex.ru +Message-Id: +Subject: TestYandex +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_Part_3583_1813202275.1723557031026" +X-Mailer: Yandex Calendar +X-Calendar-Env: production +X-Calendar-Domain: public +X-Calendar-Request-Id: 1723557030568169-4803765656386115486_calendar-public-production-web-core-66.vla/ee4061b8-54ff-48f0-b816-3bef329be30d_calendar-public-production-worker-69.sas +X-Calendar-Server-Host: calendar-public-production-worker-69.sas.yp-c.yandex.net +X-Calendar-Action-Source: WEB_MAYA +X-Calendar-Mail-Type: event_invitation +Return-Path: info@calendar.yandex.ru +X-Yandex-Forward: dee54417419565c4dc3fb71ed671183a + +------=_Part_3583_1813202275.1723557031026 +Content-Type: multipart/alternative; + boundary="----=_Part_3584_696930174.1723557031026" + +------=_Part_3584_696930174.1723557031026 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +=C2=ABTestYandex=C2=BB +=D0=A1=D0=B5=D0=B3=D0=BE=D0=B4=D0=BD=D1=8F, 13-=D0=B3=D0=BE =D0=B0=D0=B2=D0= +=B3=D1=83=D1=81=D1=82=D0=B0, =D1=81 17:00 =D0=B4=D0=BE 17:30 (Europe/Moscow= +, GMT+03:00) + +=D0=9F=D0=BE=D0=B9=D0=B4=D1=83:=C2=A0https://calendar.yandex.ru/api/add-eve= +nt-invitation?uid=3D791336428&private_token=3D5d50b0903d12264dd54c44b88a92a= +cc9cf62d4a3&event_id=3D2182739972&decision=3Dyes&from_mail=3D1 +=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE,=C2=A0=D0=BF=D0=BE=D0=B9= +=D0=B4=D1=83:=C2=A0https://calendar.yandex.ru/api/add-event-invitation?uid= +=3D791336428&private_token=3D5d50b0903d12264dd54c44b88a92acc9cf62d4a3&event= +_id=3D2182739972&decision=3Dmaybe&from_mail=3D1 +=D0=9D=D0=B5=C2=A0=D0=BF=D0=BE=D0=B9=D0=B4=D1=83:=C2=A0https://calendar.yan= +dex.ru/api/add-event-invitation?uid=3D791336428&private_token=3D5d50b0903d1= +2264dd54c44b88a92acc9cf62d4a3&event_id=3D2182739972&decision=3Dno + + +=D0=9E=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=82=D0=BE=D1=80:=C2=A0ip= +opov@citc.ru +=D0=9F=D1=80=D0=B8=D0=B3=D0=BB=D0=B0=D1=88=D0=B5=D0=BD=D1=8B: + +=D0=9F=D0=BE=D1=81=D0=BC=D0=BE=D1=82=D1=80=D0=B5=D1=82=D1=8C, =D0=BA=D1=82= +=D0=BE =D0=BF=D0=BE=D0=B9=D0=B4=D1=91=D1=82: +https://calendar.yandex.ru/event/?uid=3D791336428&private_token=3D5d50b0903= +d12264dd54c44b88a92acc9cf62d4a3&event_id=3D2182739972&show_date=3D2024-08-1= +3 + +--=20 +=D0=A3=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5 =D0=BE= +=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE =D1=87=D0=B5=D1=80= +=D0=B5=D0=B7=C2=A0=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA=D1=81.=D0=9A=D0=B0=D0=BB= +=D0=B5=D0=BD=D0=B4=D0=B0=D1=80=D1=8C (https://calendar.yandex.ru?uid=3D7913= +36428).=C2=A0=D0=95=D1=81=D0=BB=D0=B8 =D0=B2=D1=8B =D0=BD=D0=B5 =D1=85=D0= +=BE=D1=82=D0=B8=D1=82=D0=B5 =D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B0=D1=82=D1= +=8C =D1=82=D0=B0=D0=BA=D0=B8=D0=B5 =D0=BF=D0=B8=D1=81=D1=8C=D0=BC=D0=B0,=C2= +=A0=D0=BE=D1=82=D0=BA=D0=B0=D0=B6=D0=B8=D1=82=D0=B5=D1=81=D1=8C =D0=BE=D1= +=82 =D0=BD=D0=B8=D1=85 (https://yandex.ru/support/calendar/event-notificati= +on.html#turn-off-notifications).=C2=A0=C2=A0=C2=A9=C2=A02007=E2=80=942024= +=C2=A0=C2=AB=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=C2=BB +------=_Part_3584_696930174.1723557031026 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +
=C2=ABTestYandex= +=C2=BB
=D0=A1=D0=B5=D0=B3=D0=BE=D0=B4=D0=BD=D1=8F, 13-=D0=B3=D0=BE =D0=B0= +=D0=B2=D0=B3=D1=83=D1=81=D1=82=D0=B0, =D1=81 17:00 =D0=B4=D0=BE 17:30 (Euro= +pe/Moscow, GMT+03:00)
=D0=9F= +=D0=BE=D0=B9=D0=B4=D1=83=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0= +=BE,=C2=A0=D0=BF=D0=BE=D0=B9=D0=B4=D1=83=D0=9D=D0=B5=C2=A0=D0=BF=D0=BE=D0=B9=D0=B4=D1=83
=D0=9E=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=82=D0=BE=D1= +=80ipopov@citc.ru
=D0=9F=D1=80=D0=B8=D0= +=B3=D0=BB=D0=B0=D1=88=D0=B5=D0=BD=D1=8Bskyvv1sp@yandex.ru
=D0= +=9F=D0=BE=D1=81=D0=BC=D0=BE=D1=82=D1=80=D0=B5=D1=82=D1=8C, =D0=BA=D1=82=D0= +=BE =D0=BF=D0=BE=D0=B9=D0=B4=D1=91=D1=82

=D0=A3=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0= +=BD=D0=B8=D0=B5 =D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0= +=BE =D1=87=D0=B5=D1=80=D0=B5=D0=B7=C2=A0=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA= +=D1=81.=D0=9A=D0=B0=D0=BB=D0=B5=D0=BD=D0=B4=D0=B0=D1=80=D1=8C.=C2=A0=D0= +=95=D1=81=D0=BB=D0=B8 =D0=B2=D1=8B =D0=BD=D0=B5 =D1=85=D0=BE=D1=82=D0=B8=D1= +=82=D0=B5 =D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B0=D1=82=D1=8C =D1=82=D0=B0=D0= +=BA=D0=B8=D0=B5 =D0=BF=D0=B8=D1=81=D1=8C=D0=BC=D0=B0,=C2=A0=D0=BE=D1=82=D0=BA=D0=B0=D0=B6=D0=B8=D1=82=D0= +=B5=D1=81=D1=8C =D0=BE=D1=82 =D0=BD=D0=B8=D1=85.=C2=A0=C2=A0=C2=A9=C2= +=A02007=E2=80=942024=C2=A0=C2=AB=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=C2=BB +------=_Part_3584_696930174.1723557031026 +Content-Type: text/calendar; charset=utf-8; +Content-Transfer-Encoding: quoted-printable + +BEGIN:VCALENDAR +PRODID:-//Yandex LLC//Yandex Calendar//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REQUEST +X-YANDEX_MAIL_TYPE:event_invitation +BEGIN:VTIMEZONE +TZID:Europe/Volgograd +LAST-MODIFIED:20231222T233358Z +TZURL:https://www.tzurl.org/zoneinfo-outlook/Europe/Volgograd +X-LIC-LOCATION:Europe/Volgograd +BEGIN:STANDARD +TZNAME:MSK +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=3DEurope/Volgograd:20240813T170000 +DTEND;TZID=3DEurope/Volgograd:20240813T173000 +SUMMARY:TestYandex +UID:141zhi60x8914s7bzxzq27i0syandex.ru +SEQUENCE:0 +DTSTAMP:20240813T135030Z +CREATED:20240813T135030Z +LOCATION: +DESCRIPTION: +URL:https://calendar.yandex.ru/event?event_id=3D2182739972 +TRANSP:OPAQUE +CATEGORIES:=D0=9C=D0=BE=D0=B8 =D1=81=D0=BE=D0=B1=D1=8B=D1=82=D0=B8=D1=8F +ORGANIZER;CN=3Dipopov:mailto:ipopov@citc.ru +LAST-MODIFIED:20240813T135030Z +CLASS:PRIVATE +ATTENDEE;PARTSTAT=3DACCEPTED;CN=3Dipopov;ROLE=3DREQ-PARTICIPANT:mailto:ipop= +ov@citc.ru +ATTENDEE;PARTSTAT=3DNEEDS-ACTION;CN=3D"=D0=9F=D0=BE=D0=BF=D0=BE=D0=B2 =D0= +=98=D0=B2=D0=B0=D0=BD";ROLE=3DREQ-PARTICIPANT:mailto:skyvv1sp@yandex.ru +END:VEVENT +END:VCALENDAR + +------=_Part_3584_696930174.1723557031026-- + +------=_Part_3583_1813202275.1723557031026 +Content-Type: application/ics; charset=utf-8; name=event.ics +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=event.ics + +QkVHSU46VkNBTEVOREFSDQpQUk9ESUQ6LS8vWWFuZGV4IExMQy8vWWFuZGV4IENhbGVuZGFyLy9F +Tg0KVkVSU0lPTjoyLjANCkNBTFNDQUxFOkdSRUdPUklBTg0KTUVUSE9EOlJFUVVFU1QNClgtWUFO +REVYX01BSUxfVFlQRTpldmVudF9pbnZpdGF0aW9uDQpCRUdJTjpWVElNRVpPTkUNClRaSUQ6RXVy +b3BlL1ZvbGdvZ3JhZA0KTEFTVC1NT0RJRklFRDoyMDIzMTIyMlQyMzMzNThaDQpUWlVSTDpodHRw +czovL3d3dy50enVybC5vcmcvem9uZWluZm8tb3V0bG9vay9FdXJvcGUvVm9sZ29ncmFkDQpYLUxJ +Qy1MT0NBVElPTjpFdXJvcGUvVm9sZ29ncmFkDQpCRUdJTjpTVEFOREFSRA0KVFpOQU1FOk1TSw0K +VFpPRkZTRVRGUk9NOiswMzAwDQpUWk9GRlNFVFRPOiswMzAwDQpEVFNUQVJUOjE5NzAwMTAxVDAw +MDAwMA0KRU5EOlNUQU5EQVJEDQpFTkQ6VlRJTUVaT05FDQpCRUdJTjpWRVZFTlQNCkRUU1RBUlQ7 +VFpJRD1FdXJvcGUvVm9sZ29ncmFkOjIwMjQwODEzVDE3MDAwMA0KRFRFTkQ7VFpJRD1FdXJvcGUv +Vm9sZ29ncmFkOjIwMjQwODEzVDE3MzAwMA0KU1VNTUFSWTpUZXN0WWFuZGV4DQpVSUQ6MTQxemhp +NjB4ODkxNHM3Ynp4enEyN2kwc3lhbmRleC5ydQ0KU0VRVUVOQ0U6MA0KRFRTVEFNUDoyMDI0MDgx +M1QxMzUwMzBaDQpDUkVBVEVEOjIwMjQwODEzVDEzNTAzMFoNCkxPQ0FUSU9OOg0KREVTQ1JJUFRJ +T046DQpVUkw6aHR0cHM6Ly9jYWxlbmRhci55YW5kZXgucnUvZXZlbnQ/ZXZlbnRfaWQ9MjE4Mjcz +OTk3Mg0KVFJBTlNQOk9QQVFVRQ0KQ0FURUdPUklFUzrQnNC+0Lgg0YHQvtCx0YvRgtC40Y8NCk9S +R0FOSVpFUjtDTj1pcG9wb3Y6bWFpbHRvOmlwb3BvdkBjaXRjLnJ1DQpMQVNULU1PRElGSUVEOjIw +MjQwODEzVDEzNTAzMFoNCkNMQVNTOlBSSVZBVEUNCkFUVEVOREVFO1BBUlRTVEFUPUFDQ0VQVEVE +O0NOPWlwb3BvdjtST0xFPVJFUS1QQVJUSUNJUEFOVDptYWlsdG86aXBvcG92QGNpdGMucnUNCkFU +VEVOREVFO1BBUlRTVEFUPU5FRURTLUFDVElPTjtDTj0i0J/QvtC/0L7QsiDQmNCy0LDQvSI7Uk9M +RT1SRVEtUEFSVElDSVBBTlQ6bWFpbHRvOnNreXZ2MXNwQHlhbmRleC5ydQ0KRU5EOlZFVkVOVA0K +RU5EOlZDQUxFTkRBUg0K +------=_Part_3583_1813202275.1723557031026-- \ No newline at end of file