-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6c3d4fd
commit 7dccbe2
Showing
11 changed files
with
625 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Miscellaneous | ||
*.class | ||
*.log | ||
*.pyc | ||
*.swp | ||
.DS_Store | ||
.atom/ | ||
.buildlog/ | ||
.history | ||
.svn/ | ||
|
||
# IntelliJ related | ||
*.iml | ||
*.ipr | ||
*.iws | ||
.idea/ | ||
|
||
# The .vscode folder contains launch configuration and tasks you configure in | ||
# VS Code which you may wish to be included in version control, so this line | ||
# is commented out by default. | ||
#.vscode/ | ||
|
||
# Flutter/Dart/Pub related | ||
**/doc/api/ | ||
.dart_tool/ | ||
.flutter-plugins | ||
.flutter-plugins-dependencies | ||
.packages | ||
.pub-cache/ | ||
.pub/ | ||
build/ | ||
pubspec.lock | ||
|
||
# Android related | ||
**/android/**/gradle-wrapper.jar | ||
**/android/.gradle | ||
**/android/captures/ | ||
**/android/gradlew | ||
**/android/gradlew.bat | ||
**/android/local.properties | ||
**/android/**/GeneratedPluginRegistrant.java | ||
|
||
# iOS/XCode related | ||
**/ios/**/*.mode1v3 | ||
**/ios/**/*.mode2v3 | ||
**/ios/**/*.moved-aside | ||
**/ios/**/*.pbxuser | ||
**/ios/**/*.perspectivev3 | ||
**/ios/**/*sync/ | ||
**/ios/**/.sconsign.dblite | ||
**/ios/**/.tags* | ||
**/ios/**/.vagrant/ | ||
**/ios/**/DerivedData/ | ||
**/ios/**/Icon? | ||
**/ios/**/Pods/ | ||
**/ios/**/.symlinks/ | ||
**/ios/**/profile | ||
**/ios/**/xcuserdata | ||
**/ios/.generated/ | ||
**/ios/Flutter/App.framework | ||
**/ios/Flutter/Flutter.framework | ||
**/ios/Flutter/Flutter.podspec | ||
**/ios/Flutter/Generated.xcconfig | ||
**/ios/Flutter/ephemeral | ||
**/ios/Flutter/app.flx | ||
**/ios/Flutter/app.zip | ||
**/ios/Flutter/flutter_assets/ | ||
**/ios/Flutter/flutter_export_environment.sh | ||
**/ios/ServiceDefinitions.json | ||
**/ios/Runner/GeneratedPluginRegistrant.* | ||
|
||
# Exceptions to above rules. | ||
!**/ios/**/default.mode1v3 | ||
!**/ios/**/default.mode2v3 | ||
!**/ios/**/default.pbxuser | ||
!**/ios/**/default.perspectivev3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# This file tracks properties of this Flutter project. | ||
# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
# | ||
# This file should be version controlled and should not be manually edited. | ||
|
||
version: | ||
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03 | ||
channel: stable | ||
|
||
project_type: package |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
## 0.1.0 | ||
* Initial release with iMIP support. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,105 @@ | ||
# enough_mail_icalendar | ||
iCalendar support for email / mime. Compatible with iCalendar Message-Based Interoperability Protocol (iMIP) RFC 6047. | ||
iCalendar support for email / mime. Compatible with the iCalendar Message-Based Interoperability Protocol (iMIP) [RFC 6047](https://datatracker.ietf.org/doc/html/rfc6047). | ||
|
||
## Installation | ||
Add this dependency your pubspec.yaml file: | ||
|
||
``` | ||
dependencies: | ||
enough_mail_icalendar: ^0.1.0 | ||
enough_mail: latest | ||
enough_icalendar: latest | ||
``` | ||
The latest version or `enough_mail_icalendar` is [![enough_mail_icalendar version](https://img.shields.io/pub/v/enough_mail_icalendar.svg)](https://pub.dartlang.org/packages/enough_mail_icalendar). | ||
|
||
|
||
|
||
## API Documentation | ||
Check out the full API documentation at https://pub.dev/documentation/enough_mail_icalendar/latest/ | ||
|
||
## Usage | ||
|
||
Use `enough_mail_icalendar` to generate and send MIME email messages for iCalendar requests. | ||
|
||
### Import | ||
|
||
```dart | ||
import 'package:enough_icalendar/enough_icalendar.dart'; | ||
import 'package:enough_mail/enough_mail.dart'; | ||
import 'package:enough_mail_icalendar/enough_mail_icalendar.dart'; | ||
``` | ||
### Generate a MimeMessage for a VCalendar | ||
With `VMessageBuilder.prepareFromCalendar(...)` create a MIME message builder for a given `VCalendar` object. | ||
|
||
```dart | ||
void buildCalendarInviteMessage(VCalendar invite) { | ||
final builder = VMessageBuilder.prepareFromCalendar(invite); | ||
final mimeMessage = builder.buildMimeMessage(); | ||
print(mimeMessage); | ||
// you can now send the MimeMessage as any other message, e.g. with `MailClient.sendMessage(mimeMessage)` | ||
} | ||
``` | ||
### Generate a Reply MimeMessage for a Received VCalendar | ||
Use `VMessageBuilder.prepareCalendarReply(...)` to create a reply MIME message for a received VCalendar. | ||
In the following example the invite is accepted. | ||
```dart | ||
void buildAcceptReplyMessage(VCalendar invite) { | ||
final me = MailAddress('Donna Strickland', '[email protected]'); | ||
final acceptMessageBuilder = VMessageBuilder.prepareCalendarReply( | ||
invite, | ||
ParticipantStatus.accepted, | ||
me, | ||
); | ||
final mimeMessage = acceptMessageBuilder.buildMimeMessage(); | ||
print(mimeMessage); | ||
} | ||
``` | ||
### Send a Reply directly for a Received VCalendar | ||
Send a reply directly with the `MailClient.sendCalendarReply()` instance method. This will generate the | ||
mime message, send it and update the originating message's flags, when the message is specified and when the | ||
mail service supports arbitrary message flags. | ||
```dart | ||
Future sendCalendarReply( | ||
VCalendar calendar, | ||
ParticipantStatus participantStatus, | ||
MimeMessage originatingMessage, | ||
MailClient mailClient, | ||
) { | ||
// generate reply email message, send it, set message flags: | ||
return mailClient.sendCalendarReply(calendar, participantStatus, | ||
originatingMessage: originatingMessage); | ||
} | ||
``` | ||
|
||
### Check if a Reply has been Send for a MimeMessage | ||
Use the `calendarParticipantStatus` getter on a `MimeMessage` instance to check for participation status flags that have been set earlier. | ||
```dart | ||
ParticipantStatus? getParticipantStatus(MimeMessage message) { | ||
// the ParticipantStatus can be detected from the message flags when | ||
//the flag was added successfully before | ||
final participantStatus = message.calendarParticipantStatus; | ||
if (participantStatus != null) { | ||
print( | ||
'detected ${participantStatus.name} through flag ${participantStatus.flag}'); | ||
} else { | ||
print('no participant status flag detected in ${message.flags}'); | ||
} | ||
return participantStatus; | ||
} | ||
``` | ||
|
||
## Features and bugs | ||
|
||
`enough_mail_icalendar` should be fully compliant with the iCalendar Message-Based Interoperability Protocol (iMIP) [RFC 6047](https://datatracker.ietf.org/doc/html/rfc6047). | ||
|
||
|
||
Please file feature requests and bugs at the [issue tracker][tracker]. | ||
|
||
[tracker]: https://github.com/Enough-Software/enough_mail_icalendar/issues | ||
|
||
## Null-Safety | ||
`enough_mail_icalendar` is null-safe. | ||
|
||
## License | ||
`enough_mail_icalendar` is licensed under the commercial friendly [Mozilla Public License 2.0](LICENSE) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package:lints/recommended.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import 'package:enough_icalendar/enough_icalendar.dart'; | ||
import 'package:enough_mail/enough_mail.dart'; | ||
import 'package:enough_mail_icalendar/enough_mail_icalendar.dart'; | ||
|
||
void main() { | ||
final invite = createInvite(); | ||
buildCalendarInviteMessage(invite); | ||
buildAcceptReplyMessage(invite); | ||
} | ||
|
||
void buildCalendarInviteMessage(VCalendar invite) { | ||
final builder = VMessageBuilder.prepareFromCalendar(invite); | ||
final mimeMessage = builder.buildMimeMessage(); | ||
print('=========================='); | ||
print('invite message:'); | ||
print('=========================='); | ||
print(mimeMessage); | ||
// you can now send the MimeMessage as any other message, e.g. with `MailClient.sendMessage(mimeMessage)` | ||
} | ||
|
||
void buildAcceptReplyMessage(VCalendar invite) { | ||
final me = MailAddress('Donna Strickland', '[email protected]'); | ||
final acceptMessageBuilder = VMessageBuilder.prepareCalendarReply( | ||
invite, | ||
ParticipantStatus.accepted, | ||
me, | ||
); | ||
final mimeMessage = acceptMessageBuilder.buildMimeMessage(); | ||
print('\n=========================='); | ||
print('reply message:'); | ||
print('=========================='); | ||
print(mimeMessage); | ||
} | ||
|
||
Future sendCalendarReply( | ||
VCalendar calendar, | ||
ParticipantStatus participantStatus, | ||
MimeMessage originatingMessage, | ||
MailClient mailClient, | ||
) { | ||
// generate reply email message, send it, set message flags: | ||
return mailClient.sendCalendarReply(calendar, participantStatus, | ||
originatingMessage: originatingMessage); | ||
} | ||
|
||
ParticipantStatus? getParticipantStatus(MimeMessage message) { | ||
// the ParticipantStatus can be detected from the message flags when | ||
//the flag was added successfully before | ||
final participantStatus = message.calendarParticipantStatus; | ||
if (participantStatus != null) { | ||
print( | ||
'detected ${participantStatus.name} through flag ${participantStatus.flag}'); | ||
} else { | ||
print('no participant status flag detected in ${message.flags}'); | ||
} | ||
return participantStatus; | ||
} | ||
|
||
VCalendar createInvite() { | ||
final me = MailAddress('Andrea Ghez', '[email protected]'); | ||
final invitees = [ | ||
MailAddress('Andrea Ghez', '[email protected]'), | ||
MailAddress('Donna Strickland', '[email protected]'), | ||
MailAddress('Maria Goeppert Mayer', '[email protected]'), | ||
MailAddress('Marie Curie, née Sklodowska', '[email protected]'), | ||
]; | ||
final invite = VCalendar.createEvent( | ||
start: DateTime(2021, 08, 01, 11, 00), | ||
duration: IsoDuration(hours: 1), | ||
organizer: me.organizer, | ||
attendees: invitees.map((address) => address.attendee).toList(), | ||
location: 'Stockholm', | ||
summary: 'Physics Winners', | ||
description: 'Let\'s discuss what to research next.', | ||
); | ||
return invite; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/// A library for handling iCalendar invites in email. | ||
/// | ||
/// Compatible with the iCalendar Message-Based Interoperability Protocol (iMIP), [RFC 6047](https://datatracker.ietf.org/doc/html/rfc6047) | ||
library enough_mail_icalendar; | ||
|
||
export 'src/builder.dart'; | ||
export 'src/extensions.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import 'package:enough_icalendar/enough_icalendar.dart'; | ||
import 'package:enough_mail/enough_mail.dart'; | ||
import 'extensions.dart'; | ||
|
||
class VMessageBuilder { | ||
VMessageBuilder._(); | ||
|
||
/// Prepares a message builder with a request for the specified [calendar]. | ||
static MessageBuilder prepareFromCalendar( | ||
VCalendar calendar, { | ||
List<MailAddress> toRecipients = const [], | ||
List<MailAddress> ccRecipients = const [], | ||
List<MailAddress> bccRecipients = const [], | ||
String? plaintTextPart, | ||
MailAddress? from, | ||
String filename = 'invite.ics', | ||
String? subject, | ||
}) { | ||
final organizer = calendar.organizer; | ||
if (from == null && (organizer == null || organizer.email == null)) { | ||
throw StateError( | ||
'Either the [from] parameter must be set or the calendar requires a child with an [organizer] set.'); | ||
} | ||
|
||
final builder = MessageBuilder.prepareMultipartMixedMessage(); | ||
builder.subject = subject ?? calendar.summary ?? 'Invite'; | ||
final fromSender = from ?? calendar.organizerMailAddress!; | ||
builder.from = [fromSender]; | ||
if (toRecipients.isEmpty && ccRecipients.isEmpty && bccRecipients.isEmpty) { | ||
final attendees = calendar.attendees; | ||
if (attendees == null || attendees.isEmpty) { | ||
throw StateError( | ||
'Warning: neither recipients specified nor attendees found in calendar'); | ||
} | ||
builder.to = calendar.attendeeMailAddresses; | ||
} else { | ||
builder.to = toRecipients; | ||
builder.cc = ccRecipients; | ||
builder.bcc = bccRecipients; | ||
} | ||
final text = plaintTextPart ?? | ||
calendar.description ?? | ||
calendar.summary ?? | ||
'This message contains an calendar invite'; | ||
builder.addTextPlain(text); | ||
final calendarPart = builder.addText( | ||
calendar.toString(), | ||
mediaType: MediaSubtype.textCalendar.mediaType, | ||
disposition: ContentDispositionHeader.from( | ||
ContentDisposition.attachment, | ||
filename: filename, | ||
), | ||
); | ||
if (calendar.method != null) { | ||
final contentType = calendarPart.contentType!; | ||
contentType.parameters['method'] = calendar.method!.name; | ||
} | ||
return builder; | ||
} | ||
|
||
static MessageBuilder prepareCalendarReply( | ||
VCalendar calendar, | ||
ParticipantStatus participantStatus, | ||
MailAddress from, { | ||
String? comment, | ||
String productId = 'enough_mail with enough_icalendar', | ||
String icsFilename = 'reply.ics', | ||
}) { | ||
final organizer = calendar.organizer; | ||
if (organizer == null || organizer.email == null) { | ||
throw StateError( | ||
'VCALENDAR has no organizer or the organizer has no email: $organizer'); | ||
} | ||
final reply = calendar.replyWithParticipantStatus( | ||
participantStatus, | ||
attendeeEmail: from.email, | ||
comment: comment, | ||
productId: productId, | ||
); | ||
final subject = | ||
MessageBuilder.createReplySubject(calendar.summary ?? 'Invite'); | ||
final messageBuilder = prepareFromCalendar( | ||
reply, | ||
from: from, | ||
toRecipients: [organizer.mailAddress!], | ||
filename: icsFilename, | ||
plaintTextPart: comment ?? | ||
'"${calendar.summary}" is ${participantStatus.name} by $from.', | ||
subject: subject, | ||
); | ||
return messageBuilder; | ||
} | ||
} |
Oops, something went wrong.