public async Task <T> LoadItem <T>(string url, TokenHolder token) where T : new() { var str = await GetHttpContentWithToken(url, token); var obj = JsonConvert.DeserializeObject <T>(str); return(obj); }
/// <summary> /// Perform an HTTP GET request to a URL using an HTTP Authorization header /// </summary> /// <param name="url">The URL</param> /// <param name="token">The token</param> /// <returns>String containing the results of the GET operation</returns> public async Task <string> GetHttpContentWithToken(string url, TokenHolder token) { var httpClient = new System.Net.Http.HttpClient(); System.Net.Http.HttpResponseMessage response; try { int backOffMultiplier = 1; begin: var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url); //Add the token in Authorization header request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.getToken()); response = await httpClient.SendAsync(request); var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { return(content); } else { if (response.StatusCode.Equals(System.Net.HttpStatusCode.NotFound)) { // resource probably belongs to another tenant return(content); } if (response.StatusCode.Equals(System.Net.HttpStatusCode.Forbidden)) { // client id lacks permissions LogText.Text = "ClientId lacks permission to fetch: " + url; return(content); } if (response.StatusCode.Equals(System.Net.HttpStatusCode.Unauthorized)) { LogText.Text = "Token expired?? Not authorized to fetch: " + url; token.refreshToken(); goto begin; } else { LogText.Text = $"Error statuscode: {response.StatusCode} content: {response.Content}" + Environment.NewLine + $"sleeping for {30*backOffMultiplier}sec.."; Thread.Sleep(backOffMultiplier * 30 * 1000); LogText.Text = "Continuing..."; backOffMultiplier *= 2; goto begin; } } } catch (Exception ex) { LogText.Text = ex.ToString(); return(ex.ToString()); } }
public async Task <Items <T> > LoadItems <T>(string url, TokenHolder token) where T : new() { int backOffMultiplier = 1; begin: var str = await GetHttpContentWithToken(url, token); var obj = JsonConvert.DeserializeObject <Items <T> >(str); if (obj.Error != null) { if (obj.Error.Code == "TooManyRequests") { LogText.Text = "too many requests to server," + Environment.NewLine + $"sleeping for {30*backOffMultiplier}sec.."; Thread.Sleep(backOffMultiplier * 30 * 1000); backOffMultiplier *= 2; goto begin; } } return(obj); }
/// <summary> /// Call AcquireToken - to acquire a token requiring user to sign-in /// </summary> private async void CallGraphButton_Click(object sender, RoutedEventArgs e) { AuthenticationResult authResult = null; var app = App.PublicClientApp; LogText.Text = string.Empty; LogText.IsReadOnly = true; if (string.IsNullOrEmpty(HistoryText.Text)) { HistoryText.Text = path; } var accounts = await app.GetAccountsAsync(); var firstAccount = accounts.FirstOrDefault(); try { authResult = await app.AcquireTokenSilent(scopes, firstAccount) .ExecuteAsync(); } catch (MsalUiRequiredException ex) { // A MsalUiRequiredException happened on AcquireTokenSilent. // This indicates you need to call AcquireTokenInteractive to acquire a token System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}"); try { authResult = await app.AcquireTokenInteractive(scopes) .WithAccount(accounts.FirstOrDefault()) .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle) // optional, used to center the browser on the window .WithPrompt(Prompt.SelectAccount) .ExecuteAsync(); } catch (MsalException msalex) { LogText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}"; } } catch (Exception ex) { LogText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}"; return; } if (authResult != null) { TokenHolder tokenHolder = new TokenHolder(app, scopes, authResult); this.SignOutButton.Visibility = Visibility.Visible; LogText.Text = "Loading data ..."; var me = await LoadItem <User>("https://graph.microsoft.com/beta/me", tokenHolder); var path = HistoryText.Text; var dbPath = Path.Combine(path, authResult.Account.Username);; if (!System.IO.Directory.Exists(dbPath)) { System.IO.Directory.CreateDirectory(dbPath); } var chatsList = new List <Models.Graph.Chats.Chat>(); var url = new Uri("https://graph.microsoft.com/beta/me/chats"); do { var chatsObj = await LoadItems <Models.Graph.Chats.Chat>(url.OriginalString, tokenHolder); url = chatsObj.OdataNextLink; chatsList.AddRange(chatsObj.Value); } while (url != null); System.IO.File.WriteAllText(Path.Combine(dbPath, "chats.json"), JsonConvert.SerializeObject(chatsList)); var chatsPath = Path.Combine(dbPath, "chats");; if (!System.IO.Directory.Exists(chatsPath)) { System.IO.Directory.CreateDirectory(chatsPath); } int i = 0; foreach (var chat in chatsList) { LogText.Text = $"Loading messages for chat {++i}/{chatsList.Count}"; var chatDirPath = Path.Combine(chatsPath, chat.Id.SHA1()); if (!System.IO.Directory.Exists(chatDirPath)) { System.IO.Directory.CreateDirectory(chatDirPath); } System.IO.File.WriteAllText(Path.Combine(chatDirPath, "chat.json"), JsonConvert.SerializeObject(chat)); var messagesPath = Path.Combine(chatDirPath, "messages.json"); var listMessages = new List <Message>(); var messages = await LoadItems <Message>($"https://graph.microsoft.com/beta/me/chats/{chat.Id}/messages", tokenHolder); if (messages.OdataCount > 0) { listMessages.AddRange(messages.Value); do { var newMessages = await LoadItems <Message>(messages.OdataNextLink.ToString(), tokenHolder); if (messages.OdataNextLink != null && newMessages.OdataNextLink != null && messages.OdataNextLink.ToString().Equals(newMessages.OdataNextLink.ToString())) { LogText.Text = $"MS Graph API loop detected! Affected chat is {chat.Id.SHA1()}."; break; } messages = newMessages; if (messages.OdataCount == 0) { break; } listMessages.AddRange(messages.Value); }while(true); } var x_messages = listMessages.OrderBy(x => x.CreatedDateTime).ToList(); System.IO.File.WriteAllText(messagesPath, JsonConvert.SerializeObject(x_messages)); var members = await LoadItems <Member>($"https://graph.microsoft.com/beta/me/chats/{chat.Id}/members", tokenHolder); if (members.Value != null && members.Value.Count > 0 && x_messages.Count > 0) { System.IO.File.WriteAllText(Path.Combine(chatDirPath, "members.json"), JsonConvert.SerializeObject(members.Value)); var arr = members.Value.Where(x => x.UserId.ToString() != me.Id).ToList(); var history = string.Empty; if (arr.Count > 0) { history = string.Join(",", arr.Select(x => x.DisplayName)); if (history.Length > 100) { history = history.Remove(100) + "..."; } } if (string.IsNullOrEmpty(history)) { history = "history"; } var data = x_messages.Select(x => { var text = System.Net.WebUtility.HtmlDecode(x.Body.Content.StripHTML()); text = Regex.Replace(text, @"^\s+$[\r\n]*", string.Empty, RegexOptions.Multiline); var str = $"{x.CreatedDateTime?.ToString("yyyy-MM-dd HH:mm:ss")} {text}"; return(str); }); System.IO.File.WriteAllLines(Path.Combine(chatDirPath, $"{history}.txt"), data); } var shareGateChatDirPath = Path.Combine(chatDirPath, "sharegate"); if (!System.IO.Directory.Exists(shareGateChatDirPath)) { System.IO.Directory.CreateDirectory(shareGateChatDirPath); } var shareGateChatDirAttachmentsPath = Path.Combine(shareGateChatDirPath, "Messages Attachments"); if (!System.IO.Directory.Exists(shareGateChatDirAttachmentsPath)) { System.IO.Directory.CreateDirectory(shareGateChatDirAttachmentsPath); } var shareGateMessagesPath = Path.Combine(shareGateChatDirPath, "Messages.json"); var sharegate_messages = ConvertToShareGate(x_messages, shareGateChatDirAttachmentsPath, tokenHolder); var sharegate_messagesDotJson = WrapShareGateMessages(await sharegate_messages); var json_string = UnDoDoubleEscaping(JsonConvert.SerializeObject(sharegate_messagesDotJson)); System.IO.File.WriteAllText(shareGateMessagesPath, json_string); var shareGateMessagesDotAspxTemplatePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Messages.aspx.template"); var shareGateMessagesDotAspxTemplate = System.IO.File.ReadAllText(shareGateMessagesDotAspxTemplatePath); var shareGateMessagesDotAspxPath = Path.Combine(shareGateChatDirPath, "Messages.aspx"); System.IO.File.WriteAllText(shareGateMessagesDotAspxPath, shareGateMessagesDotAspxTemplate.Replace("####INSERT_JSON_HERE####", json_string)); } LogText.Text = "Done."; } }
public async Task <string> FetchAttachmentWithToken(string url, string attachmentsPath, string filenamePrefix, TokenHolder token) { var httpClient = new System.Net.Http.HttpClient(); try { var fileMetadataStr = await GetHttpContentWithToken(url, token); Metadata obj = null; if (fileMetadataStr != null) { obj = JsonConvert.DeserializeObject <Metadata>(fileMetadataStr); } int backOffMultiplier = 1; begin: var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url + "/$value"); //Add the token in Authorization header request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.getToken()); var response = httpClient.SendAsync(request); if (response.Result.IsSuccessStatusCode) { var content = response.Result.Content.ReadAsByteArrayAsync().Result; string filenameSuffix = GetFilenameSuffix(response.Result.Content.Headers.ContentType.MediaType, obj); System.IO.File.WriteAllBytes(Path.Combine(attachmentsPath, $"{filenamePrefix}{filenameSuffix}"), content); return($"{filenamePrefix}{filenameSuffix}"); } else { if (response.Result.StatusCode.Equals(System.Net.HttpStatusCode.NotFound)) { return(null); } if (fileMetadataStr.Contains("ErrorInsufficientPermissionsInAccessToken")) { LogText.Text = "ClientId lacks permission to fetch: " + url; return(null); } if (response.Result.StatusCode.Equals(System.Net.HttpStatusCode.Unauthorized)) { token.refreshToken(); goto begin; } LogText.Text = $"Error statuscode: {response.Result.StatusCode} content: {response.Result.Content}" + Environment.NewLine + $"sleeping for {30*backOffMultiplier}sec.."; Thread.Sleep(backOffMultiplier * 30 * 1000); backOffMultiplier *= 2; goto begin; } return(null); // no picture found } catch (Exception ex) { LogText.Text = ex.ToString(); return(null); // probably no profile picture was found } }
private Task <string> TryToFetchPicture(string v, string attachmentsPath, TokenHolder accessToken) { return(FetchAttachmentWithToken($"https://graph.microsoft.com/beta/users/{v}/photos/48x48", attachmentsPath, v, accessToken)); }
private async Task <string> FetchImages(string content, string attachmentsPath, string messageId, TokenHolder accessToken) { Regex graphFiles = new Regex(@"https:\/\/graph\.microsoft\.com\/[^\""]+\/\$value"); MatchCollection urlsToFetch = graphFiles.Matches(content); string transformedContent = content; for (int count = 0; count < urlsToFetch.Count; count++) { var graphUrl = urlsToFetch[count].Value; var attachmentFileNamePrefix = messageId + "_" + count; var inlineMessageAttachmentFilename = await FetchAttachmentWithToken(graphUrl.Replace("/$value", ""), attachmentsPath, attachmentFileNamePrefix, accessToken); transformedContent = transformedContent.Replace(graphUrl, $"Messages Attachments/{inlineMessageAttachmentFilename}"); } return(transformedContent); }
private async Task <string> TransformToShareGateMessageBodyContent(Message message, Dictionary <string, string> pictureCache, string attachmentsPath, TokenHolder accessToken, string attachmentCode) { var sender = (Newtonsoft.Json.Linq.JObject)message.From.AdditionalData["user"]; // null for bots if (sender != null && sender.ToString().Contains("userIdentityType") && sender["userIdentityType"].ToString().Equals("aadUser") && !pictureCache.Keys.Contains(sender["id"].ToString())) { pictureCache.Add(sender["id"].ToString(), await TryToFetchPicture(sender["id"].ToString(), attachmentsPath, accessToken)); } string pictureFilename = sender != null && sender["userIdentityType"].ToString().Equals("aadUser") ? pictureCache[sender["id"].ToString()] : null; var pictureCode = pictureFilename != null ? $"<img src=\"Messages Attachments/{pictureFilename}\" width=\"32\" height=\"32\" style=\"vertical-align:top; width:32px; height:32px;\">" : ""; string messageBody; if (message.Body.ContentType.ToString().Equals("Html")) { messageBody = await FetchImages(message.Body.Content, attachmentsPath, message.Id, accessToken);; } else { messageBody = message.Body.Content; } string senderDisplayName = sender != null ? sender["displayName"].ToString() : "exporter encountered unsupported sender (probably a bot?)"; return("<div style=\"display: flex; margin-top: 10px\">" + "<div style=\"flex: none; overflow: hidden; border-radius: 50%; height: 32px; width: 32px; margin: 0 10px 10px 0\">" + pictureCode + "</div>" + "<div style=\"flex: 1; overflow: hidden;\">" + "<div style=\"font-size:1.2rem; white-space:nowrap; text-overflow:ellipsis; overflow: hidden;\">" + $"<span style=\"font-weight:700;\">{senderDisplayName}</span>" + $"<span style=\"margin-left:1rem;\">{message.CreatedDateTime}</span>" + "</div>" + $"<div>{messageBody}</div>" + "</div>" + "</div>" + attachmentCode); }
private async Task <SgMessage> ConvertOneMessageToShareGate(Message message, Dictionary <string, string> pictureCache, string attachmentsPath, TokenHolder accessToken) { var sg_message = new SgMessage(); sg_message.Subject = message.Subject != null ? message.Subject : ""; sg_message.Body = new SgMessageBody(); sg_message.Body.ContentType = "html"; var attachmentCode = ""; sg_message.Attachments = message.Attachments.Select(attachment => { var sg_attachment = new SgAttachment(); sg_attachment.Id = attachment.Id; sg_attachment.Content = "querying attachment not implemented"; sg_attachment.ContentType = attachment.ContentType; if (attachment.ContentType.Equals("reference")) { sg_attachment.Content = attachment.AdditionalData["contentUrl"].ToString(); attachmentCode += $"<div>Attachment: <a href=\"{attachment.AdditionalData["contentUrl"].ToString()}\">${attachment.Name} - {attachment.AdditionalData["contentUrl"].ToString()}</a></div>"; } if (attachment.ContentType.Equals("application/vnd.microsoft.card.adaptive")) { sg_attachment.Content = "adaptive card not migrated"; // dynamic adaptiveCard = JsonConvert.DeserializeObject(attachment.AdditionalData["content"].ToString()); // attachmentCode += $"<div>Attachment: <img src=\"{adaptiveCard.url.ToString()}\" alt=\"{adaptiveCard.altText.ToString()}\" /> </div>"; } sg_attachment.Name = attachment.Name; sg_attachment.ExportedAttachmentContentUrl = ""; return(sg_attachment); }).ToList(); sg_message.Body.Content = await TransformToShareGateMessageBodyContent(message, pictureCache, attachmentsPath, accessToken, attachmentCode); sg_message.Mentions = new List <object>(); sg_message.Importance = message.Importance.ToString().ToLower(); return(sg_message); }
private async Task <List <List <SgMessage> > > ConvertToShareGate(List <Message> x_messages, string attachmentsPath, TokenHolder accessToken) { var retList = new List <List <SgMessage> >(); DateTime previousCurrentDay = DateTime.MinValue; List <SgMessage> currentDayList = null; var pictureCacheIds = new Dictionary <string, string>(); foreach (var message in x_messages) { var messageDay = message.LastModifiedDateTime.Value.DateTime.Date; var newDay = !previousCurrentDay.Equals(messageDay); previousCurrentDay = messageDay; var sg_message = ConvertOneMessageToShareGate(message, pictureCacheIds, attachmentsPath, accessToken); if (newDay) { currentDayList = new List <SgMessage>(); retList.Add(currentDayList); } currentDayList.Add(await sg_message); } return(retList); }