diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/LocalProfilerIndex.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/LocalProfilerIndex.java index e8c3d2d68..79cbff6f2 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/LocalProfilerIndex.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/LocalProfilerIndex.java @@ -11,9 +11,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -74,7 +72,7 @@ public String getUrlForRequest(@NotNull ProfilerRequestInterface request) { return this.baseUrl + "/" + StringUtils.stripStart(request.getProfilerUrl(), "/"); } - return ProfilerUtil.getBaseProfilerUrlFromRequest(request.getProfilerUrl()); + return ProfilerUtil.getBaseProfilerUrlFromRequest(request) + "/" + StringUtils.stripStart(request.getProfilerUrl(), "/"); } @NotNull @@ -102,19 +100,7 @@ private String getContentForHash(@NotNull String hash) { return null; } - StringBuilder content = new StringBuilder(); - - try { - BufferedReader in = new BufferedReader(new FileReader(file)); - String str; - while ((str = in.readLine()) != null) { - content.append(str); - } - in.close(); - } catch (IOException ignored) { - } - - return content.toString(); + return ProfilerUtil.getContentForFile(file); } private class MyProfilerRequestBuilderCallable implements Callable { @@ -125,7 +111,7 @@ private class MyProfilerRequestBuilderCallable implements Callable messages = collector.getMessages(); if(messages.size() > 0) { - this.editorPane1.setText(messages.iterator().next().getMessage()); + this.editorPane1.setText(messages.iterator().next().message()); } } } public void selected(MailMessage mailMessage) { - this.editorPane1.setText(mailMessage.getMessage()); + this.editorPane1.setText(mailMessage.message()); } private void start() { @@ -177,8 +178,8 @@ public Component getListCellRendererComponent(JList list, Object value, int inde renderer.setText(((LocalProfilerRequest) value).getUrl()); } - if(value instanceof MailMessage) { - renderer.setText(((MailMessage) value).getTitle()); + if (value instanceof MailMessage) { + renderer.setText(StringUtils.abbreviate(((MailMessage) value).title(), 40)); } return renderer; diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/collector/LocalMailCollector.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/collector/LocalMailCollector.java index f8e7d65fe..62cc41d1d 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/collector/LocalMailCollector.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/collector/LocalMailCollector.java @@ -5,9 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,29 +23,47 @@ public LocalMailCollector(@NotNull String contents) { @NotNull public Collection getMessages() { + Collection mails = new ArrayList<>(); + String messages = this.findTwice(this.contents, "MessageDataCollector\":(\\d+):"); - if(messages == null) { - return Collections.emptyList(); - } + if(messages != null) { + Matcher matcher = Pattern.compile("\"\\x00Swift_Mime_SimpleMimeEntity\\x00_body\";s:(\\d+):\"", Pattern.MULTILINE).matcher(messages); - Matcher matcher = Pattern.compile("\"\\x00Swift_Mime_SimpleMimeEntity\\x00_body\";s:(\\d+):\"", Pattern.MULTILINE).matcher(messages); + while(matcher.find()){ + String domain = matcher.group(1); - Collection mails = new ArrayList<>(); - while(matcher.find()){ - String domain = matcher.group(1); - //String array_strings = matcher.group(2); + int start = matcher.end(); + int end = start + Integer.parseInt(domain); - int start = matcher.end(); - int end = start + Integer.parseInt(domain); + mails.add(new MailMessage(messages.substring(start, end), "", "", "swiftmailer")); + } + } - //System.out.println(content.substring(start, end)); - mails.add(new MailMessage(messages.substring(start, end), "aa", "aa")); + // try to find any serialized object inside the mailer class + Matcher matcher = Pattern.compile("AbstractHeader\\x00name\"[\\w;:]+\"Subject\"").matcher(this.contents); + Set titles = new HashSet<>(); - //Matcher match_strings = Pattern.compile("'(.*?)'\\s=>\\s'.*?'", Pattern.MULTILINE).matcher(array_strings); - //while(match_strings.find()){ - // string_map.addString(domain, match_strings.group(1)); - //} + while(matcher.find()){ + int end = matcher.end(); + + // find ending scope + int endingHeaderScope = contents.indexOf("}", end); + if (endingHeaderScope > 0) { + String subjectHeaderScope = contents.substring(matcher.start(), endingHeaderScope); + + // find the email "Subject" header value + Matcher matcher2 = Pattern.compile("UnstructuredHeader.*value\"[\\w;:]+\"(.*)\"").matcher(subjectHeaderScope); + if (matcher2.find()) { + String subject = matcher2.group(1); + if (!subject.isBlank()) { + titles.add(subject); + } + } + } + } + for (String title : titles) { + mails.add(new MailMessage("", title, "", "mailer")); } return mails; diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/dict/MailMessage.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/dict/MailMessage.java index d65e89c66..3cb427285 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/dict/MailMessage.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/dict/MailMessage.java @@ -5,36 +5,5 @@ /** * @author Daniel Espendiller */ -public class MailMessage { - - @NotNull - private final String message; - - @NotNull - private final String title; - - @NotNull - private final String format; - - public MailMessage(@NotNull String message, @NotNull String title, @NotNull String format) { - this.message = message; - this.title = title; - this.format = format; - } - - @NotNull - public String getMessage() { - return message; - } - - @NotNull - public String getTitle() { - return title; - } - - @NotNull - public String getFormat() { - return format; - } - +public record MailMessage(@NotNull String message, @NotNull String title, @NotNull String format, @NotNull String panel) { } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/utils/ProfilerUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/utils/ProfilerUtil.java index 552cfe553..5f7a64c31 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/utils/ProfilerUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/utils/ProfilerUtil.java @@ -19,9 +19,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -31,6 +29,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; /** * @author Daniel Espendiller @@ -424,11 +423,18 @@ public static List getProfilerRequestCollectorDecorate * https://127.0.0.1:8000/app_dev.php */ @Nullable - public static String getBaseProfilerUrlFromRequest(@NotNull String requestUrl) { - URL url; + public static String getBaseProfilerUrlFromRequest(@NotNull ProfilerRequestInterface request) { + URL url = null; try { - url = new URL(requestUrl); + url = new URL(request.getProfilerUrl()); } catch (MalformedURLException e) { + try { + url = new URL(request.getUrl()); + } catch (MalformedURLException ignored) { + } + } + + if (url == null) { return null; } @@ -468,5 +474,70 @@ public static String formatProfilerRow(@NotNull ProfilerRequestInterface profile return String.format("(%s) %s", statusCode == 0 ? "n/a" : statusCode, StringUtils.abbreviate(path, 35)); } + + public static String getContentForFile(@NotNull File file) { + boolean isGzipFile; + + try { + byte[] buffer = new byte[3]; + + InputStream is = new FileInputStream(file); + int __ = is.read(buffer); + + // check gzip header + isGzipFile = buffer[0] == 31 + && buffer[1] == -117 + && buffer[2] == 8; + + is.close(); + } catch (IOException e) { + return null; + } + + if (isGzipFile) { + return getProfilerContentGzdecode(file); + } + + return getContentForRaw(file); + } + + @Nullable + private static String getContentForRaw(@NotNull File file) { + StringBuilder content = new StringBuilder(); + + try { + BufferedReader in = new BufferedReader(new FileReader(file)); + String str; + while ((str = in.readLine()) != null) { + content.append(str); + } + in.close(); + } catch (IOException ignored) { + return null; + } + + return content.toString(); + } + + @Nullable + private static String getProfilerContentGzdecode(File file) { + try { + InputStream in = new FileInputStream(file.getPath()); + + GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(in.readAllBytes())); + BufferedReader bf = new BufferedReader(new InputStreamReader(gis)); + StringBuilder outStr = new StringBuilder(); + + String line; + while ((line=bf.readLine())!=null) { + outStr.append(line); + } + + return outStr.toString(); + } catch (IOException ignored) { + } + + return null; + } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/SymfonyProfilerWidget.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/SymfonyProfilerWidget.java index 6e19ccd4d..df1c755af 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/SymfonyProfilerWidget.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/SymfonyProfilerWidget.java @@ -13,11 +13,15 @@ import com.intellij.openapi.wm.StatusBarWidget; import com.intellij.openapi.wm.impl.status.EditorBasedWidget; import com.intellij.ui.popup.PopupFactoryImpl; +import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; import fr.adrienbrault.idea.symfony2plugin.profiler.ProfilerIndexInterface; import fr.adrienbrault.idea.symfony2plugin.profiler.collector.DefaultDataCollectorInterface; +import fr.adrienbrault.idea.symfony2plugin.profiler.collector.MailCollectorInterface; +import fr.adrienbrault.idea.symfony2plugin.profiler.dict.MailMessage; import fr.adrienbrault.idea.symfony2plugin.profiler.dict.ProfilerRequestInterface; import fr.adrienbrault.idea.symfony2plugin.profiler.factory.ProfilerFactoryUtil; import fr.adrienbrault.idea.symfony2plugin.profiler.widget.action.SymfonyProfilerWidgetActions; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -75,39 +79,45 @@ public DefaultActionGroup getActions(){ attachProfileItem(controllerActions, controllerActionsMap, collector.getController(), ProfilerTarget.CONTROLLER); } - // @TODO: use collector - //String content = profilerRequest.getContent(); - //if(content != null && content.contains("Swift_Mime_Headers_MailboxHeader")) { - // mailActions.add(new SymfonyProfilerWidgetActions.UrlAction(getProject(), profilerRequest, statusCode).withPanel("swiftmailer").withIcon(Symfony2Icons.MAIL)); - //} + MailCollectorInterface collectorMail = profilerRequest.getCollector(MailCollectorInterface.class); + if(collectorMail != null) { + for (MailMessage message : collectorMail.getMessages()) { + String title = message.title(); + if (title.isBlank()) { + mailActions.add(new SymfonyProfilerWidgetActions.UrlAction(index, profilerRequest, message.panel()) + .withIcon(Symfony2Icons.MAIL) + .withText(String.format("(%s) %s", profilerRequest.getHash(), StringUtils.abbreviate(title, 40)))); + } + } + } } // routes - if(urlActions.size() > 0) { + if(!urlActions.isEmpty()) { actionGroup.addSeparator("Debug-Url"); actionGroup.addAll(urlActions); } // mails send by request - if(mailActions.size() > 0) { + if(!mailActions.isEmpty()) { actionGroup.addSeparator("E-Mail"); actionGroup.addAll(mailActions); } // routes - if(routeActions.size() > 0) { + if(!routeActions.isEmpty()) { actionGroup.addSeparator("Routes"); actionGroup.addAll(routeActions); } // controller methods - if(controllerActions.size() > 0) { + if(!controllerActions.isEmpty()) { actionGroup.addSeparator("Controller"); actionGroup.addAll(controllerActions); } // template should be most use case; so keep it in cursor range - if(templateActions.size() > 0) { + if(!templateActions.isEmpty()) { actionGroup.addSeparator("Template"); actionGroup.addAll(templateActions); } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/action/SymfonyProfilerWidgetActions.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/action/SymfonyProfilerWidgetActions.java index 46f93f694..05ad7fbdf 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/action/SymfonyProfilerWidgetActions.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/profiler/widget/action/SymfonyProfilerWidgetActions.java @@ -120,6 +120,13 @@ public UrlAction withIcon(Icon icon) { return this; } + public UrlAction withText(String text) { + Presentation presentation = getTemplatePresentation(); + presentation.setText(text); + + return this; + } + @Override public void actionPerformed(AnActionEvent event) { String urlForRequest = profilerIndex.getUrlForRequest(profilerRequest); @@ -127,6 +134,10 @@ public void actionPerformed(AnActionEvent event) { return; } + if (this.panel != null) { + urlForRequest += "?panel=" + this.panel; + } + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; if (desktop == null || !desktop.isSupported(Desktop.Action.BROWSE)) { return; diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/ProfilerUtilTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/ProfilerUtilTest.java index 9609606b8..0cb65ea9e 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/ProfilerUtilTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/ProfilerUtilTest.java @@ -1,11 +1,13 @@ package fr.adrienbrault.idea.symfony2plugin.tests.profiler; import com.intellij.psi.PsiFile; +import fr.adrienbrault.idea.symfony2plugin.profiler.dict.HttpProfilerRequest; import fr.adrienbrault.idea.symfony2plugin.profiler.dict.LocalProfilerRequest; import fr.adrienbrault.idea.symfony2plugin.profiler.utils.ProfilerUtil; import fr.adrienbrault.idea.symfony2plugin.profiler.dict.ProfilerRequestInterface; import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase; +import java.io.File; import java.util.Collection; import java.util.Map; @@ -81,30 +83,30 @@ public void testGetRenderedElementTwigTemplates() { * @see ProfilerUtil#getBaseProfilerUrlFromRequest */ public void testGetBaseProfilerUrlFromRequest() { - assertEquals("http://127.0.0.1", ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1")); - assertEquals("http://127.0.0.1", ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:80")); - assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080")); - assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/")); - assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/app_dev.php")); + assertEquals("http://127.0.0.1", ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1", "", ""))); + assertEquals("http://127.0.0.1", ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:80", "", ""))); + assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080", "", ""))); + assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/", "", ""))); + assertEquals("http://127.0.0.1:8080", ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/app_dev.php", "", ""))); assertEquals( "http://127.0.0.1:8080/app_dev.php", - ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/app_dev.php/") + ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/app_dev.php/", "", "")) ); assertEquals( "http://127.0.0.1:8080/app/app_dev.php", - ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/app/app_dev.php/") + ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/app/app_dev.php/", "", "")) ); assertEquals( "http://127.0.0.1:8080/app/app_stage.php", - ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/app/app_stage.php/") + ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/app/app_stage.php/", "", "")) ); assertEquals( "http://127.0.0.1:8080/app/app_test.php", - ProfilerUtil.getBaseProfilerUrlFromRequest("http://127.0.0.1:8080/app/app_test.php/") + ProfilerUtil.getBaseProfilerUrlFromRequest(new HttpProfilerRequest(0, "aaa", "http://127.0.0.1:8080/app/app_test.php/", "", "")) ); } @@ -132,4 +134,12 @@ public void testFormatProfilerRow() { ProfilerUtil.formatProfilerRow(new LocalProfilerRequest("18e6b8,127.0.0.1,GET,asdss127.0.0.1:8000/app_test.php/foobar/foobar/,1474185112,76c8ab,404".split(","))) ); } + + public void testGetContentForFile() { + String contentFor = ProfilerUtil.getContentForFile(new File(this.getTestDataPath() + "/748f72-gzip-profiler")); + assertTrue(contentFor.startsWith("a:9")); + + String contentFor2 = ProfilerUtil.getContentForFile(new File(this.getTestDataPath() + "/748f72-gzip-profiler-raw")); + assertTrue(contentFor2.startsWith("a:9")); + } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/LocalMailCollectorTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/LocalMailCollectorTest.java new file mode 100644 index 000000000..9d202c118 --- /dev/null +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/LocalMailCollectorTest.java @@ -0,0 +1,28 @@ +package fr.adrienbrault.idea.symfony2plugin.tests.profiler.collector; + +import fr.adrienbrault.idea.symfony2plugin.profiler.collector.LocalMailCollector; +import fr.adrienbrault.idea.symfony2plugin.profiler.dict.MailMessage; +import fr.adrienbrault.idea.symfony2plugin.profiler.utils.ProfilerUtil; +import junit.framework.TestCase; + +import java.io.File; +import java.util.Collection; + +/** + * @author Daniel Espendiller + */ +public class LocalMailCollectorTest extends TestCase { + protected String getTestDataPath() { + return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/fixtures"; + } + + public void testFoo() { + String data = ProfilerUtil.getContentForFile(new File(this.getTestDataPath() + "/mailer-dc7bb5")); + + LocalMailCollector localMailCollector = new LocalMailCollector(data); + + Collection messages = localMailCollector.getMessages(); + + assertTrue(messages.stream().anyMatch(mailMessage -> "Time for Symfony Mailer!".equals(mailMessage.title()))); + } +} diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/fixtures/mailer-dc7bb5 b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/fixtures/mailer-dc7bb5 new file mode 100644 index 000000000..ffc02e3e4 Binary files /dev/null and b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/collector/fixtures/mailer-dc7bb5 differ diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler new file mode 100644 index 000000000..56f98a7da Binary files /dev/null and b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler differ diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler-raw b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler-raw new file mode 100644 index 000000000..5b8f70e4e Binary files /dev/null and b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/profiler/fixtures/748f72-gzip-profiler-raw differ