Autopsy  4.13.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
StringsContentPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.contentviewers.textcontentviewer;
20 
21 import java.awt.Cursor;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.util.List;
25 import java.util.logging.Level;
26 import org.openide.util.NbBundle;
28 import javax.swing.JMenuItem;
29 import javax.swing.JOptionPane;
30 import org.apache.commons.lang3.StringUtils;
35 import org.sleuthkit.datamodel.Content;
36 import org.sleuthkit.datamodel.TskCoreException;
37 
41 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
42 public class StringsContentPanel extends javax.swing.JPanel {
43 
44  private static long currentOffset = 0;
45  private static final long PAGE_LENGTH = 16384;
46  private static final long serialVersionUID = 1L;
47  private final byte[] data = new byte[(int) PAGE_LENGTH];
48  private static int currentPage = 1;
49  private Content dataSource;
50  //string extract utility
51  private final StringExtract stringExtract = new StringExtract();
52  private static final Logger logger = Logger.getLogger(StringsContentPanel.class.getName());
53 
58  initComponents();
59  customizeComponents();
60  this.resetDisplay();
61  }
62 
63  private void customizeComponents() {
64  outputViewPane.setComponentPopupMenu(rightClickMenu);
65  ActionListener actList = (ActionEvent e) -> {
66  JMenuItem jmi = (JMenuItem) e.getSource();
67  if (jmi.equals(copyMenuItem)) {
68  outputViewPane.copy();
69  } else if (jmi.equals(selectAllMenuItem)) {
70  outputViewPane.selectAll();
71  }
72  };
73  copyMenuItem.addActionListener(actList);
74  selectAllMenuItem.addActionListener(actList);
75 
76  List<SCRIPT> supportedScripts = StringExtract.getSupportedScripts();
77  supportedScripts.forEach((s) -> {
78  languageCombo.addItem(s);
79  });
80 
81  }
82 
83  final void resetDisplay() {
84  // clear / reset the fields
85  currentPage = 1;
86  currentOffset = 0;
87  this.dataSource = null;
88  currentPageLabel.setText("");
89  totalPageLabel.setText("");
90  prevPageButton.setEnabled(false);
91  nextPageButton.setEnabled(false);
92  outputViewPane.setText(""); // reset the output view
93  setComponentsVisibility(false); // hides the components that not needed
94  }
95 
101  @SuppressWarnings("unchecked")
102  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
103  private void initComponents() {
104 
105  rightClickMenu = new javax.swing.JPopupMenu();
106  copyMenuItem = new javax.swing.JMenuItem();
107  selectAllMenuItem = new javax.swing.JMenuItem();
108  outputScrollPane = new javax.swing.JScrollPane();
109  outputViewPane = new javax.swing.JTextPane();
110  controlScrollPane = new javax.swing.JScrollPane();
111  controlPanel = new javax.swing.JPanel();
112  totalPageLabel = new javax.swing.JLabel();
113  ofLabel = new javax.swing.JLabel();
114  currentPageLabel = new javax.swing.JLabel();
115  pageLabel = new javax.swing.JLabel();
116  nextPageButton = new javax.swing.JButton();
117  pageLabel2 = new javax.swing.JLabel();
118  prevPageButton = new javax.swing.JButton();
119  goToPageLabel = new javax.swing.JLabel();
120  goToPageTextField = new javax.swing.JTextField();
121  languageCombo = new javax.swing.JComboBox<>();
122  languageLabel = new javax.swing.JLabel();
123 
124  copyMenuItem.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.copyMenuItem.text")); // NOI18N
125  rightClickMenu.add(copyMenuItem);
126 
127  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.selectAllMenuItem.text")); // NOI18N
128  rightClickMenu.add(selectAllMenuItem);
129 
130  setMinimumSize(new java.awt.Dimension(5, 5));
131  setPreferredSize(new java.awt.Dimension(100, 58));
132 
133  outputScrollPane.setPreferredSize(new java.awt.Dimension(640, 402));
134 
135  outputViewPane.setEditable(false);
136  outputViewPane.setFont(new java.awt.Font("Courier New", 0, 11)); // NOI18N
137  outputViewPane.setPreferredSize(new java.awt.Dimension(100, 40));
138  outputScrollPane.setViewportView(outputViewPane);
139 
140  controlScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
141  controlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
142 
143  totalPageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.totalPageLabel.text_1")); // NOI18N
144 
145  ofLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.ofLabel.text_1")); // NOI18N
146 
147  currentPageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.currentPageLabel.text_1")); // NOI18N
148  currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14));
149  currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14));
150 
151  pageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.pageLabel.text_1")); // NOI18N
152  pageLabel.setMaximumSize(new java.awt.Dimension(33, 14));
153  pageLabel.setMinimumSize(new java.awt.Dimension(33, 14));
154 
155  nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
156  nextPageButton.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.nextPageButton.text")); // NOI18N
157  nextPageButton.setBorderPainted(false);
158  nextPageButton.setContentAreaFilled(false);
159  nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
160  nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
161  nextPageButton.setPreferredSize(new java.awt.Dimension(55, 23));
162  nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
163  nextPageButton.addActionListener(new java.awt.event.ActionListener() {
164  public void actionPerformed(java.awt.event.ActionEvent evt) {
165  nextPageButtonActionPerformed(evt);
166  }
167  });
168 
169  pageLabel2.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.pageLabel2.text")); // NOI18N
170  pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14));
171  pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14));
172 
173  prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
174  prevPageButton.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.prevPageButton.text")); // NOI18N
175  prevPageButton.setBorderPainted(false);
176  prevPageButton.setContentAreaFilled(false);
177  prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
178  prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
179  prevPageButton.setPreferredSize(new java.awt.Dimension(55, 23));
180  prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
181  prevPageButton.addActionListener(new java.awt.event.ActionListener() {
182  public void actionPerformed(java.awt.event.ActionEvent evt) {
183  prevPageButtonActionPerformed(evt);
184  }
185  });
186 
187  goToPageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.goToPageLabel.text")); // NOI18N
188 
189  goToPageTextField.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.goToPageTextField.text")); // NOI18N
190  goToPageTextField.addActionListener(new java.awt.event.ActionListener() {
191  public void actionPerformed(java.awt.event.ActionEvent evt) {
192  goToPageTextFieldActionPerformed(evt);
193  }
194  });
195 
196  languageCombo.setToolTipText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.languageCombo.toolTipText")); // NOI18N
197  languageCombo.addActionListener(new java.awt.event.ActionListener() {
198  public void actionPerformed(java.awt.event.ActionEvent evt) {
199  languageComboActionPerformed(evt);
200  }
201  });
202 
203  languageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.languageLabel.text")); // NOI18N
204  languageLabel.setToolTipText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.languageLabel.toolTipText")); // NOI18N
205 
206  javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
207  controlPanel.setLayout(controlPanelLayout);
208  controlPanelLayout.setHorizontalGroup(
209  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
210  .addGroup(controlPanelLayout.createSequentialGroup()
211  .addContainerGap()
212  .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
213  .addGap(18, 18, 18)
214  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
215  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
216  .addComponent(ofLabel)
217  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
218  .addComponent(totalPageLabel)
219  .addGap(50, 50, 50)
220  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
221  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
222  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
223  .addGap(0, 0, 0)
224  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
225  .addGap(18, 18, 18)
226  .addComponent(goToPageLabel)
227  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
228  .addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
229  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
230  .addComponent(languageLabel)
231  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
232  .addComponent(languageCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
233  .addContainerGap())
234  );
235  controlPanelLayout.setVerticalGroup(
236  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
237  .addGroup(controlPanelLayout.createSequentialGroup()
238  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
239  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
240  .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
241  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
242  .addComponent(ofLabel)
243  .addComponent(totalPageLabel))
244  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
245  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
246  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
247  .addComponent(goToPageLabel)
248  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
249  .addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
250  .addComponent(languageCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
251  .addComponent(languageLabel)))
252  .addGap(0, 0, 0))
253  );
254 
255  controlScrollPane.setViewportView(controlPanel);
256 
257  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
258  this.setLayout(layout);
259  layout.setHorizontalGroup(
260  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
261  .addComponent(outputScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
262  .addComponent(controlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
263  );
264  layout.setVerticalGroup(
265  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
266  .addGroup(layout.createSequentialGroup()
267  .addComponent(controlScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
268  .addGap(7, 7, 7)
269  .addComponent(outputScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE))
270  );
271  }// </editor-fold>//GEN-END:initComponents
272 
273  private void languageComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_languageComboActionPerformed
274  if (dataSource != null) {
275  setDataView(dataSource, currentOffset);
276  }
277  }//GEN-LAST:event_languageComboActionPerformed
278 
279  private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed
280  String pageNumberStr = goToPageTextField.getText();
281  int pageNumber;
282  int maxPage = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1;
283  try {
284  pageNumber = Integer.parseInt(pageNumberStr);
285  } catch (NumberFormatException ex) {
286  pageNumber = maxPage + 1;
287  }
288  if (pageNumber > maxPage || pageNumber < 1) {
289  JOptionPane.showMessageDialog(this,
290  NbBundle.getMessage(this.getClass(),
291  "StringsTextViewer.goToPageTextField.msgDlg",
292  maxPage),
293  NbBundle.getMessage(this.getClass(),
294  "StringsTextViewer.goToPageTextField.err"),
295  JOptionPane.WARNING_MESSAGE);
296  return;
297  }
298  currentOffset = (pageNumber - 1) * PAGE_LENGTH;
299  currentPage = pageNumber;
300  currentPageLabel.setText(Integer.toString(currentPage));
301  setDataView(dataSource, currentOffset);
302  }//GEN-LAST:event_goToPageTextFieldActionPerformed
303 
304  private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
305  //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
306  currentOffset -= PAGE_LENGTH;
307  currentPage -= 1;
308  currentPageLabel.setText(Integer.toString(currentPage));
309  setDataView(dataSource, currentOffset);
310  }//GEN-LAST:event_prevPageButtonActionPerformed
311 
312  private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
313  //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
314  currentOffset += PAGE_LENGTH;
315  currentPage += 1;
316  currentPageLabel.setText(Integer.toString(currentPage));
317  setDataView(dataSource, currentOffset);
318  }//GEN-LAST:event_nextPageButtonActionPerformed
319 
320  // Variables declaration - do not modify//GEN-BEGIN:variables
321  private javax.swing.JPanel controlPanel;
322  private javax.swing.JScrollPane controlScrollPane;
323  private javax.swing.JMenuItem copyMenuItem;
324  private javax.swing.JLabel currentPageLabel;
325  private javax.swing.JLabel goToPageLabel;
326  private javax.swing.JTextField goToPageTextField;
327  private javax.swing.JComboBox<SCRIPT> languageCombo;
328  private javax.swing.JLabel languageLabel;
329  private javax.swing.JButton nextPageButton;
330  private javax.swing.JLabel ofLabel;
331  private javax.swing.JScrollPane outputScrollPane;
332  private javax.swing.JTextPane outputViewPane;
333  private javax.swing.JLabel pageLabel;
334  private javax.swing.JLabel pageLabel2;
335  private javax.swing.JButton prevPageButton;
336  private javax.swing.JPopupMenu rightClickMenu;
337  private javax.swing.JMenuItem selectAllMenuItem;
338  private javax.swing.JLabel totalPageLabel;
339  // End of variables declaration//GEN-END:variables
340 
347  void setDataView(Content dataSource, long offset) {
348  if (dataSource == null) {
349  return;
350  }
351  // change the cursor to "waiting cursor" for this operation
352  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
353  this.dataSource = dataSource;
354  int bytesRead = 0;
355  // set the data on the bottom and show it
356 
357  if (dataSource.getSize() > 0) {
358  try {
359  bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data
360  } catch (TskCoreException ex) {
361  logger.log(Level.WARNING, "Error while trying to show the String content.", ex); //NON-NLS
362  }
363  }
364  String text;
365  if (bytesRead > 0) {
366  //text = DataConversion.getString(data, bytesRead, 4);
367  final SCRIPT selScript = (SCRIPT) languageCombo.getSelectedItem();
368  stringExtract.setEnabledScript(selScript);
369  StringExtractResult res = stringExtract.extract(data, bytesRead, 0);
370  text = res.getText();
371  if (StringUtils.isBlank(text)) {
372  text = NbBundle.getMessage(this.getClass(),
373  "StringsTextViewer.setDataView.errorNoText", currentOffset,
374  currentOffset + PAGE_LENGTH);
375  }
376  } else {
377  text = NbBundle.getMessage(this.getClass(), "StringsTextViewer.setDataView.errorText", currentOffset,
378  currentOffset + PAGE_LENGTH);
379  }
380 
381  // disable or enable the next button
382  if (offset + PAGE_LENGTH < dataSource.getSize()) {
383  nextPageButton.setEnabled(true);
384  } else {
385  nextPageButton.setEnabled(false);
386  }
387 
388  if (offset == 0) {
389  prevPageButton.setEnabled(false);
390  currentPage = 1; // reset the page number
391  } else {
392  prevPageButton.setEnabled(true);
393  }
394 
395  int totalPage = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1;
396  totalPageLabel.setText(Integer.toString(totalPage));
397  currentPageLabel.setText(Integer.toString(currentPage));
398  outputViewPane.setText(text); // set the output view
399  setComponentsVisibility(true); // shows the components that not needed
400  outputViewPane.moveCaretPosition(0);
401 
402  this.setCursor(null);
403  }
404 
405  void setDataView(StringContent dataSource) {
406  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
407  try {
408  this.dataSource = null;
409 
410  // set the data on the bottom and show it
411  String text = dataSource.getString();
412  nextPageButton.setEnabled(false);
413  prevPageButton.setEnabled(false);
414  currentPage = 1;
415 
416  int totalPage = 1;
417  totalPageLabel.setText(Integer.toString(totalPage));
418  currentPageLabel.setText(Integer.toString(currentPage));
419  outputViewPane.setText(text); // set the output view
420  setComponentsVisibility(true); // shows the components that not needed
421  outputViewPane.moveCaretPosition(0);
422  } finally {
423  this.setCursor(null);
424  }
425  }
426 
432  private void setComponentsVisibility(boolean isVisible) {
433  currentPageLabel.setVisible(isVisible);
434  totalPageLabel.setVisible(isVisible);
435  ofLabel.setVisible(isVisible);
436  prevPageButton.setVisible(isVisible);
437  nextPageButton.setVisible(isVisible);
438  pageLabel.setVisible(isVisible);
439  pageLabel2.setVisible(isVisible);
440  goToPageTextField.setVisible(isVisible);
441  goToPageLabel.setVisible(isVisible);
442  languageCombo.setVisible(isVisible);
443  languageLabel.setVisible(isVisible);
444  }
445 
446 }
StringExtractResult extract(byte[] buff, int len, int offset)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.