Jump to content

mvanrijnen

Members
  • Content Count

    477
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by mvanrijnen

  1. Also the method of inline sending the attachments is limited to a size ( i believe 3MB), my code (i'm in a hurry, so just a dump), you can get the idea how to from this. const CNST_RESOURCE_CREATEUPLOADSESSION = 'users/{sendasemailaddress}/messages/{messageid}/attachments/createUploadSession'; CNST_RESOURCE_CREATEDRAFTMESSAGE = 'users/{sendasemailaddress}/mailFolders/drafts/messages'; CNST_RESOURCE_SENDCREATEDRAFT = 'users/{sendasemailaddress}/messages/{messageid}/send'; function THSMSGraphAPI.FetchUploadURL(const AUploadSize: integer; const AFileName, AMessageID, AContentID, AContentType : string; const AIsInline : Boolean): string; var fuploadRequest : TRESTRequest; fuploadResponse : TMSGraphUploadSessionResponse; attreq : TMSGraphUploadSessionRequest; begin Result := ''; fuploadRequest := TRESTRequest.Create(nil); try fuploadRequest.Client := FRestClient; fuploadRequest.Method := TRESTRequestMethod.rmPOST; fuploadRequest.Resource := CNST_RESOURCE_CREATEUPLOADSESSION.Replace('{sendasemailaddress}', AccountEmailAddress, [rfReplaceAll, rfIgnoreCase]).Replace('{messageid}', AMessageID, [rfReplaceAll, rfIgnoreCase]); attreq.AttachmentItem.attachmentType := 'file'; attreq.AttachmentItem.name := ExtractFileName(AFileName); attreq.AttachmentItem.size := AUploadSize; attreq.AttachmentItem.isInline := (AIsInline) or (not AContentID.IsEmpty); attreq.AttachmentItem.contentID := AContentID; attreq.AttachmentItem.contentType := AContentType; fuploadRequest.AddBody(attreq.AsJSON, TRESTContentType.ctAPPLICATION_JSON); fuploadRequest.Execute(); if not LogInvalidResponse(fuploadRequest.Response, True, 'THSMSGraphAPI.FetchUploadURL') then begin fuploadResponse := TMSGraphUploadSessionResponse.FromJSON(fuploadRequest.Response.Content); Result := fuploadResponse.uploadUrl; end; finally fuploadRequest.Free; end; end; procedure THSMSGraphAPI.AddLargeAttachments(const AEmailMessageAttachments : TMvREMailAttachments; const AMessageID : string); function CreateSourceStream(const AAttachment : TMvREMailAttachment) : TStream; begin Result := nil; if AAttachment.IsFile then Result := TFileStream.Create(AAttachment.FileName, fmOpenRead or fmShareDenyWrite) else Result := TStringStream.Create(AAttachment.PayLoad); end; procedure UploadParts(const AUploadUrl : string; const ASourceStream : TStream; const AContentType : string); var memStream : TMemoryStream; fuploadPartRequest : TRESTRequest; hdr : string; size, done, todo : integer; begin fuploadPartRequest := TRESTRequest.Create(nil); memStream := TMemoryStream.Create; try fuploadPartRequest.URLAlreadyEncoded := True; fuploadPartRequest.Client := FUplClient; fuploadPartRequest.Method := TRESTRequestMethod.rmPUT; FUplClient.BaseURL := AUploadUrl; size := ASourceStream.Size; done := 0; todo := 0; ASourceStream.Position := 0; while (done<size) do begin todo := Min(CNST_MAXATTSIZE, size-done); memStream.Clear; ASourceStream.Position := done; memStream.CopyFrom(ASourceStream, todo); memStream.Position := 0; hdr := 'bytes ' + (done).ToString + '-' + (done+todo-1).ToString + '/' + size.ToString; fuploadPartRequest.ClearBody; fuploadPartRequest.Params.Clear; fuploadPartRequest.AddParameter('Content-Length', todo.ToString, TRESTRequestParameterKind.pkHTTPHEADER); fuploadPartRequest.AddParameter('Content-Range', hdr, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); if not AContentType.IsEmpty then fuploadPartRequest.AddParameter('Content-Type', AContentType, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); fuploadPartRequest.AddBody(memStream, TRESTContentType.ctAPPLICATION_OCTET_STREAM); ExecuteRequest(fuploadPartRequest, 'THSMSGraphAPI.AddLargeAttachments.UploadParts', True); LogInvalidResponse(fuploadPartRequest.Response, True, 'UploadParts'); done := done + todo; end; finally memStream.Free; fuploadPartRequest.Free; end; end; var att : TMvREMailAttachment; url : string; stream : TStream; begin for att in AEmailMessageAttachments do //if att.IsLargeAttachment then begin stream := CreateSourceStream(att); try // url := FetchUploadURL(stream.Size, att.FileName, AMessageID); url := FetchUploadURL(stream.Size, att.FileName, AMessageID, att.ContentID, att.ContentType, att.IsInline); UploadParts(url, stream, att.ContentType); finally stream.Free; end; end; end; function THSMSGraphAPI.SendEmailWithLargeAttachments(const AEmailMessage: TMvREMailMessage; AAPIMessage: TMSGraphSendEmailMessageBody): Boolean; function FetchImmutableID(const AID : string) : string; var req : TRESTRequest; request : TMSGraphTranslateExchangeIdsRequest; responseJSON, requestJSON : string; response : TMSGraphTranslateExchangeIdsResponse; begin Result := ''; req := TRESTRequest.Create(nil); try try req.Client := FRestClient; req.Method := TRESTRequestMethod.rmPOST; req.Resource := 'users/{sendasemailaddress}/translateExchangeIds'.Replace('{sendasemailaddress}', AccountEmailAddress, [rfReplaceAll, rfIgnoreCase]); SetLength(request.inputIds, 1); request.inputIds[0] := AID; request.targetIdType := 'restImmutableEntryId'; request.sourceIdType := 'restId'; TgoBsonSerializer.Serialize( request, requestJSON); req.AddBody(requestJSON, TRESTContentType.ctAPPLICATION_JSON); req.Execute(); responseJSON := req.Response.Content; TgoBsonSerializer.Deserialize(responseJSON, response); if length(response.value)>0 then Result := response.value[0].targetId; except on e: exception do begin Result := ''; raise Exception.CreateFmt('[THSMSGraphAPI.SendEmailWithLargeAttachments.FetchImmutableId]: %s\n%s', [e.ClassName, e.Message]); end; end; finally req.Free; end; end; function FetchMessagesForInternetMessageID(const AInternetMessageID : string) : TArray<string>; var req : TRESTRequest; request : TMSGraphTranslateExchangeIdsRequest; responseJSON, requestJSON : string; response : TMSGraphTranslateExchangeIdsResponse; begin SetLength(Result, 0); req := TRESTRequest.Create(nil); try try req.Client := FRestClient; req.Method := TRESTRequestMethod.rmGET; req.Resource := 'users/{sendasemailaddress}/messages?$filter=internetMessageId eq ''{internetmessageid}''' .Replace('{sendasemailaddress}', AccountEmailAddress, [rfReplaceAll, rfIgnoreCase]) .Replace('{internetmessageid}', TidUri.ParamsEncode(AInternetMessageID), [rfReplaceAll, rfIgnoreCase]); req.Execute(); except on e: exception do begin raise Exception.CreateFmt('[THSMSGraphAPI.SendEmailWithLargeAttachments.FetchMessagesForInternetMessageID]: %s\n%s', [e.ClassName, e.Message]); end; end; finally req.Free; end; end; var req1, req2 : TRESTRequest; draft : TMSGraphDraftResponse; draftremoved, step1done : boolean; removemsgid, immid : string; begin Result := False; immid := ''; removemsgid := ''; step1done := False; draftremoved := False; req1 := TRESTRequest.Create(nil); req2 := TRESTRequest.Create(nil); try try req1.Client := FRestClient; req1.Method := TRESTRequestMethod.rmPOST; req2.Client := FRestClient; req2.Method := TRESTRequestMethod.rmPOST; req1.Resource := CNST_RESOURCE_CREATEDRAFTMESSAGE.Replace('{sendasemailaddress}', AccountEmailAddress, [rfReplaceAll, rfIgnoreCase]); req1.AddBody(AAPIMessage.message.AsJson(), TRESTContentType.ctAPPLICATION_JSON); try req1.Execute(); except on e: exception do begin raise Exception.CreateFmt('[THSMSGraphAPI.SendEmailWithLargeAttachments.Req1]: %s\n%s', [e.ClassName, e.Message]); end; end; if not LogInvalidResponse(req1.Response, True, 'THSMSGraphAPI.SendEmailWithLargeAttachments.1') then begin draft := TMSGraphDraftResponse.FromJSON(req1.Response.Content); removemsgid := draft.id; step1done := IsValidResponse(req1.Response); if not (AEmailMessage.SaveOnSend) then begin immid := FetchImmutableID(draft.id); removemsgid := immid; end; {$ifdef debug} if not immid.IsEmpty then LogExInfo('ImmutableID: %s', [immid], 'THSMSGraphAPI.SendEmailWithLargeAttachments'); {$endif} //raise Exception.Create('TEST Error Message'); try AddLargeAttachments(AEmailMessage.Attachments, draft.id); except on e: exception do begin raise Exception.CreateFmt('[THSMSGraphAPI.SendEmailWithLargeAttachments.AddLargeAttachments]: %s\n%s', [e.ClassName, e.Message]); end; end; end; req2.Resource := CNST_RESOURCE_SENDCREATEDRAFT.Replace('{sendasemailaddress}', AccountEmailAddress, [rfReplaceAll, rfIgnoreCase]).Replace('{messageid}', draft.id, [rfReplaceAll, rfIgnoreCase]); try req2.Execute(); except on e: exception do begin raise Exception.CreateFmt('[THSMSGraphAPI.SendEmailWithLargeAttachments.Req2]: %s\n%s', [e.ClassName, e.Message]); end; end; if not LogInvalidResponse(req2.Response, True, 'SendWithLargeAttachments.2') then begin Result := True; if not (AEmailMessage.SaveOnSend) then begin if not immid.IsEmpty then draftremoved := TryRemoveEmailMessage(immid) else LogExWarn('Could not remove draft sendmessage because empty immutableid', 'THSMSGraphAPI.SendEmailWithLargeAttachments'); end; end; except on e: exception do begin Result := False; if (not draftremoved) and (step1done) and (not removemsgid.IsEmpty) then begin Sleep(1000); draftremoved := TryRemoveEmailMessage(removemsgid); if draftremoved then LogExWarn('Draft message with id: %s removed.', [removemsgid], 'THSMSGraphAPI.SendEmailWithLargeAttachments') else LogExError('Draft message with id: %s was not removed!', [removemsgid], 'THSMSGraphAPI.SendEmailWithLargeAttachments') end; raise; end; end; finally req2.Free; req1.Free; end; end;
  2. mvanrijnen

    Help with Interbase

    strange way of looking to solve a problem, if you don't bother, than do no ask....
  3. mvanrijnen

    Help with Interbase

    From Delphi to Interbase, over localnetwork/internet ? Which database component set your are using (e.g. FireDac) ?
  4. mvanrijnen

    SMTP Server -> Client

    i've build a service (called ProtoBridge, not public available), which does just this. But more 😉 It can act as a simple bridge between older machines/services to forward emails to o365/gmail (oauth2) etc etc. It can also serve as a ftp server, and you can configure different virtual ftp folders which direct the incoming file(s) to a specific email adress so you can "bridge" protocols it can act as server for: * simple smtp * ftp * local folders * smb folders and forward to: * simple smtp * smtp (oauth2 etc) * O365 (MS Graph API) * Exchange EWS * ftp * local folder * smb folder It's using INDY for all the protocols (except MS Graph API & EWS), off course is this a service which is not intended to use on a public server. (there is a white & blacklist on IP numbers available)
  5. mvanrijnen

    My app dies in Server 2019

    A specific version?
  6. mvanrijnen

    Signotaur Code Signing Server - Looking for beta testers

    Yes. my question was more, (a discussion i had on this forum a few years ago also), do we benefit for preventing false positives using signing (makes it easier turning the mgmt in positive direction, so they don't only see it as a cost) ? (we are going to implement signing anyway).
  7. mvanrijnen

    Signotaur Code Signing Server - Looking for beta testers

    So we create only software for internal use, using (at the moment) , do we benefit from code signing ?
  8. mvanrijnen

    Delphi 12.2 Patch 1

    this is not sustainable as a process to get updates to the end users. I just spent 2 days of work updating our buildmachine from11.x to 12.2. Can start over now?
  9. mvanrijnen

    creating a frame at runtime

    In that case, the text is not static 😉
  10. mvanrijnen

    Simulate blocking mode to send Email

    Not a direct answer, but we (in our custom ERP solution), put the emails in a table (kind of a mailqueue) , and another process (service) polls and/or gets signaled when to scan the table for messages to send. With this we have better control, and are sure that the email is send in the background. We also can control a minimum, maximum datetime to send the message etc etc. (sometimes an email message is irrelevant if it didn't get send after a specific date) So not high prio mails are send like every 5 minutes, high prio directly. Only problem is that interactive emails (send with a client like outlook) are not under the same control. The service itself is highly configurable, with different outgoing servers, accounts etc etc.,
  11. yes, so called "CONSTANT VARIABLES" 😉
  12. mvanrijnen

    Delphi roadmap 2024

    hope we get build in Nullable types soon.
  13. mvanrijnen

    Watch me coding in Delphi on YouTube

    i really do no understand why you publish this on youtube, why not a normal website where people can read this stuff ? Stuff like this is much better taken into the brain while reading instead of watching a video. Is it because you want to generate income from YT with it ?
  14. mvanrijnen

    DelphiLSP.Exe version 11.1 vs 12.2

    LSP in 12.x (when it works) gives also wrong method/prop/var names sometimes, such as in the same unit strict private fields from other object
  15. thats not good, having a problem with a RAD server package, which i would like to debug, gonna be a problem this week. Maybe the right time to say goodbye to RADserver and transfer the code to MARS (the Rest Server, not the planet) @Marco CantuWhats the status of this problem?
  16. Is this fixed in D12.2 ? Would nice to know before i spent 2 days updating the dev environment .
  17. Hi folks, We have a new developer at out team, he got C# & Python at his study, and has to learn Delphi now to be productive in our team 🙂 What sites you recommend to start out with Delphi, we have him now looking to Learn Delphi, and the links mentioned there.
  18. So unusable 🙂 Thats the EMB way, big mouth, big blogs, moving fancy blog posts, but not complete components or working IDE (i also hacked a few of the Rest.BackEnd.* units because they do not complete support all properties of the TRestRequest etc)
  19. mvanrijnen

    What happened to OmniThreadLibrary.com

    Omnithreadlibrary.com down? Check here and read user reports (downforeveryoneorjustme.com)
  20. Which service are you trying to reach? Cause (long time ago for me) i think that it depends on the server settings if a ClientCert is needed. See for examplle "IdServerIOHandlerSSLOpenSSL1.SSLOptions.Mode :" property. Could be that this also has to be set in the client
  21. At what point you are stuck? You can use the "TRestClient.OnNeedClientCertificate" event, simple example code: We check the certificate name, which has to begin with a certain value (stored in the CNST_CERT_PREFIX constant), and of course it has to be a valid certificate. (i believe you need the client certificate installed in "user" context, not sure about that, long time ago i was busy with this). procedure TMyProgram.DoOnClientCertificateNeeded(const Sender: TObject; const ARequest: TURLRequest; const ACertificateList: TCertificateList; var AnIndex: Integer); var idx : integer; begin if CNST_CERT_PREFIX.IsEmpty then raise Exception.Create('[TMyProgram.DoOnClientCertificateNeeded] CNST_CERT_PREFIX is empty.'); for idx := 0 to ACertificateList.Count - 1 do begin if ACertificateList[idx].CertName.StartsWith(CNST_CERT_PREFIX) then begin if (ACertificateList[idx].Start<=Now) and (ACertificateList[idx].Expiry>Now) then begin AnIndex := idx; break; end else raise Exception.Create('[TMyProgram.DoOnClientCertificateNeeded] Client Certificate Expired.'); end; end; end;
  22. mvanrijnen

    Upgarding from RAD Server 11 to RAD Server 12

    Just moving to D12 myself. in the EMSDevServer (development environment for RadServer), i do not get breakpoints in my code?
  23. mvanrijnen

    How to rearrange projects in projects group file?

    just drag and drop in the projects list of the group?
  24. mvanrijnen

    REST Web Service

    also check: GitHub - andrea-magni/MARS: MARS-Curiosity Delphi REST Library
×