Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions Documentation/Assets/UML Diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Documentation/Classes/Office365.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ The `event` object used with Microsoft Calendar methods includes the following m
| hideAttendees | | Boolean | (Default: false) If `true`, attendees will only see themselves.|Yes|

## Mail

### Office365.mail.append()

**Office365.mail.append**( *email* : Object ; *folderId* : Text) : Object
Expand Down
23 changes: 19 additions & 4 deletions Project/Sources/Classes/Office365Mail.4dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ Class constructor($inProvider : cs.OAuth2Provider; $inParameters : Object)
// ----------------------------------------------------


Function _postJSONMessage($inURL : Text; $inMail : Object; $bSkipMessageEncapsulation : Boolean; $inHeaders : Object) : Object
Function _postJSONMessage($inFunction : Text; $inURL : Text; $inMail : Object; $bSkipMessageEncapsulation : Boolean; $inHeaders : Object) : Object
/**
* @function _postJSONMessage
* @private
* @param {Text} $inFunction - Caller name for error reporting (e.g. `"office365.mail.send"`)
* @param {Text} $inURL - Target Graph API endpoint URL
* @param {Object} $inMail - Mail object in Microsoft Graph JSON format
* @param {Boolean} $bSkipMessageEncapsulation - When `True`, sends `$inMail` as-is;
Expand All @@ -54,6 +55,10 @@ Function _postJSONMessage($inURL : Text; $inMail : Object; $bSkipMessageEncapsul
End if
$headers["Content-Type"]:="application/json"

If (Not(This._validateGraphMessageProperties($inMail; $inFunction)))
return This._returnStatus()
End if

var $message : Object
var $messageCopy : Object:=This._copyGraphMessage($inMail)
If (Not(OB Is defined($inMail; "message")) && Not($bSkipMessageEncapsulation))
Expand Down Expand Up @@ -128,7 +133,9 @@ Function _postMessage($inFunction : Text; $inURL : Text; $inMail : Variant; $bSk
* @param {Object} $inHeader - Additional HTTP headers forwarded to `_postJSONMessage`
* @returns {Object} Status object
* @description Dispatches to `_postJSONMessage` or `_postMailMIMEMessage` based on `mailType`
* and the actual type of `$inMail`; throws error 10 on type mismatch
* and the actual type of `$inMail`; throws error 10 on type mismatch.
* JMAP objects are validated via `_validateJMAPMessageProperties` before conversion;
* Microsoft Graph objects are validated via `_validateGraphMessageProperties`.
*/

var $status : Object
Expand All @@ -146,10 +153,14 @@ Function _postMessage($inFunction : Text; $inURL : Text; $inMail : Variant; $bSk
$status:=This._postMailMIMEMessage($inURL; $inMail)

: ((This.mailType="JMAP") && (Value type($inMail)=Is object))
$status:=This._postMailMIMEMessage($inURL; $inMail)
If (This._validateJMAPMessageProperties($inMail; $inFunction))
$status:=This._postMailMIMEMessage($inURL; $inMail)
Else
$status:=This._returnStatus()
End if

: ((This.mailType="Microsoft") && (Value type($inMail)=Is object))
$status:=This._postJSONMessage($inURL; $inMail; $bSkipMessageEncapsulation; $inHeader)
$status:=This._postJSONMessage($inFunction; $inURL; $inMail; $bSkipMessageEncapsulation; $inHeader)

Else
Super._throwError(10; {which: 1; function: $inFunction})
Expand Down Expand Up @@ -542,6 +553,10 @@ Function update($inMailId : Text; $inMail : Object) : Object

If ((Type($inMail)=Is object) && (Type($inMailId)=Is text) && (Length(String($inMailId))>0))

If (Not(This._validateGraphMessageProperties($inMail; "office365.mail.update")))
return This._returnStatus()
End if

var $response : Object
var $URL : Text:=Super._getURL()

Expand Down
101 changes: 101 additions & 0 deletions Project/Sources/Classes/_GraphAPI.4dm
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,107 @@ Function _copyGraphMessage($inMessage : Object) : Object
// ----------------------------------------------------


Function _validateGraphMessageProperties($inPayload : Object; $inFunction : Text) : Boolean
/**
* @function _validateGraphMessageProperties
* @private
* @param {Object} $inPayload - Graph message object or request envelope (`{message: ...}`)
* @param {Text} $inFunction - Caller function name for error reporting
* @returns {Boolean} `True` when all properties are supported; otherwise `False`
* @description Validates mail payload keys before sending to Microsoft Graph.
* Adds an error to the stack for each unsupported property (for example `attachment`
* instead of `attachments`). Uses a Collection-based lookup for O(1)-style
* maintainability and readability over a `Case of` chain.
*/

var $isValid : Boolean:=True

If (($inPayload#Null) && (Value type($inPayload)=Is object))

var $message : Object
var $allowedPayloadKeys : Object:={message: True; saveToSentItems: True; comment: True}
If (OB Is defined($inPayload; "message") && (Value type($inPayload.message)=Is object))
$message:=$inPayload.message

var $payloadKey : Text
For each ($payloadKey; OB Keys($inPayload))
If (Not(OB Is defined($allowedPayloadKeys; $payloadKey)))
This._pushError(12; {function: $inFunction; message: "Unsupported property \""+$payloadKey+"\" in mail payload."})
$isValid:=False
End if
End for each
Else
$message:=$inPayload
End if

var $allowedMessageKeys : Collection:=New collection(\
"attachments"; "bccRecipients"; "body"; "bodyPreview"; \
"categories"; "ccRecipients"; "changeKey"; "conversationId"; \
"conversationIndex"; "createdDateTime"; "extensions"; "flag"; \
"from"; "hasAttachments"; "id"; "importance"; "inferenceClassification"; \
"internetMessageHeaders"; "internetMessageId"; "isDeliveryReceiptRequested"; \
"isDraft"; "isRead"; "isReadReceiptRequested"; "lastModifiedDateTime"; \
"multiValueExtendedProperties"; "parentFolderId"; "receivedDateTime"; \
"replyTo"; "sender"; "sentDateTime"; "singleValueExtendedProperties"; \
"subject"; "toRecipients"; "uniqueBody"; "webLink")

var $messageKey : Text
For each ($messageKey; OB Keys($message))
If ($allowedMessageKeys.indexOf($messageKey)<0)
This._pushError(12; {function: $inFunction; message: "Unsupported property \""+$messageKey+"\" in mail object."})
$isValid:=False
End if
End for each

End if

return $isValid


// ----------------------------------------------------


Function _validateJMAPMessageProperties($inMail : Object; $inFunction : Text) : Boolean
/**
* @function _validateJMAPMessageProperties
* @private
* @param {Object} $inMail - 4D JMAP email object (as produced by `MAIL Convert from MIME`)
* @param {Text} $inFunction - Caller function name for error reporting
* @returns {Boolean} `True` when all properties are valid RFC 8621 / 4D JMAP properties;
* otherwise `False`
* @description Validates a JMAP mail object against the set of properties supported by
* 4D's `MAIL Convert to MIME` command (RFC 8621). Adds an error to the stack for each
* unsupported property.
*/

var $isValid : Boolean:=True

If (($inMail#Null) && (Value type($inMail)=Is object))

var $allowedKeys : Collection:=New collection(\
"from"; "to"; "cc"; "bcc"; "replyTo"; "sender"; \
"subject"; "sentAt"; "receivedAt"; \
"textBody"; "htmlBody"; "bodyValues"; \
"attachments"; "keywords"; "headers"; \
"messageId"; "inReplyTo"; "references"; \
"id"; "threadId"; "preview"; "size"; "hasAttachment"; "mailboxIds")

var $key : Text
For each ($key; OB Keys($inMail))
If ($allowedKeys.indexOf($key)<0)
This._pushError(12; {function: $inFunction; message: "Unsupported property \""+$key+"\" in JMAP mail object."})
$isValid:=False
End if
End for each

End if

return $isValid


// ----------------------------------------------------


Function _loadFromObject($inObject : Object)
/**
* @function _loadFromObject
Expand Down
Loading