commit 3b3d1e81780ec7d36bd101754a1da7574342f656 Author: tonikelope Date: Thu Sep 29 18:53:14 2016 +0200 Let's dance baby diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..66cf6065b --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ + +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml + + +*.class + + +*.jar +*.war +*.ear \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 000000000..add94f8db --- /dev/null +++ b/build.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + Builds, tests, and runs the project MegaBasterd. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 000000000..83f290abb --- /dev/null +++ b/manifest.mf @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +SplashScreen-Image: megabasterd/dot_com.jpg +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 000000000..40f9a87ed --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 000000000..3405b3455 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=71569f25 +build.xml.script.CRC32=cf2d2409 +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=84d88617 +nbproject/build-impl.xml.script.CRC32=69f27973 +nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.1.48 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 000000000..ec5c2b965 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,101 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=MegaBasterd +application.vendor=tonikelope +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MegaBasterd.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jackson-core-asl-1.9.13.jar=jackson-core-asl-1.9.13.jar +file.reference.jackson-mapper-asl-1.9.13.jar=jackson-mapper-asl-1.9.13.jar +file.reference.sqlite-jdbc-3.8.11.2.jar=sqlite-jdbc-3.8.11.2.jar +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.sqlite-jdbc-3.8.11.2.jar}:\ + ${file.reference.jackson-core-asl-1.9.13.jar}:\ + ${file.reference.jackson-mapper-asl-1.9.13.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=megabasterd.MainPanel +# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found. +manifest.custom.application.library.allowable.codebase= +# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts. +manifest.custom.caller.allowable.codebase= +# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed +manifest.custom.codebase= +# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) +manifest.custom.permissions= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs=-splash:src/megabasterd/dot_com.jpg +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 000000000..326cafde7 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + MegaBasterd + + + + + + + + + diff --git a/src/megabasterd/AboutDialog.form b/src/megabasterd/AboutDialog.form new file mode 100644 index 000000000..c3d6d37e4 --- /dev/null +++ b/src/megabasterd/AboutDialog.form @@ -0,0 +1,182 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/AboutDialog.java b/src/megabasterd/AboutDialog.java new file mode 100644 index 000000000..62e212b29 --- /dev/null +++ b/src/megabasterd/AboutDialog.java @@ -0,0 +1,224 @@ +package megabasterd; + +import java.awt.Font; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MainPanel.VERSION; +import static megabasterd.MiscTools.copyTextToClipboard; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.updateFont; + +/** + * + * @author tonikelope + */ +public final class AboutDialog extends javax.swing.JDialog { + + private static final String MEGA_URL = "https://mega.nz/#F!lYsRWaQB!uVhntmyKcVECRaOxAbcL4A"; + + + public AboutDialog(MainPanelView parent, boolean modal) { + + super(parent, modal); + + initComponents(); + + swingReflectionInvoke("setText", title_label , "MegaBasterd "+ VERSION); + updateFont(title_label, FONT_DEFAULT, Font.BOLD); + updateFont(subtitle_label, FONT_DEFAULT, Font.BOLD); + updateFont(mcdown_url_button, FONT_DEFAULT, Font.PLAIN); + updateFont(check_version_button, FONT_DEFAULT, Font.PLAIN); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + title_label = new javax.swing.JLabel(); + made_in_spain_label = new javax.swing.JLabel(); + subtitle_label = new javax.swing.JLabel(); + mc_logo_label = new javax.swing.JLabel(); + mcdown_url_button = new javax.swing.JButton(); + author_webpage_label = new javax.swing.JLabel(); + pica_roja_label = new javax.swing.JLabel(); + check_version_button = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("About"); + setIconImage(null); + setResizable(false); + + title_label.setFont(new java.awt.Font("Dialog", 1, 48)); // NOI18N + title_label.setText("MegaBasterd 1.8.1"); + title_label.setDoubleBuffered(true); + + made_in_spain_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/made_in_spain.jpg"))); // NOI18N + made_in_spain_label.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + made_in_spain_label.setDoubleBuffered(true); + + subtitle_label.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + subtitle_label.setForeground(new java.awt.Color(102, 102, 102)); + subtitle_label.setText("(Made with love by tonikelope)"); + subtitle_label.setDoubleBuffered(true); + + mc_logo_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/mega_crypter.png"))); // NOI18N + mc_logo_label.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + mc_logo_label.setDoubleBuffered(true); + + mcdown_url_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + mcdown_url_button.setText("Copy MegaBasterd URL"); + mcdown_url_button.setDoubleBuffered(true); + mcdown_url_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mcdown_url_buttonActionPerformed(evt); + } + }); + + author_webpage_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + author_webpage_label.setForeground(new java.awt.Color(102, 102, 102)); + author_webpage_label.setText("http://t0ni.xyz"); + author_webpage_label.setDoubleBuffered(true); + + pica_roja_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/pica_roja_big.png"))); // NOI18N + pica_roja_label.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + pica_roja_label.setDoubleBuffered(true); + + check_version_button.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + check_version_button.setText("Check version"); + check_version_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + check_version_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(subtitle_label) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(mcdown_url_button, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(check_version_button, javax.swing.GroupLayout.Alignment.TRAILING))) + .addGroup(layout.createSequentialGroup() + .addComponent(made_in_spain_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(author_webpage_label) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(pica_roja_label) + .addGap(18, 18, 18) + .addComponent(mc_logo_label)))) + .addComponent(title_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(title_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(subtitle_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(mcdown_url_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(check_version_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(made_in_spain_label) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pica_roja_label, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(mc_logo_label, javax.swing.GroupLayout.Alignment.TRAILING)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(author_webpage_label))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void mcdown_url_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mcdown_url_buttonActionPerformed + + + copyTextToClipboard(MEGA_URL); + + JOptionPane.showMessageDialog(this, "MEGA URL was copied to clipboard!"); + }//GEN-LAST:event_mcdown_url_buttonActionPerformed + + private void check_version_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_check_version_buttonActionPerformed + + + swingReflectionInvoke("setEnabled", check_version_button, false); + + final AboutDialog main = this; + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + try { + + MegaAPI ma = new MegaAPI(); + + HashMap folder_nodes = ma.getFolderNodes("lYsRWaQB", "uVhntmyKcVECRaOxAbcL4A"); + + boolean new_version=true; + + for(Object o:folder_nodes.values()) { + + HashMap current_node = (HashMap)o; + + if(((String)current_node.get("name")).contains("_"+VERSION.replaceAll(" *beta *", "")+".")) { + + JOptionPane.showMessageDialog(main, "You have the latest version ;)"); + + new_version = false; + + break; + } + } + + if(new_version) { + + JOptionPane.showMessageDialog(main, "NEW VERSION IS AVAILABLE!"); + } + + } catch (Exception ex) { + Logger.getLogger(AboutDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + swingReflectionInvoke("setEnabled", main.check_version_button, true); + + }}); + + }//GEN-LAST:event_check_version_buttonActionPerformed + + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel author_webpage_label; + private javax.swing.JButton check_version_button; + private javax.swing.JLabel made_in_spain_label; + private javax.swing.JLabel mc_logo_label; + private javax.swing.JButton mcdown_url_button; + private javax.swing.JLabel pica_roja_label; + private javax.swing.JLabel subtitle_label; + private javax.swing.JLabel title_label; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/Chunk.java b/src/megabasterd/Chunk.java new file mode 100644 index 000000000..d53442953 --- /dev/null +++ b/src/megabasterd/Chunk.java @@ -0,0 +1,89 @@ +package megabasterd; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import static java.lang.String.valueOf; + +/** + * + * @author tonikelope + */ +public final class Chunk { + + private final long _id; + private final long _offset; + private final long _size; + private final ByteArrayOutputStream _data_os; + private final String _url; + + + public Chunk(long id, long file_size, String file_url) throws ChunkInvalidIdException + { + _id = id; + + _offset = calculateOffset(); + + if(file_size > 0) + { + if(_offset>=file_size) { + throw new ChunkInvalidIdException(valueOf(id)); + } + + } else { + + if(id>1) { + + throw new ChunkInvalidIdException(valueOf(id)); + } + } + + _size = calculateSize(file_size); + + _url = file_url!=null?file_url+"/"+_offset+"-"+(_offset+_size-1):null; + + _data_os = new ByteArrayOutputStream((int)_size); + } + + public long getOffset() { + return _offset; + } + + public ByteArrayOutputStream getOutputStream() { + return _data_os; + } + + public long getId() { + return _id; + } + + public long getSize() { + return _size; + } + + public String getUrl() { + return _url; + } + + public ByteArrayInputStream getInputStream() { + return new ByteArrayInputStream(_data_os.toByteArray()); + } + + private long calculateSize(long file_size) + { + long chunk_size = (_id>=1 && _id<=7)?_id*128*1024:1024*1024; + + if(_offset + chunk_size > file_size) { + chunk_size = file_size - _offset; + } + + return chunk_size; + } + + private long calculateOffset() + { + long[] offs = {0, 128, 384, 768, 1280, 1920, 2688}; + + return (_id<=7?offs[(int)_id-1]:(3584 + (_id-8)*1024))*1024; + } + +} diff --git a/src/megabasterd/ChunkDownloader.java b/src/megabasterd/ChunkDownloader.java new file mode 100644 index 000000000..edb75453f --- /dev/null +++ b/src/megabasterd/ChunkDownloader.java @@ -0,0 +1,255 @@ +package megabasterd; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import static megabasterd.MainPanel.THROTTLE_SLICE_SIZE; +import static megabasterd.MainPanel.USER_AGENT; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; +import static megabasterd.MiscTools.swingReflectionInvoke; + + +/** + * + * @author tonikelope + */ +public class ChunkDownloader implements Runnable, SecureNotifiable { + + private final int _id; + private final Download _download; + private volatile boolean _exit; + private final Object _secure_notify_lock; + private boolean _notified; + + + public ChunkDownloader(int id, Download download) + { + _notified = false; + _exit = false; + _secure_notify_lock = new Object(); + _id = id; + _download = download; + } + + public void setExit(boolean exit) { + _exit = exit; + } + + public boolean isExit() { + return _exit; + } + + public Download getDownload() { + return _download; + } + + public int getId() { + return _id; + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(ChunkDownloader.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + @Override + public void run() + { + String worker_url=null; + Chunk chunk; + int reads, conta_error, http_status; + byte[] buffer = new byte[THROTTLE_SLICE_SIZE]; + InputStream is; + boolean error; + + System.out.println("Worker ["+_id+"]: let's do some work!"); + + try + { + conta_error = 0; + + error = false; + + while(!_exit && !_download.isStopped()) + { + if(worker_url == null || error) { + + worker_url=_download.getDownloadUrlForWorker(); + } + + chunk = new Chunk(_download.nextChunkId(), _download.getFile_size(), worker_url); + + URL url = new URL(chunk.getUrl()); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setReadTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setRequestProperty("User-Agent", USER_AGENT); + conn.setRequestProperty("Connection", "close"); + + error = false; + + try{ + + if(!_exit && !_download.isStopped()) { + + is = new ThrottledInputStream(conn.getInputStream(), _download.getMain_panel().getStream_supervisor()); + + http_status = conn.getResponseCode(); + + if ( http_status != HttpURLConnection.HTTP_OK ) + { + System.out.println("Failed : HTTP error code : " + http_status); + + error = true; + + } else { + + while(!_exit && !_download.isStopped() && !_download.getChunkwriter().isExit() && chunk.getOutputStream().size() < chunk.getSize() && (reads=is.read(buffer))!=-1 ) + { + chunk.getOutputStream().write(buffer, 0, reads); + + _download.getPartialProgressQueue().add(reads); + + _download.getProgress_meter().secureNotify(); + + if(_download.isPaused() && !_download.isStopped()) { + + _download.pause_worker(); + + secureWait(); + } + } + + is.close(); + + if(chunk.getOutputStream().size() < chunk.getSize()) { + + if(chunk.getOutputStream().size() > 0) + { + _download.getPartialProgressQueue().add(-1*chunk.getOutputStream().size()); + + _download.getProgress_meter().secureNotify(); + + } + + error = true; + } + } + + if(error && !_download.isStopped()) { + + _download.rejectChunkId(chunk.getId()); + + conta_error++; + + Thread.sleep(getWaitTimeExpBackOff(conta_error)*1000); + + } else if(!error) { + + System.out.println("Worker ["+_id+"] has downloaded chunk ["+chunk.getId()+"]!"); + + _download.getChunkwriter().getChunk_queue().put(chunk.getId(), chunk); + + _download.getChunkwriter().secureNotify(); + + conta_error = 0; + } + + } else if(_exit) { + + _download.rejectChunkId(chunk.getId()); + } + } + catch (IOException ex) + { + error = true; + + _download.rejectChunkId(chunk.getId()); + + if(chunk.getOutputStream().size() > 0) + { + _download.getPartialProgressQueue().add(-1*chunk.getOutputStream().size()); + + _download.getProgress_meter().secureNotify(); + } + + + Logger.getLogger(ChunkDownloader.class.getName()).log(Level.SEVERE, null, ex); + + } catch (InterruptedException ex) { + Logger.getLogger(ChunkDownloader.class.getName()).log(Level.SEVERE, null, ex); + } finally { + conn.disconnect(); + } + } + + }catch(ChunkInvalidIdException e) { + + }catch (IOException ex) { + _download.emergencyStopDownloader(ex.getMessage()); + Logger.getLogger(ChunkDownloader.class.getName()).log(Level.SEVERE, null, ex); + } + + if(!_exit) { + + swingReflectionInvoke("setEnabled", _download.getView().getSlots_spinner(), false); + + swingReflectionInvoke("setText", _download.getView().getSlot_status_label(), ""); + + _download.setFinishing_download(true); + + } else if(!_download.isFinishing_download()) { + + swingReflectionInvoke("setEnabled", _download.getView().getSlots_spinner(), true); + + swingReflectionInvoke("setText", _download.getView().getSlot_status_label(), ""); + } + + _download.stopThisSlot(this); + + _download.getChunkwriter().secureNotify(); + + System.out.println("Worker ["+_id+"]: bye bye"); + } + + + +} diff --git a/src/megabasterd/ChunkDownloaderMono.java b/src/megabasterd/ChunkDownloaderMono.java new file mode 100644 index 000000000..90e25f9f0 --- /dev/null +++ b/src/megabasterd/ChunkDownloaderMono.java @@ -0,0 +1,181 @@ +package megabasterd; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import static megabasterd.MainPanel.THROTTLE_SLICE_SIZE; +import static megabasterd.MainPanel.USER_AGENT; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; + + +/** + * + * @author tonikelope + */ +public class ChunkDownloaderMono extends ChunkDownloader { + + public ChunkDownloaderMono(int id, Download download) { + super(id, download); + } + + @Override + public void run() + { + String worker_url=null; + Chunk chunk; + int reads, max_reads, conta_error, http_status=200; + byte[] buffer = new byte[THROTTLE_SLICE_SIZE]; + boolean error; + + System.out.println("Worker ["+getId()+"]: let's do some work!"); + + HttpURLConnection conn=null; + + try + { + conta_error = 0; + + error = false; + + URL url = null; + + InputStream is=null; + + while(!isExit() && !getDownload().isStopped()) + { + if(worker_url == null || error) { + + worker_url=getDownload().getDownloadUrlForWorker(); + } + + chunk = new Chunk(getDownload().nextChunkId(), getDownload().getFile_size(), worker_url); + + if(url == null || error) { + + url = new URL(worker_url+"/"+chunk.getOffset()); + conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setReadTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setRequestProperty("User-Agent", USER_AGENT); + conn.setRequestProperty("Connection", "close"); + is = new ThrottledInputStream(conn.getInputStream(), getDownload().getMain_panel().getStream_supervisor()); + http_status = conn.getResponseCode(); + } + + if(http_status != HttpURLConnection.HTTP_OK){ + + System.out.println("Failed : HTTP error code : " + http_status); + + error = true; + + } else { + + error = false; + + try{ + + if(!isExit() && !getDownload().isStopped()) { + + while(!getDownload().isStopped() && !getDownload().getChunkwriter().isExit() && chunk.getOutputStream().size() < chunk.getSize() && (reads=is.read(buffer, 0, (max_reads=(int)(chunk.getSize() - chunk.getOutputStream().size())) <= buffer.length?max_reads:buffer.length))!=-1 ) + { + chunk.getOutputStream().write(buffer, 0, reads); + + getDownload().getPartialProgressQueue().add(reads); + + getDownload().getProgress_meter().secureNotify(); + + if(getDownload().isPaused() && !getDownload().isStopped()) { + + getDownload().pause_worker_mono(); + + secureWait(); + } + } + + if(chunk.getOutputStream().size() < chunk.getSize()) { + + if(chunk.getOutputStream().size() > 0) + { + getDownload().getPartialProgressQueue().add(-1*chunk.getOutputStream().size()); + + getDownload().getProgress_meter().secureNotify(); + } + + error = true; + } + + if(error && !getDownload().isStopped()) { + + getDownload().rejectChunkId(chunk.getId()); + + conta_error++; + + Thread.sleep(getWaitTimeExpBackOff(conta_error)*1000); + + } else if(!error) { + + getDownload().getChunkwriter().getChunk_queue().put(chunk.getId(), chunk); + + getDownload().getChunkwriter().secureNotify(); + + conta_error = 0; + } + + } else if(isExit()) { + + getDownload().rejectChunkId(chunk.getId()); + } + } + catch (IOException ex) + { + error = true; + + getDownload().rejectChunkId(chunk.getId()); + + if(chunk.getOutputStream().size() > 0) + { + getDownload().getPartialProgressQueue().add(-1*chunk.getOutputStream().size()); + + getDownload().getProgress_meter().secureNotify(); + } + + Logger.getLogger(ChunkDownloaderMono.class.getName()).log(Level.SEVERE, null, ex); + + } catch (InterruptedException ex) { + Logger.getLogger(ChunkDownloaderMono.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + }catch(ChunkInvalidIdException e) { + + }catch (MalformedURLException ex) { + + Logger.getLogger(ChunkDownloaderMono.class.getName()).log(Level.SEVERE, null, ex); + + } catch (IOException ex) { + + Logger.getLogger(ChunkDownloaderMono.class.getName()).log(Level.SEVERE, null, ex); + + getDownload().emergencyStopDownloader(ex.getMessage()); + + } finally { + + if(conn!=null) { + + conn.disconnect(); + } + } + + getDownload().stopThisSlot(this); + + getDownload().getChunkwriter().secureNotify(); + + System.out.println("Worker ["+getId()+"]: bye bye"); + + } +} diff --git a/src/megabasterd/ChunkInvalidIdException.java b/src/megabasterd/ChunkInvalidIdException.java new file mode 100644 index 000000000..67f62f5f7 --- /dev/null +++ b/src/megabasterd/ChunkInvalidIdException.java @@ -0,0 +1,15 @@ +package megabasterd; + + +/** + * + * @author tonikelope + */ +public final class ChunkInvalidIdException extends Exception { + + public ChunkInvalidIdException(String message) + { + super(message); + } + +} diff --git a/src/megabasterd/ChunkUploader.java b/src/megabasterd/ChunkUploader.java new file mode 100644 index 000000000..01feec91e --- /dev/null +++ b/src/megabasterd/ChunkUploader.java @@ -0,0 +1,311 @@ +package megabasterd; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import javax.crypto.CipherInputStream; +import javax.crypto.NoSuchPaddingException; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; +import static megabasterd.MiscTools.swingReflectionInvoke; + + + +/** + * + * @author tonikelope + */ +public final class ChunkUploader implements Runnable, SecureNotifiable { + + private final int _id; + private final Upload _upload; + private volatile boolean _exit; + private final Object _secure_notify_lock; + private boolean _notified=false; + + public ChunkUploader(int id, Upload upload) + { + _secure_notify_lock = new Object(); + _id = id; + _upload = upload; + _exit = false; + } + + public void setExit(boolean exit) { + _exit = exit; + } + + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(ChunkUploader.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + public int getId() { + return _id; + } + + @Override + public void run() + { + System.out.println("ChunkUploader "+_id+" hello!"); + + String worker_url=_upload.getUl_url(); + Chunk chunk; + int reads, to_read, conta_error, re, http_status, tot_bytes_up; + byte[] buffer = new byte[MainPanel.THROTTLE_SLICE_SIZE]; + boolean error; + OutputStream out; + + + try + { + RandomAccessFile f = new RandomAccessFile(_upload.getFile_name(), "r"); + + conta_error = 0; + + while(!_exit && !_upload.isStopped()) + { + chunk = new Chunk(_upload.nextChunkId(), _upload.getFile_size(), worker_url); + + f.seek(chunk.getOffset()); + + do + { + to_read = chunk.getSize() - chunk.getOutputStream().size() >= buffer.length?buffer.length:(int)(chunk.getSize() - chunk.getOutputStream().size()); + + re=f.read(buffer, 0, to_read); + + chunk.getOutputStream().write(buffer, 0, re); + + }while(!_exit && !_upload.isStopped() && chunk.getOutputStream().size() 0) { + + _upload.getPartialProgress().add(-1*tot_bytes_up); + + _upload.getProgress_meter().secureNotify(); + } + + error = true; + + } else { + + String content_encoding = conn.getContentEncoding(); + + InputStream is=(content_encoding!=null && content_encoding.equals("gzip"))?new GZIPInputStream(conn.getInputStream()):conn.getInputStream(); + + ByteArrayOutputStream byte_res = new ByteArrayOutputStream(); + + while( (reads=is.read(buffer)) != -1 ) { + + byte_res.write(buffer, 0, reads); + } + + String response = new String(byte_res.toByteArray()); + + conn.disconnect(); + + if(response.length() > 0) { + + if( MegaAPI.checkMEGAError(response) != 0 ) + { + error = true; + + } else { + + System.out.println("Completion handle -> "+response); + + _upload.setCompletion_handle(response); + } + } + } + } + + conn.disconnect(); + + if(error && !_upload.isStopped()) { + + _upload.rejectChunkId(chunk.getId()); + + conta_error++; + + if(!_exit) { + + Thread.sleep(getWaitTimeExpBackOff(conta_error)*1000); + } + + } else if(!error) { + + System.out.println(" Worker "+_id+" ha subido chunk "+chunk.getId()); + + _upload.getMac_generator().getChunk_queue().put(chunk.getId(), chunk); + + _upload.getMac_generator().secureNotify(); + + conta_error = 0; + } + + } + + } else if(_exit) { + + _upload.rejectChunkId(chunk.getId()); + } + } + catch (IOException ex) + { + _upload.rejectChunkId(chunk.getId()); + + if(tot_bytes_up > 0) { + + _upload.getPartialProgress().add(-1*tot_bytes_up); + + _upload.getProgress_meter().secureNotify(); + } + + Logger.getLogger(ChunkUploader.class.getName()).log(Level.SEVERE, null, ex); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | InterruptedException ex) { + Logger.getLogger(ChunkUploader.class.getName()).log(Level.SEVERE, null, ex); + + } finally { + conn.disconnect(); + } + + } + + }catch(ChunkInvalidIdException e) { + + } catch (IOException ex) { + + _upload.emergencyStopUploader(ex.getMessage()); + + Logger.getLogger(ChunkUploader.class.getName()).log(Level.SEVERE, null, ex); + } + + if(!_exit) { + + swingReflectionInvoke("setEnabled", _upload.getView().getSlots_spinner(), false); + + swingReflectionInvoke("setText", _upload.getView().getSlot_status_label(), ""); + + _upload.setFinishing_upload(true); + + } else if(!_upload.isFinishing_upload()) { + + swingReflectionInvoke("setEnabled", _upload.getView().getSlots_spinner(), true); + + swingReflectionInvoke("setText", _upload.getView().getSlot_status_label(), ""); + } + + _upload.stopThisSlot(this); + + _upload.getMac_generator().secureNotify(); + + System.out.println("ChunkUploader "+_id+" bye bye..."); + } + +} diff --git a/src/megabasterd/ChunkWriter.java b/src/megabasterd/ChunkWriter.java new file mode 100644 index 000000000..19aa6aed5 --- /dev/null +++ b/src/megabasterd/ChunkWriter.java @@ -0,0 +1,206 @@ +package megabasterd; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.crypto.CipherInputStream; +import javax.crypto.NoSuchPaddingException; + + +public final class ChunkWriter implements Runnable, SecureNotifiable { + + private long _last_chunk_id_written; + private long _bytes_written; + private final long _file_size; + private final ConcurrentHashMap _chunk_queue; + private final Download _download; + private final byte[] _byte_file_key; + private final byte[] _byte_iv; + private volatile boolean _exit; + private final ConcurrentLinkedQueue _rejectedChunkIds; + private final Object _secure_notify_lock; + private boolean _notified; + public ChunkWriter(Download downloader) throws Exception + { + _notified = false; + _exit = false; + _download = downloader; + _secure_notify_lock = new Object(); + _file_size = _download.getFile_size(); + _byte_file_key = CryptTools.initMEGALinkKey(_download.getFile_key()); + _byte_iv = CryptTools.initMEGALinkKeyIV(_download.getFile_key()); + _chunk_queue = new ConcurrentHashMap(); + _rejectedChunkIds = new ConcurrentLinkedQueue(); + + if(_download.getProgress() == 0) + { + _download.setLast_chunk_id_dispatched(0); + + _last_chunk_id_written = 0; + + _bytes_written = 0; + } + else + { + _last_chunk_id_written = calculateLastWrittenChunk(_download.getProgress()); + + _download.setLast_chunk_id_dispatched(_last_chunk_id_written); + + _bytes_written = _download.getProgress(); + } + } + + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(ChunkWriter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + public byte[] getByte_file_key() { + return _byte_file_key; + } + + public ConcurrentLinkedQueue getRejectedChunkIds() { + return _rejectedChunkIds; + } + + + public boolean isExit() { + return _exit; + } + + public long getBytes_written() { + return _bytes_written; + } + + public long getLast_chunk_id_written() { + return _last_chunk_id_written; + } + + + public ConcurrentHashMap getChunk_queue() + { + return _chunk_queue; + } + + @Override + public void run() + { + Chunk current_chunk; + CipherInputStream cis; + byte[] buffer = new byte[16*1024]; + int reads; + + try { + + System.out.println("Filewriter: let's do some work!"); + + if(_file_size > 0) + { + while(!_exit && (!_download.isStopped() || _download.chunkDownloadersRunning()) && _bytes_written < _file_size) + { + while(_chunk_queue.containsKey(_last_chunk_id_written+1)) + { + current_chunk = _chunk_queue.get(_last_chunk_id_written+1); + + cis = new CipherInputStream(current_chunk.getInputStream(), CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", _byte_file_key, CryptTools.forwardMEGALinkKeyIV(_byte_iv, _bytes_written))); + + while((reads=cis.read(buffer))!=-1) + { + _download.getOutput_stream().write(buffer, 0, reads); + } + + cis.close(); + + _bytes_written+=current_chunk.getSize(); + + _chunk_queue.remove(current_chunk.getId()); + + _last_chunk_id_written = current_chunk.getId(); + + } + + if(!_exit && (!_download.isStopped() || _download.chunkDownloadersRunning()) && _bytes_written < _file_size) + { + + System.out.println("Filewriter waiting for chunk ["+(_last_chunk_id_written+1)+"]..."); + + secureWait(); + } + } + } + + } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException ex) { + + System.out.println(ex.getMessage()); + + _download.emergencyStopDownloader(ex.getMessage()); + } + + _exit = true; + + _download.secureNotify(); + + System.out.println("Filewriter: bye bye"+_download.getFile().getName()); + } + + private long calculateLastWrittenChunk(long temp_file_size) + { + if(temp_file_size > 3584*1024) + { + return 7 + (long)Math.ceil((temp_file_size - 3584*1024)/(1024*1024)); + } + else + { + int i=0, tot=0; + + while(tot < temp_file_size) + { + i++; + tot+=i*128*1024; + } + + return i; + } + } +} diff --git a/src/megabasterd/ClipboardChangeObservable.java b/src/megabasterd/ClipboardChangeObservable.java new file mode 100644 index 000000000..3f537b0d4 --- /dev/null +++ b/src/megabasterd/ClipboardChangeObservable.java @@ -0,0 +1,16 @@ +package megabasterd; + +import java.awt.datatransfer.Transferable; + +/** + * + * @author tonikelope + */ +public interface ClipboardChangeObservable { + + void attachObserver(ClipboardChangeObserver observer); + void detachObserver(ClipboardChangeObserver observer); + Transferable getContents(); + void notifyChangeToMyObservers(); + +} diff --git a/src/megabasterd/ClipboardChangeObserver.java b/src/megabasterd/ClipboardChangeObserver.java new file mode 100644 index 000000000..ed75d6197 --- /dev/null +++ b/src/megabasterd/ClipboardChangeObserver.java @@ -0,0 +1,11 @@ +package megabasterd; + +/** + * + * @author tonikelope + */ +public interface ClipboardChangeObserver { + + void notifyClipboardChange(); + +} diff --git a/src/megabasterd/ClipboardSpy.java b/src/megabasterd/ClipboardSpy.java new file mode 100644 index 000000000..146fdb494 --- /dev/null +++ b/src/megabasterd/ClipboardSpy.java @@ -0,0 +1,186 @@ +package megabasterd; + +import static java.awt.Toolkit.getDefaultToolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.Transferable; +import static java.lang.System.out; +import static java.lang.Thread.sleep; +import java.util.concurrent.ConcurrentLinkedQueue; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; + + +public final class ClipboardSpy implements Runnable, ClipboardOwner, SecureNotifiable, ClipboardChangeObservable { + + private static final int SLEEP = 50; + + private final Clipboard _sysClip = getDefaultToolkit().getSystemClipboard(); + + private boolean _notified=false; + + private final ConcurrentLinkedQueue _observers; + + private Transferable _contents; + + private final Object _secure_notify_lock; + + + + public ClipboardSpy() { + _contents = null; + _secure_notify_lock = new Object(); + _observers = new ConcurrentLinkedQueue<>(); + } + @Override + public Transferable getContents() { + return _contents; + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + getLogger(ClipboardSpy.class.getName()).log(SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + + @Override + public void run() { + + _contents = getClipboardContents(); + + gainOwnership(_contents); + + out.println("Spying clipboard..."); + + secureWait(); + } + + + @Override + public void lostOwnership(Clipboard c, Transferable t) { + + _contents = getClipboardContents(); + + notifyChangeToMyObservers(); + + gainOwnership(_contents); + } + + + private Transferable getClipboardContents() { + + boolean error; + + Transferable c = null; + + do{ + error = false; + + try{ + + c = _sysClip.getContents(null); + + } catch(Exception ex) { + + error = true; + + try { + sleep(SLEEP); + } catch (InterruptedException ex1) { + getLogger(ClipboardSpy.class.getName()).log(SEVERE, null, ex1); + } + } + + }while(error); + + return c; + } + + + private void gainOwnership(Transferable t) { + + boolean error; + + do{ + error = false; + + try{ + + _sysClip.setContents(t,this); + + } catch(Exception ex) { + + error = true; + + try { + sleep(SLEEP); + } catch (InterruptedException ex1) { + getLogger(ClipboardSpy.class.getName()).log(SEVERE, null, ex1); + } + } + + }while(error); + + } + + @Override + public void attachObserver(ClipboardChangeObserver observer) { + + if(!_observers.contains(observer)) { + + _observers.add(observer); + } + } + + @Override + public void detachObserver(ClipboardChangeObserver observer) { + + _observers.remove(observer); + + } + + @Override + public void notifyChangeToMyObservers() { + + for(ClipboardChangeObserver o:_observers) { + + o.notifyClipboardChange(); + } + } + +} \ No newline at end of file diff --git a/src/megabasterd/ContentType.java b/src/megabasterd/ContentType.java new file mode 100644 index 000000000..4922cd641 --- /dev/null +++ b/src/megabasterd/ContentType.java @@ -0,0 +1,59 @@ +package megabasterd; + +import java.util.HashMap; + +public final class ContentType { + + private final HashMap _content_type; + + + public ContentType() { + + _content_type = new HashMap(); + + _content_type.put("mp2", "audio/x-mpeg"); + _content_type.put("mp3", "audio/x-mpeg"); + _content_type.put("mpga", "audio/x-mpeg"); + _content_type.put("mpega", "audio/x-mpeg"); + _content_type.put("mpg", "video/x-mpeg-system"); + _content_type.put("mpeg", "video/x-mpeg-system"); + _content_type.put("mpe", "video/x-mpeg-system"); + _content_type.put("vob", "video/x-mpeg-system"); + _content_type.put("aac", "audio/mp4"); + _content_type.put("mp4", "video/mp4"); + _content_type.put("mpg4", "video/mp4"); + _content_type.put("m4v", "video/x-m4v"); + _content_type.put("avi", "video/x-msvideo"); + _content_type.put("ogg", "application/ogg"); + _content_type.put("ogv", "video/ogg"); + _content_type.put("asf", "video/x-ms-asf-plugin"); + _content_type.put("asx", "video/x-ms-asf-plugin"); + _content_type.put("ogv", "video/ogg"); + _content_type.put("wmv", "video/x-ms-wmv"); + _content_type.put("wmx", "video/x-ms-wvx"); + _content_type.put("wma", "audio/x-ms-wma"); + _content_type.put("wav", "audio/wav"); + _content_type.put("3gp", "audio/3gpp"); + _content_type.put("3gp2", "audio/3gpp2"); + _content_type.put("divx", "video/divx"); + _content_type.put("flv", "video/flv"); + _content_type.put("mkv", "video/x-matroska"); + _content_type.put("mka", "audio/x-matroska"); + _content_type.put("m3u", "audio/x-mpegurl"); + _content_type.put("webm", "video/webm"); + _content_type.put("rm", "application/vnd.rn-realmedia"); + _content_type.put("ra", "audio/x-realaudio"); + _content_type.put("amr", "audio/amr"); + _content_type.put("flac", "audio/x-flac"); + _content_type.put("mov", "video/quicktime"); + _content_type.put("qt", "video/quicktime"); + } + public HashMap getContent_type() { + return _content_type; + } + + public String getMIME(String ext) + { + return _content_type.get(ext); + } +} diff --git a/src/megabasterd/ContextMenuMouseListener.java b/src/megabasterd/ContextMenuMouseListener.java new file mode 100644 index 000000000..cacbd5100 --- /dev/null +++ b/src/megabasterd/ContextMenuMouseListener.java @@ -0,0 +1,125 @@ +package megabasterd; + +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JPopupMenu; +import javax.swing.text.JTextComponent; + +public final class ContextMenuMouseListener extends MouseAdapter { + + private final JPopupMenu _popup; + private final Action _cutAction; + private final Action _copyAction; + private final Action _pasteAction; + private final Action _undoAction; + private final Action _selectAllAction; + private JTextComponent _textComponent; + private String _savedString; + private _Actions _lastActionSelected; + + + + public ContextMenuMouseListener() { + _savedString = ""; + _popup = new JPopupMenu(); + _undoAction = new AbstractAction("Undo") { + + @Override + public void actionPerformed(ActionEvent ae) { + _textComponent.setText(""); + _textComponent.replaceSelection(_savedString); + _lastActionSelected = _Actions.UNDO; + } + }; + + _popup.add(_undoAction); + _popup.addSeparator(); + _cutAction = new AbstractAction("Cut") { + + @Override + public void actionPerformed(ActionEvent ae) { + _lastActionSelected = _Actions.CUT; + _savedString = _textComponent.getText(); + _textComponent.cut(); + } + }; + + _popup.add(_cutAction); + + _copyAction = new AbstractAction("Copy") { + + @Override + public void actionPerformed(ActionEvent ae) { + _lastActionSelected = _Actions.COPY; + _textComponent.copy(); + } + }; + + _popup.add(_copyAction); + + _pasteAction = new AbstractAction("Paste") { + + @Override + public void actionPerformed(ActionEvent ae) { + _lastActionSelected = _Actions.PASTE; + _savedString = _textComponent.getText(); + _textComponent.paste(); + } + }; + + _popup.add(_pasteAction); + _popup.addSeparator(); + + _selectAllAction = new AbstractAction("Select All") { + + @Override + public void actionPerformed(ActionEvent ae) { + _lastActionSelected = _Actions.SELECT_ALL; + _textComponent.selectAll(); + } + }; + + _popup.add(_selectAllAction); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getModifiers() == InputEvent.BUTTON3_MASK) { + if (!(e.getSource() instanceof JTextComponent)) { + System.out.println(e.getSource().getClass()); + return; + } + + _textComponent = (JTextComponent) e.getSource(); + _textComponent.requestFocus(); + + boolean enabled = _textComponent.isEnabled(); + boolean editable = _textComponent.isEditable(); + boolean nonempty = !(_textComponent.getText() == null || _textComponent.getText().isEmpty()); + boolean marked = _textComponent.getSelectedText() != null; + + boolean pasteAvailable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).isDataFlavorSupported(DataFlavor.stringFlavor); + + _undoAction.setEnabled(enabled && editable && (_lastActionSelected == _Actions.CUT || _lastActionSelected == _Actions.PASTE)); + _cutAction.setEnabled(enabled && editable && marked); + _copyAction.setEnabled(enabled && marked); + _pasteAction.setEnabled(enabled && editable && pasteAvailable); + _selectAllAction.setEnabled(enabled && nonempty); + + int nx = e.getX(); + + if (nx > 500) { + nx -= _popup.getSize().width; + } + + _popup.show(e.getComponent(), nx, e.getY() - _popup.getSize().height); + } + } + private enum _Actions { UNDO, CUT, COPY, PASTE, SELECT_ALL } +} diff --git a/src/megabasterd/CryptTools.java b/src/megabasterd/CryptTools.java new file mode 100644 index 000000000..77469a3d4 --- /dev/null +++ b/src/megabasterd/CryptTools.java @@ -0,0 +1,316 @@ +package megabasterd; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPrivateKeySpec; +import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import static megabasterd.MiscTools.Bin2UrlBASE64; +import static megabasterd.MiscTools.UrlBASE642Bin; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.hex2bin; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.long2bytearray; + + +public final class CryptTools { + + public static final int[] AES_ZERO_IV_I32A = {0,0,0,0}; + + public static final byte[] AES_ZERO_IV = i32a2bin(AES_ZERO_IV_I32A); + + + + public static Cipher genDecrypter(String algo, String mode, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException + { + SecretKeySpec skeySpec = new SecretKeySpec(key, algo); + + Cipher decryptor = Cipher.getInstance(mode); + + if(iv != null) { + + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + + decryptor.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); + + } else { + + decryptor.init(Cipher.DECRYPT_MODE, skeySpec); + } + + return decryptor; + } + + public static Cipher genCrypter(String algo, String mode, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException + { + SecretKeySpec skeySpec = new SecretKeySpec(key, algo); + + Cipher cryptor = Cipher.getInstance(mode); + + if(iv != null) { + + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + + cryptor.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); + + } else { + + cryptor.init(Cipher.ENCRYPT_MODE, skeySpec); + } + + return cryptor; + } + + public static byte[] aes_cbc_encrypt(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/CBC/NoPadding", key, iv); + + return cryptor.doFinal(data); + } + + public static byte[] aes_cbc_decrypt(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CBC/NoPadding", key, iv); + + return decryptor.doFinal(data); + } + + public static byte[] aes_ecb_encrypt(byte[] data, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/ECB/NoPadding", key, null); + + return cryptor.doFinal(data); + } + + public static byte[] aes_ecb_decrypt(byte[] data, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/ECB/NoPadding", key, null); + + return decryptor.doFinal(data); + } + + public static byte[] aes_ctr_encrypt(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/CTR/NoPadding", key, iv); + + return cryptor.doFinal(data); + } + + public static byte[] aes_ctr_decrypt(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", key, iv); + + return decryptor.doFinal(data); + } + + public static int[] aes_cbc_encrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/CBC/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(cryptor.doFinal(i32a2bin(data))); + } + + public static int[] aes_cbc_decrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CBC/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(decryptor.doFinal(i32a2bin(data))); + } + + public static int[] aes_ecb_encrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/ECB/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(cryptor.doFinal(i32a2bin(data))); + } + + public static int[] aes_ecb_decrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/ECB/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(decryptor.doFinal(i32a2bin(data))); + } + + public static int[] aes_ctr_encrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/CTR/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(cryptor.doFinal(i32a2bin(data))); + } + + public static int[] aes_ctr_decrypt_ia32(int[] data, int[] key, int[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", i32a2bin(key), i32a2bin(iv)); + + return bin2i32a(decryptor.doFinal(i32a2bin(data))); + } + + public static byte[] rsaDecrypt(BigInteger enc_data, BigInteger p, BigInteger q, BigInteger d) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(p.multiply(q), d); + + KeyFactory factory = KeyFactory.getInstance("RSA"); + + Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); + + RSAPrivateKey privKey = (RSAPrivateKey) factory.generatePrivate(privateSpec); + + cipher.init(Cipher.DECRYPT_MODE, privKey); + + byte[] enc_data_byte = enc_data.toByteArray(); + + if(enc_data_byte[0] == 0) { + + enc_data_byte=Arrays.copyOfRange(enc_data_byte, 1, enc_data_byte.length); + } + + byte[] plainText = cipher.doFinal(enc_data_byte); + + if(plainText[0] == 0) { + + plainText=Arrays.copyOfRange(plainText, 1, plainText.length); + } + + return plainText; + } + + public static byte[] initMEGALinkKey(String key_string) throws Exception + { + int[] int_key = bin2i32a(UrlBASE642Bin(key_string)); + int[] k=new int[4]; + + k[0] = int_key[0] ^ int_key[4]; + k[1] = int_key[1] ^ int_key[5]; + k[2] = int_key[2] ^ int_key[6]; + k[3] = int_key[3] ^ int_key[7]; + + return i32a2bin(k); + } + + public static byte[] initMEGALinkKeyIV(String key_string) throws Exception + { + int[] int_key =bin2i32a(UrlBASE642Bin(key_string)); + int[] iv = new int[4]; + + iv[0] = int_key[4]; + iv[1] = int_key[5]; + iv[2] = 0; + iv[3] = 0; + + return i32a2bin(iv); + } + + public static byte[] forwardMEGALinkKeyIV(byte[] iv, long forward_bytes) + { + byte[] new_iv = new byte[iv.length]; + + System.arraycopy(iv, 0, new_iv, 0, iv.length/2); + + byte[] ctr = long2bytearray(forward_bytes/iv.length); + + System.arraycopy(ctr, 0, new_iv, iv.length/2, ctr.length); + + return new_iv; + } + + public static String decryptMegaDownloaderLink(String link) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, Exception, IllegalBlockSizeException, BadPaddingException + { + String[] keys = {"6B316F36416C2D316B7A3F217A30357958585858585858585858585858585858","ED1F4C200B35139806B260563B3D3876F011B4750F3A1A4A5EFD0BBE67554B44"}; + String iv="79F10A01844A0B27FF5B2D4E0ED3163E"; + + String enc_type, folder, dec_link; + + if((enc_type=findFirstRegex("mega://f?(enc[0-9]*)\\?", link, 1)) != null) + { + Cipher decrypter; + + String the_key=null; + + switch (enc_type.toLowerCase()) { + case "enc": + the_key = keys[0]; + break; + case "enc2": + the_key = keys[1]; + break; + } + + folder=findFirstRegex("mega://(f)?enc[0-9]*\\?", link, 1); + + decrypter = CryptTools.genDecrypter("AES", "AES/CBC/NoPadding", hex2bin(the_key), hex2bin(iv)); + + byte[] decrypted_data = decrypter.doFinal(UrlBASE642Bin(findFirstRegex("mega://f?enc[0-9]*\\?([\\da-zA-Z_,-]*)", link, 1))); + + dec_link=new String(decrypted_data).trim(); + + return "https://mega.nz/#"+(folder!=null?"f":"")+dec_link; + + } else { + return link; + } + } + + public static String MEGAUserHash(byte[] str, int[] aeskey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, Exception { + + int[] s32 = bin2i32a(str); + + int[] h32 = {0,0,0,0}; + + int[] iv = {0,0,0,0}; + + for(int i=0; i> selectDownloads() throws SQLException { + + ArrayList> downloads = new ArrayList<>(); + + ResultSet res; + + try (Connection conn = SqliteSingleton.getInstance().getConn(); Statement stat = conn.createStatement()) { + + res = stat.executeQuery("SELECT * FROM downloads"); + + while(res.next()) { + + HashMap download = new HashMap<>(); + + download.put("url", res.getString("url")); + download.put("path", res.getString("path")); + download.put("filename", res.getString("filename")); + download.put("filekey", res.getString("filekey")); + download.put("filesize", res.getLong("filesize")); + download.put("filepass", res.getString("filepass")); + download.put("filenoexpire", res.getString("filenoexpire")); + + downloads.add(download); + } + } + + return downloads; + } + + public static ArrayList> selectUploads() throws SQLException { + + ArrayList> uploads = new ArrayList<>(); + + ResultSet res; + + try (Connection conn = SqliteSingleton.getInstance().getConn(); Statement stat = conn.createStatement()) { + + res = stat.executeQuery("SELECT * FROM uploads"); + + while(res.next()) { + + HashMap upload = new HashMap<>(); + + upload.put("filename", res.getString("filename")); + upload.put("email", res.getString("email")); + upload.put("url", res.getString("url")); + upload.put("ul_key", res.getString("ul_key")); + upload.put("parent_node", res.getString("parent_node")); + upload.put("root_node", res.getString("root_node")); + upload.put("share_key", res.getString("share_key")); + upload.put("folder_link", res.getString("folder_link")); + uploads.add(upload); + } + } + + return uploads; + } + + public static HashMap selectMegaAccounts() throws SQLException { + + HashMap accounts = new HashMap<>(); + + ResultSet res; + + try (Connection conn = SqliteSingleton.getInstance().getConn(); Statement stat = conn.createStatement()) { + + res = stat.executeQuery("SELECT * FROM mega_accounts"); + + while(res.next()) { + + HashMap account_data = new HashMap<>(); + + account_data.put("password", res.getString("password")); + account_data.put("password_aes", res.getString("password_aes")); + account_data.put("user_hash", res.getString("user_hash")); + + accounts.put(res.getString("email"), account_data); + } + } + + return accounts; + } + + + public static void insertMegaAccount(String email, String password, String password_aes, String user_hash) throws SQLException { + + try (Connection conn = SqliteSingleton.getInstance().getConn(); PreparedStatement ps = conn.prepareStatement("INSERT OR REPLACE INTO mega_accounts (email,password,password_aes,user_hash) VALUES (?, ?, ?, ?)")) { + + ps.setString(1, email); + + ps.setString(2, password); + + ps.setString(3, password_aes); + + ps.setString(4, user_hash); + + ps.executeUpdate(); + + } + + } + + public static void deleteMegaAccount(String email) throws SQLException { + + try (Connection conn = SqliteSingleton.getInstance().getConn(); PreparedStatement ps = conn.prepareStatement("DELETE from mega_accounts WHERE email=?")) { + + ps.setString(1, email); + + ps.executeUpdate(); + } + } + + private DBTools() { + } + +} diff --git a/src/megabasterd/Download.java b/src/megabasterd/Download.java new file mode 100644 index 000000000..8826c4631 --- /dev/null +++ b/src/megabasterd/Download.java @@ -0,0 +1,1407 @@ +package megabasterd; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import static java.lang.Integer.parseInt; +import static java.lang.Long.valueOf; +import static java.lang.Math.ceil; +import static java.lang.System.out; +import static java.lang.Thread.sleep; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.FileChannel; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import static megabasterd.CryptTools.genCrypter; +import static megabasterd.DBTools.deleteDownload; +import static megabasterd.DBTools.insertDownload; +import static megabasterd.DBTools.selectSettingValueFromDB; +import static megabasterd.MainPanel.CONNECTION_TIMEOUT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MainPanel.USER_AGENT; +import static megabasterd.MiscTools.UrlBASE642Bin; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.formatBytes; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.truncateText; + +/** + * + * @author tonikelope + */ +public final class Download implements Transference, Runnable, SecureNotifiable { + + public static final boolean VERIFY_CBC_MAC_DEFAULT=false; + public static final boolean USE_SLOTS_DEFAULT=false; + public static final Object CBC_LOCK=new Object(); + + private final MainPanel _main_panel; + private DownloadView _view; + private final Object _secure_notify_lock; + private boolean _notified; + private final String _url; + private final String _download_path; + private String _file_name; + private String _file_key; + private Long _file_size; + private String _file_pass; + private String _file_noexpire; + private final boolean _use_slots; + private final int _slots; + private final boolean _restart; + private final ArrayList _chunkworkers; + private final ExecutorService _thread_pool; + private volatile boolean _exit; + private volatile boolean _pause; + private final ConcurrentLinkedQueue _partialProgressQueue; + private volatile long _progress; + private ChunkWriter _chunkwriter; + private ProgressMeter _progress_meter; + private SpeedMeter _speed_meter; + private String _last_download_url; + private boolean _provision_ok; + private boolean _finishing_download; + private int _paused_workers; + private File _file; + private boolean _checking_cbc; + private boolean _retrying_request; + private Double _progress_bar_rate; + private OutputStream _output_stream; + private String _fatal_error; + private boolean _status_error; + private final ConcurrentLinkedQueue _rejectedChunkIds; + private long _last_chunk_id_dispatched; + + public Download(MainPanel main_panel, String url, String download_path, String file_name, String file_key, Long file_size, String file_pass, String file_noexpire, boolean use_slots, int slots, boolean restart) { + _paused_workers = 0; + _last_chunk_id_dispatched = 0L; + _status_error = false; + _fatal_error = null; + _retrying_request = false; + _checking_cbc = false; + _finishing_download = false; + _pause = false; + _exit = false; + _last_download_url = null; + _provision_ok = true; + _progress = 0L; + _notified = false; + _main_panel = main_panel; + _url = url; + _download_path = download_path; + _file_name = file_name; + _file_key = file_key; + _file_size = file_size; + _file_pass = file_pass; + _file_noexpire = file_noexpire; + _use_slots = use_slots; + _slots = slots; + _restart= restart; + _secure_notify_lock = new Object(); + _chunkworkers = new ArrayList(); + _partialProgressQueue = new ConcurrentLinkedQueue(); + _rejectedChunkIds = new ConcurrentLinkedQueue(); + _thread_pool = newCachedThreadPool(); + _view = null; //Lazy init (getter!) + _speed_meter = null; //Lazy init (getter!) + _progress_meter = null; //Lazy init (getter!) + + } + + + public boolean isChecking_cbc() { + return _checking_cbc; + } + + public boolean isRetrying_request() { + return _retrying_request; + } + + public boolean isExit() { + return _exit; + } + + public boolean isPause() { + return _pause; + } + + public void setExit(boolean exit) { + _exit = exit; + } + + public void setPause(boolean pause) { + _pause = pause; + } + + public ChunkWriter getChunkwriter() { + return _chunkwriter; + } + + public ConcurrentLinkedQueue getPartialProgressQueue() { + return _partialProgressQueue; + } + + public void setFinishing_download(boolean finishing_download) { + _finishing_download = finishing_download; + } + + public boolean isFinishing_download() { + return _finishing_download; + } + + public String getFile_key() { + return _file_key; + } + + @Override + public long getProgress() { + return _progress; + } + + public OutputStream getOutput_stream() { + return _output_stream; + } + + public File getFile() { + return _file; + } + + public ArrayList getChunkworkers() { + return _chunkworkers; + } + + public void setPaused_workers(int paused_workers) { + _paused_workers = paused_workers; + } + + public String getUrl() { + return _url; + } + + public String getDownload_path() { + return _download_path; + } + + @Override + public String getFile_name() { + return _file_name; + } + + public String getFile_pass() { + return _file_pass; + } + + public String getFile_noexpire() { + return _file_noexpire; + } + + public boolean isUse_slots() { + return _use_slots; + } + + public int getSlots() { + return _slots; + } + + public void setLast_chunk_id_dispatched(long last_chunk_id_dispatched) { + _last_chunk_id_dispatched = last_chunk_id_dispatched; + } + + public boolean isProvision_ok() { + return _provision_ok; + } + + + + @Override + public ProgressMeter getProgress_meter() { + return _progress_meter == null?(_progress_meter = new ProgressMeter(this)):_progress_meter; + } + + + @Override + public SpeedMeter getSpeed_meter() { + return _speed_meter == null?(_speed_meter = new SpeedMeter(this, getMain_panel().getGlobal_dl_speed())):_speed_meter; + } + + @Override + public DownloadView getView() { + return _view == null?(_view = new DownloadView(this)):_view; + } + + @Override + public MainPanel getMain_panel() { + return _main_panel; + } + + @Override + public void start() { + + THREAD_POOL.execute(this); + } + + @Override + public void stop() { + + if(!isExit()) { + stopDownloader(); + } + } + + @Override + public void pause() { + + if(isPause()) { + + setPause(false); + + setPaused_workers(0); + + getSpeed_meter().secureNotify(); + + for(ChunkDownloader downloader:getChunkworkers()) { + + downloader.secureNotify(); + } + + getView().resume(); + + } else { + + setPause(true); + + getView().pause(); + } + + _main_panel.getDownload_manager().secureNotify(); + } + + @Override + public void restart() { + + Download new_download = new Download(getMain_panel(), getUrl(), getDownload_path(), getFile_name(), getFile_key(), getFile_size(), getFile_pass(), getFile_noexpire(), getMain_panel().isUse_slots_down(), getMain_panel().getDefault_slots_down(), true); + + getMain_panel().getDownload_manager().getTransference_remove_queue().add(this); + + getMain_panel().getDownload_manager().getTransference_provision_queue().add(new_download); + + getMain_panel().getDownload_manager().secureNotify(); + } + + @Override + public boolean isPaused() { + return isPause(); + } + + @Override + public boolean isStopped() { + return isExit(); + } + + @Override + public synchronized void checkSlotsAndWorkers() { + + if(!isExit()) { + + int sl = (int)swingReflectionInvokeAndWaitForReturn("getValue", getView().getSlots_spinner()); + + int cworkers = getChunkworkers().size(); + + if(sl != cworkers) { + + if(sl > cworkers) { + + startSlot(); + + } else { + + swingReflectionInvoke("setEnabled", getView().getSlots_spinner(), false); + + swingReflectionInvoke("setText", getView().getSlot_status_label(), "Removing slot..."); + + stopLastStartedSlot(); + } + } + } + } + + @Override + public void close() { + + _main_panel.getDownload_manager().getTransference_remove_queue().add(this); + + _main_panel.getDownload_manager().secureNotify(); + } + + @Override + public ConcurrentLinkedQueue getPartialProgress() { + return _partialProgressQueue; + } + + + @Override + public long getFile_size() { + return _file_size; + } + + + @Override + public void run() + { + swingReflectionInvoke("setVisible", getView().getClose_button(), false); + + String exit_message; + + getView().printStatusNormal("Starting download, please wait..."); + + try { + + if(!_exit) + { + String filename = _download_path+"/"+_file_name; + + _file = new File(filename); + + if(_file.getParent()!=null) + { + File path = new File(_file.getParent()); + + path.mkdirs(); + } + + if(!_file.exists()) + { + getView().printStatusNormal("Starting download (retrieving MEGA temp link), please wait..."); + + _last_download_url = getMegaFileDownloadUrl(_url); + + if(!_exit) + { + _retrying_request = false; + + swingReflectionInvoke("setMinimum", getView().getProgress_pbar(), 0); + swingReflectionInvoke("setMaximum", getView().getProgress_pbar(), Integer.MAX_VALUE); + swingReflectionInvoke("setStringPainted", getView().getProgress_pbar(), true); + + _progress_bar_rate = Integer.MAX_VALUE/(double)_file_size; + + filename = _download_path+"/"+_file_name; + + _file = new File(filename+".mctemp"); + + if(_file.exists()) + { + getView().printStatusNormal("File exists, resuming download..."); + + long max_size = calculateMaxTempFileSize(_file.length()); + + if(max_size != _file.length()) + { + getView().printStatusNormal("Truncating temp file..."); + + try (FileChannel out_truncate = new FileOutputStream(filename+".mctemp", true).getChannel()) + { + out_truncate.truncate(max_size); + } + } + + _progress = _file.length(); + swingReflectionInvoke("setValue", getView().getProgress_pbar(), (int)ceil(_progress_bar_rate*_progress)); + } + else + { + _progress = 0; + swingReflectionInvoke("setValue", getView().getProgress_pbar(), 0); + } + + _output_stream = new BufferedOutputStream(new FileOutputStream(_file, (_progress > 0))); + + _chunkwriter = new ChunkWriter(this); + + _thread_pool.execute(_chunkwriter); + + _thread_pool.execute(getProgress_meter()); + + _thread_pool.execute(getSpeed_meter()); + + getMain_panel().getGlobal_dl_speed().attachSpeedMeter(getSpeed_meter()); + + getMain_panel().getGlobal_dl_speed().secureNotify(); + + if(_use_slots) { + + for(int t=1; t <= _slots; t++) + { + ChunkDownloader c = new ChunkDownloader(t, this); + + _chunkworkers.add(c); + + _thread_pool.execute(c); + } + + swingReflectionInvoke("setVisible", getView().getSlots_label(), true); + + swingReflectionInvoke("setVisible", getView().getSlots_spinner(), true); + + } else { + + ChunkDownloaderMono c = new ChunkDownloaderMono(1, this); + + _chunkworkers.add(c); + + _thread_pool.execute(c); + } + + getView().printStatusNormal("Downloading file from mega ..."); + + getMain_panel().getDownload_manager().secureNotify(); + + swingReflectionInvoke("setVisible", getView().getPause_button(), true); + swingReflectionInvoke("setVisible", getView().getProgress_pbar(), true); + + secureWait(); + + out.println("Chunkdownloaders finished!"); + + getSpeed_meter().setExit(true); + + getSpeed_meter().secureNotify(); + + getProgress_meter().setExit(true); + + getProgress_meter().secureNotify(); + + _thread_pool.shutdown(); + + while(!_thread_pool.isTerminated()) + { + try { + + _thread_pool.awaitTermination(Integer.MAX_VALUE, DAYS); + + } catch (InterruptedException ex) { + getLogger(Download.class.getName()).log(SEVERE, null, ex); + } + } + + out.println("Downloader thread pool finished!"); + + getMain_panel().getGlobal_dl_speed().detachSpeedMeter(getSpeed_meter()); + + getMain_panel().getGlobal_dl_speed().secureNotify(); + + _output_stream.close(); + + swingReflectionInvoke("setVisible", getView().getSpeed_label(), false); + swingReflectionInvoke("setVisible", getView().getRemtime_label(), false); + swingReflectionInvoke("setVisible", getView().getPause_button(), false); + swingReflectionInvoke("setVisible", getView().getStop_button(), false); + swingReflectionInvoke("setVisible", getView().getSlots_label(), false); + swingReflectionInvoke("setVisible", getView().getSlots_spinner(), false); + + getMain_panel().getDownload_manager().secureNotify(); + + if(_progress == _file_size) + { + if(_file.length() != _file_size) { + + throw new IOException("El tamaño del fichero es incorrecto!"); + } + + swingReflectionInvoke("setValue", getView().getProgress_pbar(), Integer.MAX_VALUE); + + _file.renameTo(new File(filename)); + + String verify_file = selectSettingValueFromDB("verify_down_file"); + + if(verify_file!=null && verify_file.equals("yes")) + { + _checking_cbc = true; + + getView().printStatusNormal("Waiting to check file integrity..."); + + _progress = 0; + + swingReflectionInvoke("setValue", getView().getProgress_pbar(), 0); + + synchronized(CBC_LOCK) { + + getView().printStatusNormal("Checking file integrity, please wait..."); + + swingReflectionInvoke("setVisible", getView().getStop_button(), true); + + swingReflectionInvoke("setText", getView().getStop_button(), "CANCEL CHECK"); + + getMain_panel().getDownload_manager().getTransference_running_list().remove(this); + + getMain_panel().getDownload_manager().secureNotify(); + + if(verifyFileCBCMAC(filename)) + { + exit_message = "File successfully downloaded! (Integrity check PASSED)"; + + getView().printStatusOK(exit_message); + } + else if(!_exit) + { + exit_message = "BAD NEWS :( File is DAMAGED!"; + + getView().printStatusError(exit_message); + + _status_error = true; + } + else + { + exit_message = "File successfully downloaded! (but integrity check CANCELED)"; + + getView().printStatusOK(exit_message); + + _status_error = true; + + } + + swingReflectionInvoke("setVisible", getView().getStop_button(), false); + + swingReflectionInvoke("setValue", getView().getProgress_pbar(), Integer.MAX_VALUE); + + } + } + else + { + exit_message = "File successfully downloaded!"; + + getView().printStatusOK(exit_message); + + } + } + else if(_exit && _fatal_error == null) + { + getView().hideAllExceptStatus(); + + exit_message = "Download CANCELED!"; + + getView().printStatusError(exit_message); + + _status_error = true; + + if(_file!=null && !(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", getView().getKeep_temp_checkbox())){ + _file.delete(); + } + + } + else if(_fatal_error != null) + { + getView().hideAllExceptStatus(); + + getView().printStatusError(_fatal_error); + + _status_error = true; + + + } + else + { + getView().hideAllExceptStatus(); + + exit_message = "OOOPS!! Something (bad) happened but... what?"; + + getView().printStatusError(exit_message); + + _status_error = true; + + } + + + + } + else if(_fatal_error != null) + { + getView().hideAllExceptStatus(); + + getView().printStatusError(_fatal_error); + + _status_error = true; + } + else + { + getView().hideAllExceptStatus(); + + exit_message = "Download CANCELED!"; + + getView().printStatusError(exit_message); + + _status_error = true; + + if(_file!=null && !(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", getView().getKeep_temp_checkbox())){ + _file.delete(); + } + } + + } else { + + getView().hideAllExceptStatus(); + + swingReflectionInvoke("setVisible", getView().getFile_name_label(), false); + + exit_message = filename+" already exists!"; + + getView().printStatusError(exit_message); + + _status_error = true; + } + + } + else if(_fatal_error != null) + { + getView().hideAllExceptStatus(); + + getView().printStatusError(_fatal_error); + + _status_error = true; + } + else + { + getView().hideAllExceptStatus(); + + exit_message = "Download CANCELED!"; + + getView().printStatusError(exit_message); + + _status_error = true; + + if(_file!=null && !(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", getView().getKeep_temp_checkbox())){ + _file.delete(); + } + } + } + catch (IOException ex) { + exit_message = "I/O ERROR "+ex.getMessage(); + + getView().printStatusError(exit_message); + + _status_error = true; + + out.println(ex.getMessage()); + + } catch (Exception ex) { + out.println(ex.getMessage()); + } + + if(!_exit) { + + try { + deleteDownload(_url); + } catch (SQLException ex) { + getLogger(Download.class.getName()).log(SEVERE, null, ex); + } + + getMain_panel().getDownload_manager().getTransference_running_list().remove(this); + + getMain_panel().getDownload_manager().getTransference_finished_queue().add(this); + + getMain_panel().getDownload_manager().getScroll_panel().remove(getView()); + + getMain_panel().getDownload_manager().getScroll_panel().add(getView()); + + getMain_panel().getDownload_manager().secureNotify(); + } + + swingReflectionInvoke("setVisible", getView().getClose_button(), true); + + if(_status_error) { + swingReflectionInvoke("setVisible", getView().getRestart_button(), true); + } + + out.println(_file_name+" Downloader: bye bye"); + } + + public void provisionIt(boolean retry) throws MegaAPIException, MegaCrypterAPIException { + + getView().printStatusNormal("Provisioning download, please wait..."); + + swingReflectionInvoke("setVisible", getView().getCopy_link_button(), true); + + String[] file_info; + + String exit_message=null; + + try { + if(_file_name == null) + { + file_info = getMegaFileMetadata(_url, getMain_panel().getView(), retry); + + if(file_info==null) { + + _provision_ok=false; + + } else { + + _file_name = file_info[0]; + + _file_size = valueOf(file_info[1]); + + _file_key=file_info[2]; + + if(file_info.length == 5) + { + _file_pass = file_info[3]; + + _file_noexpire = file_info[4]; + } + + try { + + insertDownload(_url, _download_path, _file_name, _file_key, _file_size, _file_pass, _file_noexpire); + + } catch (SQLException ex) { + + _provision_ok=false; + + exit_message = "Error registering download (file "+ _download_path+"/"+_file_name +" already downloading)"; + } + } + } else if(_restart) { + + try { + + insertDownload(_url, _download_path, _file_name, _file_key, _file_size, _file_pass, _file_noexpire); + + } catch (SQLException ex) { + + _provision_ok=false; + + exit_message = "Error registering download (file "+ _download_path+"/"+_file_name +" already downloading)"; + } + } + }catch(MegaAPIException | MegaCrypterAPIException ex){ + + throw ex; + + } catch (Exception ex) { + + _provision_ok=false; + + exit_message = ex.getMessage(); + } + + if(!_provision_ok) { + + getView().hideAllExceptStatus(); + + if(_fatal_error != null) { + + getView().printStatusError(_fatal_error); + + }else if(exit_message!=null) { + + getView().printStatusError(exit_message); + } + + swingReflectionInvoke("setVisible", getView().getRestart_button(), true); + + } else { + + getView().printStatusNormal("Waiting to start..."); + + swingReflectionInvoke("setVisible", getView().getFile_name_label(), true); + + swingReflectionInvoke("setText", getView().getFile_name_label(), truncateText(_download_path+"/"+_file_name, 100)); + + swingReflectionInvoke("setToolTipText", getView().getFile_name_label(), _download_path+"/"+_file_name); + + swingReflectionInvoke("setVisible", getView().getFile_size_label(), true); + + swingReflectionInvoke("setText", getView().getFile_size_label(), formatBytes(_file_size)); + } + + swingReflectionInvoke("setVisible", getView().getClose_button(), true); + + } + + public synchronized void pause_worker() { + + if(++_paused_workers == _chunkworkers.size() && !_exit) { + + getView().printStatusNormal("Download paused!"); + swingReflectionInvoke("setText", getView().getPause_button(), "RESUME DOWNLOAD"); + swingReflectionInvoke("setEnabled", getView().getPause_button(), true); + } + } + + public void pause_worker_mono() { + + getView().printStatusNormal("Download paused!"); + swingReflectionInvoke("setText", getView().getPause_button(), "RESUME DOWNLOAD"); + swingReflectionInvoke("setEnabled", getView().getPause_button(), true); + + } + + public boolean checkDownloadUrl(String string_url) + { + try { + URL url = new URL(string_url+"/0-0"); + URLConnection connection = url.openConnection(); + connection.setConnectTimeout(CONNECTION_TIMEOUT); + connection.setRequestProperty("User-Agent", USER_AGENT); + + try (InputStream is = connection.getInputStream()) { + while(is.read()!=-1); + } + + return true; + + }catch (Exception ex) { + + return false; + } + } + + /* OJO!! -> ESTO ESTÁ CAMBIADO Y NO COMPROBADO!! */ + public synchronized String getDownloadUrlForWorker() throws IOException + { + if(_last_download_url != null && checkDownloadUrl(_last_download_url)) { + + return _last_download_url; + } + + boolean error; + + int api_error_retry=0; + + String download_url; + + do{ + + error = false; + + try { + if( findFirstRegex("://mega(\\.co)?\\.nz/", _url, 0) != null ) + { + MegaAPI ma = new MegaAPI(); + + download_url = ma.getMegaFileDownloadUrl(_url); + } + else + { + download_url = MegaCrypterAPI.getMegaFileDownloadUrl(_url, _file_pass, _file_noexpire); + } + + if(checkDownloadUrl(download_url)) { + + _last_download_url = download_url; + + } else { + + error = true; + } + + } catch(MegaCrypterAPIException | MegaAPIException e) { + + error = true; + + for(long i=getWaitTimeExpBackOff(api_error_retry++); i>0 && !_exit; i--) + { + try { + sleep(1000); + } catch (InterruptedException ex) {} + } + } + + }while(error); + + return _last_download_url; + } + + public synchronized void startSlot() + { + + int chunk_id = _chunkworkers.size()+1; + + ChunkDownloader c = new ChunkDownloader(chunk_id, this); + + _chunkworkers.add(c); + + try { + + _thread_pool.execute(c); + + }catch(java.util.concurrent.RejectedExecutionException e){out.println(e.getMessage());} + } + + public synchronized void stopLastStartedSlot() + { + if(!_chunkworkers.isEmpty()) { + + ChunkDownloader chunkdownloader = _chunkworkers.remove(_chunkworkers.size()-1); + chunkdownloader.setExit(true); + } + } + + public synchronized void stopThisSlot(ChunkDownloader chunkdownloader) + { + if(_chunkworkers.remove(chunkdownloader)) + { + swingReflectionInvokeAndWait("setValue", getView().getSlots_spinner(), (int)swingReflectionInvokeAndWaitForReturn("getValue", getView().getSlots_spinner())-1 ); + + if(!_exit && isPause() && _paused_workers == _chunkworkers.size()) { + + getView().printStatusNormal("Download paused!"); + + swingReflectionInvoke("setText", getView().getPause_button(), "RESUME DOWNLOAD"); + + swingReflectionInvoke("setEnabled", getView().getPause_button(), true); + } + } + } + + public synchronized boolean chunkDownloadersRunning() + { + return !getChunkworkers().isEmpty(); + } + + + @Override + public void updateProgress(int reads) + { + _progress+=reads; + + getView().updateProgressBar(_progress, _progress_bar_rate); + } + + + + private boolean verifyFileCBCMAC(String filename) throws FileNotFoundException, Exception, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException + { + int[] int_key = bin2i32a( UrlBASE642Bin(_file_key)); + + int[] iv = new int[2]; + + iv[0] = int_key[4]; + iv[1] = int_key[5]; + + int[] meta_mac = new int[2]; + + meta_mac[0] = int_key[6]; + meta_mac[1] = int_key[7]; + + int[] file_mac = {0,0,0,0}; + + int[] cbc_iv = {0,0,0,0}; + + Cipher cryptor = genCrypter("AES", "AES/CBC/NoPadding", _chunkwriter.getByte_file_key(), i32a2bin(cbc_iv)); + + try(FileInputStream is = new FileInputStream(new File(filename))) { + + long chunk_id=1; + long tot=0L; + byte[] chunk_buffer = new byte[16*1024]; + byte[] byte_block = new byte[16]; + int[] int_block; + int re, reads, to_read; + try + { + while(!_exit) + { + Chunk chunk = new Chunk(chunk_id++, _file_size, null); + + tot+=chunk.getSize(); + + int[] chunk_mac = {iv[0], iv[1], iv[0], iv[1]}; + + do + { + to_read = chunk.getSize() - chunk.getOutputStream().size() >= chunk_buffer.length?chunk_buffer.length:(int)(chunk.getSize() - chunk.getOutputStream().size()); + + re=is.read(chunk_buffer, 0, to_read); + + chunk.getOutputStream().write(chunk_buffer, 0, re); + + }while(!_exit && chunk.getOutputStream().size() 3584*1024) + { + long reminder = (size - 3584*1024)%(1024*1024); + + return reminder==0?size:(size - reminder); + } + else + { + int i=0, tot=0; + + while(tot < size) + { + i++; + tot+=i*128*1024; + } + + return tot==size?size:(tot-i*128*1024); + } + } + + + + public String[] getMegaFileMetadata(String link, MainPanelView panel, boolean retry_request) throws IOException, InterruptedException, MegaAPIException,MegaCrypterAPIException + { + + String[] file_info=null; + int retry=0, error_code; + boolean error; + + do + { + error=false; + + try + { + + if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) + { + MegaAPI ma = new MegaAPI(); + + file_info = ma.getMegaFileMetadata(link); + } + else + { + file_info = MegaCrypterAPI.getMegaFileMetadata(link, panel); + } + + } + catch(MegaAPIException | MegaCrypterAPIException ex) + { + error=true; + + error_code = parseInt(ex.getMessage()); + + switch(error_code) + { + case -2: + emergencyStopDownloader("Mega link is not valid!"); + break; + + case -14: + emergencyStopDownloader("Mega link is not valid!"); + break; + + case 22: + emergencyStopDownloader("MegaCrypter link is not valid!"); + break; + + case 23: + emergencyStopDownloader("MegaCrypter link is blocked!"); + break; + + case 24: + emergencyStopDownloader("MegaCrypter link has expired!"); + break; + + default: + + if(!retry_request) { + + throw ex; + } + + _retrying_request = true; + + swingReflectionInvoke("setEnabled", getMain_panel().getView().getNew_download_menu(), true); + + swingReflectionInvoke("setVisible", getView().getStop_button(), true); + + swingReflectionInvoke("setText", getView().getStop_button(), "CANCEL RETRY"); + + for(long i=getWaitTimeExpBackOff(retry++); i>0 && !_exit; i--) + { + if(error_code == -18) + { + getView().printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); + } + else + { + getView().printStatusError("Mega/MC APIException error "+ex.getMessage()+" (Retrying in "+i+" secs...)"); + } + + try { + sleep(1000); + } catch (InterruptedException ex2) {} + } + } + } catch(Exception ex) { + emergencyStopDownloader("Mega link is not valid!"); + } + + }while(!_exit && error); + + if(!error) { + swingReflectionInvoke("setText", getView().getStop_button(), "CANCEL DOWNLOAD"); + swingReflectionInvoke("setVisible", getView().getStop_button(), false); + } + + return file_info; + + + } + + public String getMegaFileDownloadUrl(String link) throws IOException, InterruptedException + { + + String dl_url=null; + int retry=0, error_code; + boolean error; + + do + { + error=false; + + try + { + if( findFirstRegex("://mega(\\.co)?\\.nz/", _url, 0) != null) + { + MegaAPI ma = new MegaAPI(); + + dl_url = ma.getMegaFileDownloadUrl(link); + + + } + else + { + dl_url = MegaCrypterAPI.getMegaFileDownloadUrl(link, _file_pass, _file_noexpire); + } + + } + catch(MegaAPIException | MegaCrypterAPIException ex) + { + error=true; + + error_code = parseInt(ex.getMessage()); + + switch(error_code) + { + case 22: + emergencyStopDownloader("MegaCrypter link is not valid!"); + break; + + case 23: + emergencyStopDownloader("MegaCrypter link is blocked!"); + break; + + case 24: + emergencyStopDownloader("MegaCrypter link has expired!"); + break; + + default: + + _retrying_request = true; + + swingReflectionInvoke("setVisible", getView().getStop_button(), true); + + swingReflectionInvoke("setText", getView().getStop_button(), "CANCEL RETRY"); + + for(long i=getWaitTimeExpBackOff(retry++); i>0 && !_exit; i--) + { + if(error_code == -18) + { + getView().printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); + } + else + { + getView().printStatusError("Mega/MC APIException error "+ex.getMessage()+" (Retrying in "+i+" secs...)"); + } + + try { + sleep(1000); + } catch (InterruptedException ex2) {} + } + } + } + + }while(!_exit && error); + + if(!error) { + swingReflectionInvoke("setText", getView().getStop_button(), "CANCEL DOWNLOAD"); + swingReflectionInvoke("setVisible", getView().getStop_button(), false); + } + + return dl_url; + } + + + public synchronized long nextChunkId() + { + Long next_id; + + if((next_id=_rejectedChunkIds.poll()) != null) { + return next_id; + } + else { + return ++_last_chunk_id_dispatched; + } + } + + public void rejectChunkId(long chunk_id) + { + _rejectedChunkIds.add(chunk_id); + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + getLogger(Download.class.getName()).log(SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + +} \ No newline at end of file diff --git a/src/megabasterd/DownloadManager.java b/src/megabasterd/DownloadManager.java new file mode 100644 index 000000000..8fe500ec3 --- /dev/null +++ b/src/megabasterd/DownloadManager.java @@ -0,0 +1,189 @@ +package megabasterd; + +import java.awt.Component; +import static java.lang.System.out; +import java.sql.SQLException; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import static megabasterd.DBTools.deleteDownload; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.swingReflectionInvoke; + + +public final class DownloadManager extends TransferenceManager { + + public DownloadManager(MainPanel main_panel) { + + super(main_panel, main_panel.getView().jPanel_scroll_down); + } + + public void remove(Download download) { + + getScroll_panel().remove(download.getView()); + + getTransference_start_queue().remove(download); + + getTransference_running_list().remove(download); + + getTransference_finished_queue().remove(download); + + if(download.isProvision_ok()) { + + try { + deleteDownload(download.getUrl()); + } catch (SQLException ex) { + getLogger(DownloadManager.class.getName()).log(SEVERE, null, ex); + } + } + + if(!getTransference_remove_queue().isEmpty()) { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), "Removing "+getTransference_remove_queue().size()+" downloads, please wait..."); + + } else { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), ""); + } + } + + public void provision(Download download, boolean retry) throws MegaAPIException, MegaCrypterAPIException + { + getScroll_panel().add(download.getView()); + + download.provisionIt(retry); + + if(download.isProvision_ok()) { + + out.println("Provision OK!"); + + getTransference_start_queue().add(download); + + if(getTransference_provision_queue().isEmpty()) { + + sortTransferenceStartQueue(); + + for(Transference down:getTransference_start_queue()) { + + getScroll_panel().remove((Component)down.getView()); + getScroll_panel().add((Component)down.getView()); + } + + for(Transference down:getTransference_finished_queue()) { + + getScroll_panel().remove((Component)down.getView()); + getScroll_panel().add((Component)down.getView()); + } + } + } else { + + out.println("Provision error!"); + + getTransference_finished_queue().add(download); + } + + if(getTransference_provision_queue().isEmpty()) { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), ""); + + } else { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), getTransference_provision_queue().size() + " downloads waiting for provision..."); + } + + if(retry) { + + secureNotify(); + } + } + + + @Override + public void run() { + + out.println("Download manager hello!"); + + while(true) + { + if(!getTransference_provision_queue().isEmpty()) + { + swingReflectionInvoke("setEnabled", getMain_panel().getView().getNew_download_menu(), false); + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), getTransference_provision_queue().size() + " downloads waiting for provision..."); + + while(!getTransference_provision_queue().isEmpty()) + { + final Download download = (Download)getTransference_provision_queue().poll(); + + if(download != null) { + + try{ + + provision(download, false); + + }catch (MegaAPIException | MegaCrypterAPIException ex) { + + out.println("Provision failed! Retrying in separated thread..."); + + getScroll_panel().remove(download.getView()); + + final DownloadManager main = this; + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run(){ + + try { + + main.provision(download, true); + + } catch (MegaAPIException | MegaCrypterAPIException ex1) { + + getLogger(DownloadManager.class.getName()).log(SEVERE, null, ex1); + } + + }}); + } + } + } + } + + if(!getTransference_remove_queue().isEmpty()) { + + swingReflectionInvoke("setEnabled", getMain_panel().getView().getNew_download_menu(), false); + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_down_label(), "Removing "+getTransference_remove_queue().size()+" downloads, please wait..."); + + while(!getTransference_remove_queue().isEmpty()) { + + Download download = (Download)getTransference_remove_queue().poll(); + + if(download != null) { + + remove(download); + + } + } + } + + while(!getTransference_start_queue().isEmpty() && getTransference_running_list().size() < getMain_panel().getMax_dl()) { + + Download download = (Download)getTransference_start_queue().poll(); + + if(download != null) { + + start(download); + } + } + + checkButtonsAndMenus(getMain_panel().getView().getClose_all_finished_down_button(), getMain_panel().getView().getPause_all_down_button(), getMain_panel().getView().getNew_download_menu(), getMain_panel().getView().getClean_all_down_menu()); + + out.println("Download manager wait"); + + secureWait(); + + out.println("Download manager let's go"); + } + + } + +} diff --git a/src/megabasterd/DownloadView.form b/src/megabasterd/DownloadView.form new file mode 100644 index 000000000..ea142b391 --- /dev/null +++ b/src/megabasterd/DownloadView.form @@ -0,0 +1,295 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/DownloadView.java b/src/megabasterd/DownloadView.java new file mode 100644 index 000000000..fc17b3783 --- /dev/null +++ b/src/megabasterd/DownloadView.java @@ -0,0 +1,497 @@ +package megabasterd; + +import java.awt.Color; +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JProgressBar; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.copyTextToClipboard; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.updateFont; + + +public final class DownloadView extends javax.swing.JPanel implements TransferenceView { + + private final Download _download; + + public JButton getClose_button() { + return close_button; + } + + public JButton getCopy_link_button() { + return copy_link_button; + } + + public JLabel getFile_name_label() { + return file_name_label; + } + + public JLabel getFile_size_label() { + return file_size_label; + } + + public JCheckBox getKeep_temp_checkbox() { + return keep_temp_checkbox; + } + + public JButton getPause_button() { + return pause_button; + } + + public JProgressBar getProgress_pbar() { + return progress_pbar; + } + + public JLabel getRemtime_label() { + return remtime_label; + } + + public JButton getRestart_button() { + return restart_button; + } + + public JLabel getSlot_status_label() { + return slot_status_label; + } + + public JLabel getSlots_label() { + return slots_label; + } + + public JSpinner getSlots_spinner() { + return slots_spinner; + } + + public JLabel getSpeed_label() { + return speed_label; + } + + public JLabel getStatus_label() { + return status_label; + } + + public JButton getStop_button() { + return stop_button; + } + + public DownloadView(Download download) { + + initComponents(); + + _download = download; + + updateFont(status_label, FONT_DEFAULT, Font.BOLD); + updateFont(remtime_label, FONT_DEFAULT, Font.PLAIN); + updateFont(speed_label, FONT_DEFAULT, Font.BOLD); + updateFont(progress_pbar, FONT_DEFAULT, Font.PLAIN); + updateFont(slots_label, FONT_DEFAULT, Font.BOLD); + updateFont(slots_spinner, FONT_DEFAULT, Font.PLAIN); + updateFont(pause_button, FONT_DEFAULT, Font.BOLD); + updateFont(stop_button, FONT_DEFAULT, Font.BOLD); + updateFont(keep_temp_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(file_name_label, FONT_DEFAULT, Font.PLAIN); + updateFont(file_size_label, FONT_DEFAULT, Font.BOLD); + updateFont(close_button, FONT_DEFAULT, Font.PLAIN); + updateFont(copy_link_button, FONT_DEFAULT, Font.PLAIN); + updateFont(restart_button, FONT_DEFAULT, Font.PLAIN); + updateFont(slot_status_label, FONT_DEFAULT, Font.PLAIN); + + swingReflectionInvokeAndWait("setModel", slots_spinner, new SpinnerNumberModel(_download.getMain_panel().getDefault_slots_down(), Download.MIN_WORKERS, Download.MAX_WORKERS, 1)); + swingReflectionInvoke("setEditable", swingReflectionInvokeAndWaitForReturn("getTextField", swingReflectionInvokeAndWaitForReturn("getEditor", slots_spinner)), false); + swingReflectionInvoke("setVisible", slots_spinner, false); + swingReflectionInvoke("setVisible", slots_label, false); + swingReflectionInvoke("setVisible", pause_button, false); + swingReflectionInvoke("setVisible", stop_button, false); + swingReflectionInvoke("setForeground", speed_label, new Color(0,128,255)); + swingReflectionInvoke("setVisible", speed_label, false); + swingReflectionInvoke("setVisible", remtime_label, false); + swingReflectionInvoke("setVisible", progress_pbar, false); + swingReflectionInvoke("setVisible", keep_temp_checkbox, false); + swingReflectionInvoke("setVisible", file_name_label, false); + swingReflectionInvoke("setVisible", close_button, false); + swingReflectionInvoke("setVisible", copy_link_button, false); + swingReflectionInvoke("setVisible", restart_button, false); + swingReflectionInvoke("setVisible", file_size_label, false); + + } + + public void hideAllExceptStatus() + { + swingReflectionInvoke("setVisible", speed_label, false); + swingReflectionInvoke("setVisible", remtime_label, false); + swingReflectionInvoke("setVisible", slots_spinner, false); + swingReflectionInvoke("setVisible", slots_label, false); + swingReflectionInvoke("setVisible", slot_status_label, false); + swingReflectionInvoke("setVisible", pause_button, false); + swingReflectionInvoke("setVisible", stop_button, false); + swingReflectionInvoke("setVisible", progress_pbar, false); + swingReflectionInvoke("setVisible", keep_temp_checkbox, false); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + status_label = new javax.swing.JLabel(); + slots_label = new javax.swing.JLabel(); + slots_spinner = new javax.swing.JSpinner(); + remtime_label = new javax.swing.JLabel(); + speed_label = new javax.swing.JLabel(); + progress_pbar = new javax.swing.JProgressBar(); + pause_button = new javax.swing.JButton(); + stop_button = new javax.swing.JButton(); + keep_temp_checkbox = new javax.swing.JCheckBox(); + file_name_label = new javax.swing.JLabel(); + close_button = new javax.swing.JButton(); + copy_link_button = new javax.swing.JButton(); + restart_button = new javax.swing.JButton(); + file_size_label = new javax.swing.JLabel(); + slot_status_label = new javax.swing.JLabel(); + + setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 204, 255), 4, true)); + + status_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + status_label.setText("status"); + status_label.setDoubleBuffered(true); + + slots_label.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + slots_label.setText("Slots"); + slots_label.setDoubleBuffered(true); + + slots_spinner.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + slots_spinner.setToolTipText("Slots"); + slots_spinner.setDoubleBuffered(true); + slots_spinner.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + slots_spinnerStateChanged(evt); + } + }); + + remtime_label.setFont(new java.awt.Font("Verdana", 1, 18)); // NOI18N + remtime_label.setText("remaining_time"); + remtime_label.setDoubleBuffered(true); + + speed_label.setFont(new java.awt.Font("Verdana", 3, 26)); // NOI18N + speed_label.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + speed_label.setText("speed"); + speed_label.setDoubleBuffered(true); + + progress_pbar.setFont(new java.awt.Font("Verdana", 1, 18)); // NOI18N + progress_pbar.setDoubleBuffered(true); + + pause_button.setBackground(new java.awt.Color(255, 153, 0)); + pause_button.setFont(new java.awt.Font("Verdana", 1, 16)); // NOI18N + pause_button.setForeground(java.awt.Color.white); + pause_button.setText("PAUSE DOWNLOAD"); + pause_button.setDoubleBuffered(true); + pause_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pause_buttonActionPerformed(evt); + } + }); + + stop_button.setBackground(new java.awt.Color(255, 0, 0)); + stop_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + stop_button.setForeground(java.awt.Color.white); + stop_button.setText("CANCEL DOWNLOAD"); + stop_button.setDoubleBuffered(true); + stop_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + stop_buttonActionPerformed(evt); + } + }); + + keep_temp_checkbox.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + keep_temp_checkbox.setSelected(true); + keep_temp_checkbox.setText("Keep temp file"); + keep_temp_checkbox.setDoubleBuffered(true); + + file_name_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + file_name_label.setForeground(new java.awt.Color(51, 51, 255)); + file_name_label.setText("file_name"); + file_name_label.setDoubleBuffered(true); + + close_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + close_button.setText("Close"); + close_button.setDoubleBuffered(true); + close_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + close_buttonActionPerformed(evt); + } + }); + + copy_link_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + copy_link_button.setText("Copy link"); + copy_link_button.setDoubleBuffered(true); + copy_link_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + copy_link_buttonActionPerformed(evt); + } + }); + + restart_button.setBackground(new java.awt.Color(51, 51, 255)); + restart_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + restart_button.setForeground(new java.awt.Color(255, 255, 255)); + restart_button.setText("Restart"); + restart_button.setDoubleBuffered(true); + restart_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + restart_buttonActionPerformed(evt); + } + }); + + file_size_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + file_size_label.setForeground(new java.awt.Color(51, 51, 255)); + file_size_label.setText("file_size"); + file_size_label.setDoubleBuffered(true); + + slot_status_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + slot_status_label.setDoubleBuffered(true); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(close_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(restart_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(keep_temp_checkbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(stop_button)) + .addComponent(progress_pbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(speed_label, javax.swing.GroupLayout.PREFERRED_SIZE, 340, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 73, Short.MAX_VALUE) + .addComponent(pause_button)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(status_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(slots_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(slots_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(file_name_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(copy_link_button, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(slot_status_label, javax.swing.GroupLayout.Alignment.TRAILING))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(remtime_label) + .addComponent(file_size_label)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(slots_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(slots_label) + .addComponent(status_label)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(file_name_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(file_size_label)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(slot_status_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(copy_link_button))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(remtime_label) + .addGap(6, 6, 6) + .addComponent(progress_pbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(speed_label) + .addComponent(pause_button)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(keep_temp_checkbox) + .addComponent(stop_button) + .addComponent(close_button) + .addComponent(restart_button)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void slots_spinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_slots_spinnerStateChanged + + THREAD_POOL.execute(new Runnable(){ + + @Override + public void run() { + + _download.checkSlotsAndWorkers(); + } + }); + }//GEN-LAST:event_slots_spinnerStateChanged + + + private void close_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_close_buttonActionPerformed + + _download.close(); + }//GEN-LAST:event_close_buttonActionPerformed + + private void copy_link_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copy_link_buttonActionPerformed + + copyTextToClipboard(_download.getUrl()); + + JOptionPane.showMessageDialog(_download.getMain_panel().getView(), "Link was copied to clipboard!"); + }//GEN-LAST:event_copy_link_buttonActionPerformed + + private void restart_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restart_buttonActionPerformed + + _download.restart(); + }//GEN-LAST:event_restart_buttonActionPerformed + + private void stop_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stop_buttonActionPerformed + + _download.stop(); + }//GEN-LAST:event_stop_buttonActionPerformed + + private void pause_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pause_buttonActionPerformed + + _download.pause(); + }//GEN-LAST:event_pause_buttonActionPerformed + + @Override + public void pause() { + + printStatusNormal("Pausing download ..."); + + swingReflectionInvoke("setEnabled", pause_button, false); + swingReflectionInvoke("setEnabled", speed_label, false); + swingReflectionInvoke("setEnabled", slots_label, false); + swingReflectionInvoke("setEnabled", slots_spinner, false); + swingReflectionInvoke("setVisible", stop_button, true); + swingReflectionInvoke("setVisible", keep_temp_checkbox, true); + } + + @Override + public void resume() { + + printStatusNormal("Downloading file from mega ..."); + + swingReflectionInvoke("setEnabled", pause_button, false); + swingReflectionInvoke("setEnabled", speed_label, true); + swingReflectionInvoke("setEnabled", slots_label, true); + swingReflectionInvoke("setEnabled", slots_spinner, true); + swingReflectionInvoke("setVisible", stop_button, false); + swingReflectionInvoke("setVisible", keep_temp_checkbox, false); + swingReflectionInvoke("setEnabled", pause_button, true); + swingReflectionInvoke("setText", pause_button, "PAUSE DOWNLOAD"); + swingReflectionInvoke("setVisible", _download.getMain_panel().getView().getPause_all_down_button(), true); + + } + + @Override + public void stop() { + + printStatusNormal("Stopping download safely, please wait..."); + + swingReflectionInvoke("setEnabled", speed_label, false); + swingReflectionInvoke("setEnabled", pause_button, false); + swingReflectionInvoke("setEnabled", stop_button, false); + swingReflectionInvoke("setEnabled", keep_temp_checkbox, false); + swingReflectionInvoke("setEnabled", slots_label, false); + swingReflectionInvoke("setEnabled", slots_spinner, false); + } + + + @Override + public void updateSpeed(String speed, Boolean visible) { + + if(speed != null) { + + swingReflectionInvoke("setText", speed_label, speed); + } + + if(visible != null) { + + swingReflectionInvoke("setVisible", speed_label, visible); + } + } + + @Override + public void updateRemainingTime(String rem_time, Boolean visible) { + + if(speed_label != null) { + + swingReflectionInvoke("setText", remtime_label, rem_time); + } + + if(visible != null) { + + swingReflectionInvoke("setVisible", remtime_label, visible); + } + } + + @Override + public void updateProgressBar(long progress, double bar_rate) { + + swingReflectionInvoke("setValue", progress_pbar, (int)Math.ceil(bar_rate*progress)); + + } + + @Override + public void printStatusError(String message) + { + swingReflectionInvoke("setForeground", status_label, Color.red); + swingReflectionInvoke("setText", status_label, message); + } + + @Override + public void printStatusOK(String message) + { + swingReflectionInvoke("setForeground", status_label, new Color(0,128,0)); + swingReflectionInvoke("setText", status_label, message); + } + + @Override + public void printStatusNormal(String message) + { + swingReflectionInvoke("setForeground", status_label, Color.BLACK); + swingReflectionInvoke("setText", status_label, message); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton close_button; + private javax.swing.JButton copy_link_button; + private javax.swing.JLabel file_name_label; + private javax.swing.JLabel file_size_label; + private javax.swing.JCheckBox keep_temp_checkbox; + private javax.swing.JButton pause_button; + private javax.swing.JProgressBar progress_pbar; + private javax.swing.JLabel remtime_label; + private javax.swing.JButton restart_button; + private javax.swing.JLabel slot_status_label; + private javax.swing.JLabel slots_label; + private javax.swing.JSpinner slots_spinner; + private javax.swing.JLabel speed_label; + private javax.swing.JLabel status_label; + private javax.swing.JButton stop_button; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/megabasterd/FileGrabberDialog.form b/src/megabasterd/FileGrabberDialog.form new file mode 100644 index 000000000..ef23ec2c7 --- /dev/null +++ b/src/megabasterd/FileGrabberDialog.form @@ -0,0 +1,313 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/FileGrabberDialog.java b/src/megabasterd/FileGrabberDialog.java new file mode 100644 index 000000000..e7ef38d8f --- /dev/null +++ b/src/megabasterd/FileGrabberDialog.java @@ -0,0 +1,642 @@ +package megabasterd; + +import java.awt.Color; +import java.awt.Font; +import java.io.File; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JTextField; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MiscTools.deleteAllExceptSelectedTreeItems; +import static megabasterd.MiscTools.deleteSelectedTreeItems; +import static megabasterd.MiscTools.formatBytes; +import static megabasterd.MiscTools.genID; +import static megabasterd.MiscTools.sortTree; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.updateFont; + +/** + * + * @author tonikelope + */ +public final class FileGrabberDialog extends javax.swing.JDialog { + + private boolean _upload; + private final ArrayList _files; + private String _base_path; + private long _total_space; + private String _last_selected_account; + private final MainPanel _main_panel; + + public boolean isUpload() { + return _upload; + } + + public ArrayList getFiles() { + return _files; + } + + public String getBase_path() { + return _base_path; + } + + public JComboBox getAccount_combobox() { + return account_combobox; + } + + public JTextField getDir_name_textfield() { + return dir_name_textfield; + } + + + + /** + * Creates new form FileGrabber + */ + public FileGrabberDialog(java.awt.Frame parent, boolean modal) { + + super(parent, modal); + + _last_selected_account = null; + _total_space = 0L; + _base_path = null; + _upload = false; + + initComponents(); + + updateFont(dance_button, FONT_DEFAULT, Font.PLAIN); + updateFont(add_files_button, FONT_DEFAULT, Font.PLAIN); + updateFont(add_folder_button, FONT_DEFAULT, Font.PLAIN); + updateFont(account_combobox, FONT_DEFAULT, Font.PLAIN); + updateFont(upload_name_label, FONT_DEFAULT, Font.PLAIN); + updateFont(account_label, FONT_DEFAULT, Font.PLAIN); + updateFont(used_space_label, FONT_DEFAULT, Font.BOLD); + updateFont(total_file_size_label, FONT_DEFAULT, Font.BOLD); + updateFont(file_tree, FONT_DEFAULT, Font.PLAIN); + updateFont(warning_label, FONT_DEFAULT, Font.PLAIN); + updateFont(dir_name_textfield, FONT_DEFAULT, Font.PLAIN); + updateFont(skip_button, FONT_DEFAULT, Font.PLAIN); + updateFont(skip_rest_button, FONT_DEFAULT, Font.PLAIN); + + dir_name_textfield.addMouseListener(new ContextMenuMouseListener()); + + _files = new ArrayList<>(); + + swingReflectionInvoke("setEnabled", warning_label, false); + swingReflectionInvoke("setEnabled", total_file_size_label, false); + swingReflectionInvoke("setEnabled", skip_button, false); + swingReflectionInvoke("setEnabled", skip_rest_button, false); + + _main_panel = ((MainPanelView)parent).getMain_panel(); + + if( _main_panel.getMega_accounts().size() > 0) { + + for (Object o:_main_panel.getMega_accounts().keySet()) { + + account_combobox.addItem((String)o); + } + + } else + { + swingReflectionInvoke("setText", used_space_label, "No MEGA accounts available!!"); + swingReflectionInvoke("setEnabled", dance_button, false); + swingReflectionInvoke("setEnabled", add_files_button, false); + swingReflectionInvoke("setEnabled", add_folder_button, false); + swingReflectionInvoke("setEnabled", file_tree, false); + swingReflectionInvoke("setEnabled", dir_name_textfield, false); + } + + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + file_tree_scrollpane = new javax.swing.JScrollPane(); + file_tree = new javax.swing.JTree(); + jPanel2 = new javax.swing.JPanel(); + upload_name_label = new javax.swing.JLabel(); + dir_name_textfield = new javax.swing.JTextField(); + account_label = new javax.swing.JLabel(); + account_combobox = new javax.swing.JComboBox<>(); + used_space_label = new javax.swing.JLabel(); + add_folder_button = new javax.swing.JButton(); + add_files_button = new javax.swing.JButton(); + dance_button = new javax.swing.JButton(); + total_file_size_label = new javax.swing.JLabel(); + warning_label = new javax.swing.JLabel(); + skip_rest_button = new javax.swing.JButton(); + skip_button = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("FileGrabber"); + + jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Files")); + + file_tree.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root"); + file_tree.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1)); + file_tree.setDoubleBuffered(true); + file_tree.setRootVisible(false); + file_tree_scrollpane.setViewportView(file_tree); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(file_tree_scrollpane) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(file_tree_scrollpane, javax.swing.GroupLayout.DEFAULT_SIZE, 290, Short.MAX_VALUE) + ); + + upload_name_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + upload_name_label.setText("Upload name:"); + upload_name_label.setDoubleBuffered(true); + + dir_name_textfield.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + dir_name_textfield.setDoubleBuffered(true); + + account_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + account_label.setText("Account:"); + account_label.setDoubleBuffered(true); + + account_combobox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + account_combobox.setDoubleBuffered(true); + account_combobox.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + account_comboboxItemStateChanged(evt); + } + }); + + used_space_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + used_space_label.setText("Used space: 0.00GB"); + used_space_label.setDoubleBuffered(true); + + add_folder_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + add_folder_button.setText("Add folder"); + add_folder_button.setDoubleBuffered(true); + add_folder_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + add_folder_buttonActionPerformed(evt); + } + }); + + add_files_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + add_files_button.setText("Add files"); + add_files_button.setDoubleBuffered(true); + add_files_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + add_files_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(upload_name_label) + .addComponent(account_label)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(add_files_button, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(add_folder_button, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(dir_name_textfield) + .addComponent(account_combobox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(used_space_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(upload_name_label) + .addComponent(dir_name_textfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(account_label) + .addComponent(account_combobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(used_space_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(add_files_button) + .addComponent(add_folder_button)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + dance_button.setBackground(new java.awt.Color(102, 204, 255)); + dance_button.setFont(new java.awt.Font("Dialog", 1, 24)); // NOI18N + dance_button.setForeground(new java.awt.Color(255, 255, 255)); + dance_button.setText("Let's dance, baby"); + dance_button.setDoubleBuffered(true); + dance_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dance_buttonActionPerformed(evt); + } + }); + + total_file_size_label.setFont(new java.awt.Font("Dialog", 1, 28)); // NOI18N + total_file_size_label.setText("[0 B]"); + total_file_size_label.setDoubleBuffered(true); + + warning_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + warning_label.setText("If you DO NOT want to upload some folder or file you can REMOVE it."); + warning_label.setDoubleBuffered(true); + + skip_rest_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + skip_rest_button.setText("REMOVE ALL EXCEPT THIS"); + skip_rest_button.setDoubleBuffered(true); + skip_rest_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + skip_rest_buttonActionPerformed(evt); + } + }); + + skip_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + skip_button.setText("REMOVE THIS"); + skip_button.setDoubleBuffered(true); + skip_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + skip_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(total_file_size_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(warning_label) + .addGroup(layout.createSequentialGroup() + .addComponent(skip_rest_button) + .addGap(18, 18, 18) + .addComponent(skip_button))) + .addGap(0, 91, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 300, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(total_file_size_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(warning_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(skip_rest_button) + .addComponent(skip_button)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(dance_button) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void add_files_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_add_files_buttonActionPerformed + + + javax.swing.JFileChooser filechooser = new javax.swing.JFileChooser(); + + filechooser.setDialogTitle("Add files"); + filechooser.setAcceptAllFileFilterUsed(false); + filechooser.setMultiSelectionEnabled(true); + + if( filechooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION ) { + + swingReflectionInvoke("setText", total_file_size_label, ""); + + File[] files_selected = filechooser.getSelectedFiles(); + + _base_path = files_selected[0].getParentFile().getAbsolutePath(); + + System.out.println(_base_path); + + swingReflectionInvoke("setText", dir_name_textfield, files_selected[0].getParentFile().getName()+"_"+genID(10)); + + DefaultMutableTreeNode root = new DefaultMutableTreeNode(filechooser.getSelectedFile().getParent()); + + for(File file:files_selected) { + + DefaultMutableTreeNode current_file = new DefaultMutableTreeNode( file.getName() + (file.isFile()?" ["+ formatBytes(file.length())+"]":"") ); + + root.add(current_file); + } + + file_tree.setModel(new DefaultTreeModel( sortTree(root))); + + _genFileList(); + + file_tree.setRootVisible(root.getChildCount() > 0); + } + }//GEN-LAST:event_add_files_buttonActionPerformed + + private void add_folder_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_add_folder_buttonActionPerformed + + + javax.swing.JFileChooser filechooser = new javax.swing.JFileChooser(); + filechooser.setDialogTitle("Add directory"); + filechooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); + filechooser.setAcceptAllFileFilterUsed(false); + + if( filechooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION ) { + + swingReflectionInvoke("setText", total_file_size_label, ""); + + _base_path = filechooser.getSelectedFile().getAbsolutePath(); + + System.out.println(_base_path); + + swingReflectionInvoke("setText", dir_name_textfield, filechooser.getSelectedFile().getName()+"_"+genID(10)); + + DefaultMutableTreeNode root = new DefaultMutableTreeNode(filechooser.getSelectedFile().getAbsolutePath()); + + _genFileTree(filechooser.getSelectedFile().getAbsolutePath(), root); + + file_tree.setModel(new DefaultTreeModel(sortTree(root))); + + _genFileList(); + + file_tree.setRootVisible(root.getChildCount() > 0); + } + }//GEN-LAST:event_add_folder_buttonActionPerformed + + private void dance_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dance_buttonActionPerformed + + _upload = true; + + swingReflectionInvoke("setVisible", this, false); + }//GEN-LAST:event_dance_buttonActionPerformed + + private void account_comboboxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_account_comboboxItemStateChanged + + + if(!swingReflectionInvokeAndWaitForReturn("getSelectedItem", account_combobox).equals(_last_selected_account)) { + + _last_selected_account = (String) swingReflectionInvokeAndWaitForReturn("getSelectedItem", account_combobox); + + final String email = (String)account_combobox.getSelectedItem(); + + final FileGrabberDialog fg = this; + + swingReflectionInvoke("setForeground", fg.used_space_label, Color.black); + + swingReflectionInvoke("setText", used_space_label, "Checking account quota, please wait..."); + + swingReflectionInvoke("setEnabled", account_combobox, false); + + swingReflectionInvoke("setEnabled", dance_button, false); + swingReflectionInvoke("setEnabled", add_files_button, false); + swingReflectionInvoke("setEnabled", add_folder_button, false); + swingReflectionInvoke("setEnabled", file_tree, false); + swingReflectionInvoke("setEnabled", dir_name_textfield, false); + swingReflectionInvoke("setEnabled", total_file_size_label, false); + swingReflectionInvoke("setEnabled", skip_button, false); + swingReflectionInvoke("setEnabled", skip_rest_button, false); + swingReflectionInvoke("setEnabled", warning_label, false); + + MainPanel.THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + HashMap account_info = (HashMap)fg._main_panel.getMega_accounts().get(email); + + Long[] quota = null; + + MegaAPI ma = fg._main_panel.getMega_active_accounts().get(fg.account_combobox.getSelectedItem()); + + if(ma == null) { + + ma = new MegaAPI(); + + try { + + ma.login(email, (String)account_info.get("password")); + + fg._main_panel.getMega_active_accounts().put(email, ma); + + quota = ma.getQuota(); + + } catch (Exception ex) { + + Logger.getLogger(FileGrabberDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + } else { + + quota = ma.getQuota(); + } + + if(quota != null) { + + if(quota[0] <= Math.round((double)quota[1]/2)) { + + swingReflectionInvoke("setForeground", fg.used_space_label, new Color(0,128,0)); + + } else if(quota[0] < quota[1]) { + + swingReflectionInvoke("setForeground", fg.used_space_label, new Color(230,115,0)); + + } else { + + swingReflectionInvoke("setForeground", fg.used_space_label, Color.red); + } + + swingReflectionInvoke("setText", fg.used_space_label, formatBytes(quota[0])+" / "+formatBytes(quota[1])); + + } else { + + swingReflectionInvoke("setForeground", fg.used_space_label, Color.red); + + swingReflectionInvoke("setText", fg.used_space_label, "ERROR checking account quota! (Retry in few minutes)."); + } + + + swingReflectionInvoke("setEnabled", fg.dance_button, true); + swingReflectionInvoke("setEnabled", fg.add_files_button, true); + swingReflectionInvoke("setEnabled", fg.add_folder_button, true); + swingReflectionInvoke("setEnabled", fg.file_tree, true); + swingReflectionInvoke("setEnabled", fg.dir_name_textfield, true); + swingReflectionInvoke("setEnabled", fg.account_combobox, true); + swingReflectionInvoke("setEnabled", fg.total_file_size_label, true); + swingReflectionInvoke("setEnabled", fg.skip_button, true); + swingReflectionInvoke("setEnabled", fg.skip_rest_button, true); + swingReflectionInvoke("setEnabled", fg.warning_label, true); + + + } }); + + } + + }//GEN-LAST:event_account_comboboxItemStateChanged + + private void skip_rest_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skip_rest_buttonActionPerformed + + + if( deleteAllExceptSelectedTreeItems(file_tree)) { + + _genFileList(); + + file_tree.setRootVisible(((TreeNode)file_tree.getModel().getRoot()).getChildCount() > 0); + } + }//GEN-LAST:event_skip_rest_buttonActionPerformed + + private void skip_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skip_buttonActionPerformed + + + if(deleteSelectedTreeItems(file_tree)) { + + _genFileList(); + + file_tree.setRootVisible(((TreeNode)file_tree.getModel().getRoot()).getChildCount() > 0); + } + }//GEN-LAST:event_skip_buttonActionPerformed + + private void _genFileTree(String directoryName, DefaultMutableTreeNode root) { + + File directory = new File(directoryName); + + File[] fList = directory.listFiles(); + + if(fList != null) { + + for (File file : fList) { + + if (file.isFile()) { + + DefaultMutableTreeNode current_file = new DefaultMutableTreeNode( file.getName() + " ["+formatBytes(file.length())+"]" ); + + root.add(current_file); + + } else if (file.isDirectory() && file.listFiles().length > 0) { + + DefaultMutableTreeNode current_dir = new DefaultMutableTreeNode( file.getName() ); + + root.add(current_dir); + + _genFileTree(file.getAbsolutePath(), current_dir); + } + } + + } + } + + private void _genFileList() { + + _files.clear(); + + _total_space = 0L; + + DefaultMutableTreeNode root = (DefaultMutableTreeNode)file_tree.getModel().getRoot(); + + Enumeration files_tree = root.depthFirstEnumeration(); + + int conta_files = 0; + + while(files_tree.hasMoreElements()) { + + DefaultMutableTreeNode node = (DefaultMutableTreeNode)files_tree.nextElement(); + + if(node.isLeaf() && node!=root) { + + String path = ""; + + Object[] object_path = node.getUserObjectPath(); + + for(Object p:object_path) { + + path+="/"+p; + } + + path = path.replaceAll("^//", "/").trim().replaceAll(" \\[[0-9,]+ [A-Z]+\\]$", ""); + + File file = new File(path); + + if(file.isFile()) { + + conta_files++; + + _total_space+=file.length(); + + _files.add(file); + } + } + } + + swingReflectionInvoke("setText", total_file_size_label, "["+formatBytes(_total_space)+"]"); + + if(conta_files == 0) { + + swingReflectionInvoke("setEnabled", dance_button, false); + swingReflectionInvoke("setEnabled", warning_label, false); + swingReflectionInvoke("setEnabled", skip_button, false); + swingReflectionInvoke("setEnabled", skip_rest_button, false); + swingReflectionInvoke("setEnabled", total_file_size_label, false); + + } else { + + swingReflectionInvoke("setEnabled", dance_button, true); + swingReflectionInvoke("setEnabled", warning_label, true); + swingReflectionInvoke("setEnabled", skip_button, true); + swingReflectionInvoke("setEnabled", skip_rest_button, true); + swingReflectionInvoke("setEnabled", total_file_size_label, true); + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox account_combobox; + private javax.swing.JLabel account_label; + private javax.swing.JButton add_files_button; + private javax.swing.JButton add_folder_button; + private javax.swing.JButton dance_button; + private javax.swing.JTextField dir_name_textfield; + private javax.swing.JTree file_tree; + private javax.swing.JScrollPane file_tree_scrollpane; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JButton skip_button; + private javax.swing.JButton skip_rest_button; + private javax.swing.JLabel total_file_size_label; + private javax.swing.JLabel upload_name_label; + private javax.swing.JLabel used_space_label; + private javax.swing.JLabel warning_label; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/FolderLinkDialog.form b/src/megabasterd/FolderLinkDialog.form new file mode 100644 index 000000000..63786f002 --- /dev/null +++ b/src/megabasterd/FolderLinkDialog.form @@ -0,0 +1,202 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/FolderLinkDialog.java b/src/megabasterd/FolderLinkDialog.java new file mode 100644 index 000000000..d04861402 --- /dev/null +++ b/src/megabasterd/FolderLinkDialog.java @@ -0,0 +1,452 @@ +package megabasterd; + +import static java.awt.Font.BOLD; +import static java.awt.Font.PLAIN; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import javax.swing.tree.DefaultTreeModel; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.deleteAllExceptSelectedTreeItems; +import static megabasterd.MiscTools.deleteSelectedTreeItems; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.formatBytes; +import static megabasterd.MiscTools.sortTree; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.updateFont; + +/** + * + * @author tonikelope + */ +public final class FolderLinkDialog extends javax.swing.JDialog { + + private String _link; + + private boolean _download; + + private final List _download_links; + + private long _total_space; + + private boolean _mega_error; + + public List getDownload_links() { + return _download_links; + } + + public boolean isDownload() { + return _download; + } + + public boolean isMega_error() { + return _mega_error; + } + + + /** + * Creates new form FolderLink + */ + public FolderLinkDialog(java.awt.Frame parent, boolean modal, String link) { + + super(parent, modal); + + initComponents(); + + _mega_error = false; + _total_space = 0L; + _download = false; + _link = null; + + updateFont(file_tree, FONT_DEFAULT, PLAIN); + updateFont(link_detected_label, FONT_DEFAULT, PLAIN); + updateFont(warning_label, FONT_DEFAULT, PLAIN); + updateFont(skip_button, FONT_DEFAULT, PLAIN); + updateFont(dance_button, FONT_DEFAULT, PLAIN); + updateFont(restore_button, FONT_DEFAULT, PLAIN); + updateFont(skip_rest_button, FONT_DEFAULT, PLAIN); + updateFont(total_space_label, FONT_DEFAULT, BOLD); + updateFont(folder_link_label, FONT_DEFAULT, PLAIN); + + swingReflectionInvoke("setVisible", restore_button, false); + + swingReflectionInvoke("setEnabled", total_space_label, false); + + _link = link; + + swingReflectionInvoke("setText", folder_link_label, link); + + _download_links = new ArrayList<>(); + + _loadMegaDirTree(); + + if(!_mega_error) { + + _genDownloadLiks(); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + file_tree_scrollpane = new javax.swing.JScrollPane(); + file_tree = new javax.swing.JTree(); + skip_button = new javax.swing.JButton(); + link_detected_label = new javax.swing.JLabel(); + dance_button = new javax.swing.JButton(); + folder_link_label = new javax.swing.JLabel(); + warning_label = new javax.swing.JLabel(); + skip_rest_button = new javax.swing.JButton(); + restore_button = new javax.swing.JButton(); + total_space_label = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("FolderLink"); + + file_tree.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root"); + file_tree.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1)); + file_tree.setDoubleBuffered(true); + file_tree_scrollpane.setViewportView(file_tree); + + skip_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + skip_button.setText("REMOVE THIS"); + skip_button.setDoubleBuffered(true); + skip_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + skip_buttonActionPerformed(evt); + } + }); + + link_detected_label.setFont(new java.awt.Font("Dialog", 1, 30)); // NOI18N + link_detected_label.setText("Folder link detected!"); + link_detected_label.setDoubleBuffered(true); + + dance_button.setBackground(new java.awt.Color(102, 204, 255)); + dance_button.setFont(new java.awt.Font("Dialog", 1, 24)); // NOI18N + dance_button.setForeground(new java.awt.Color(255, 255, 255)); + dance_button.setText("Let's dance, baby"); + dance_button.setDoubleBuffered(true); + dance_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dance_buttonActionPerformed(evt); + } + }); + + folder_link_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + folder_link_label.setText("jLabel2"); + folder_link_label.setDoubleBuffered(true); + + warning_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + warning_label.setText("If you DO NOT want to download some folder or file you can REMOVE it."); + warning_label.setDoubleBuffered(true); + + skip_rest_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + skip_rest_button.setText("REMOVE ALL EXCEPT THIS"); + skip_rest_button.setDoubleBuffered(true); + skip_rest_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + skip_rest_buttonActionPerformed(evt); + } + }); + + restore_button.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + restore_button.setText("Restore folder data"); + restore_button.setDoubleBuffered(true); + restore_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + restore_buttonActionPerformed(evt); + } + }); + + total_space_label.setFont(new java.awt.Font("Dialog", 1, 28)); // NOI18N + total_space_label.setText("[0 B]"); + total_space_label.setDoubleBuffered(true); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(file_tree_scrollpane) + .addGroup(layout.createSequentialGroup() + .addComponent(skip_rest_button) + .addGap(18, 18, 18) + .addComponent(skip_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 300, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(folder_link_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(restore_button)) + .addComponent(total_space_label, javax.swing.GroupLayout.DEFAULT_SIZE, 876, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(link_detected_label) + .addComponent(warning_label)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(link_detected_label) + .addGap(8, 8, 8) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(folder_link_label) + .addComponent(restore_button)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(file_tree_scrollpane, javax.swing.GroupLayout.DEFAULT_SIZE, 368, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(total_space_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(warning_label) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(22, 22, 22) + .addComponent(dance_button)) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(skip_rest_button) + .addComponent(skip_button)))) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void skip_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skip_buttonActionPerformed + + + if(deleteSelectedTreeItems(file_tree)) { + + _genDownloadLiks(); + swingReflectionInvoke("setVisible", restore_button, true); + } + + }//GEN-LAST:event_skip_buttonActionPerformed + + private void dance_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dance_buttonActionPerformed + + + + _download = true; + swingReflectionInvoke("setVisible", this, false); + }//GEN-LAST:event_dance_buttonActionPerformed + + private void skip_rest_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skip_rest_buttonActionPerformed + + + if(deleteAllExceptSelectedTreeItems(file_tree)) { + + _genDownloadLiks(); + swingReflectionInvoke("setVisible", restore_button, true); + } + + }//GEN-LAST:event_skip_rest_buttonActionPerformed + + private void restore_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restore_buttonActionPerformed + + + swingReflectionInvoke("setEnabled", restore_button, false); + + swingReflectionInvoke("setEnabled", dance_button, false); + + swingReflectionInvoke("setEnabled", file_tree, false); + + final FolderLinkDialog main = this; + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + main._loadMegaDirTree(); + + main._genDownloadLiks(); + + swingReflectionInvoke("setEnabled", main.restore_button, true); + + swingReflectionInvoke("setVisible", main.restore_button, false); + + swingReflectionInvoke("setEnabled", main.dance_button, true); + + swingReflectionInvoke("setEnabled", main.file_tree, true); + + }}); + }//GEN-LAST:event_restore_buttonActionPerformed + + private void _loadMegaDirTree() { + + try { + HashMap folder_nodes; + + MegaAPI ma = new MegaAPI(); + + String folder_id = findFirstRegex("#F!([^!]+)", _link, 1); + + String folder_key = findFirstRegex("#F![^!]+!(.+)", _link, 1); + + folder_nodes = ma.getFolderNodes(folder_id, folder_key); + + MegaMutableTreeNode root=null; + + for(Object o:folder_nodes.values()) { + + HashMap current_hashmap_node = (HashMap)o; + + MegaMutableTreeNode current_node; + + if(current_hashmap_node.get("jtree_node") == null) { + + current_node = new MegaMutableTreeNode(current_hashmap_node); + + current_hashmap_node.put("jtree_node", current_node); + + } else { + + current_node = (MegaMutableTreeNode)current_hashmap_node.get("jtree_node"); + } + + String parent_id=(String)current_hashmap_node.get("parent"); + + root=null; + + do{ + + if(folder_nodes.get(parent_id) != null) { + + HashMap parent_hashmap_node = (HashMap)folder_nodes.get(parent_id); + + MegaMutableTreeNode parent_node; + + if(parent_hashmap_node.get("jtree_node") == null) { + + parent_node = new MegaMutableTreeNode(parent_hashmap_node); + + parent_hashmap_node.put("jtree_node", parent_node); + + } else { + + parent_node = (MegaMutableTreeNode)parent_hashmap_node.get("jtree_node"); + } + + parent_node.add(current_node); + + parent_id = (String)parent_hashmap_node.get("parent"); + + current_node = parent_node; + + } else { + + root = current_node; + } + + }while(current_node != root); + } + + file_tree.setModel(new DefaultTreeModel(sortTree(root))); + + } catch (Exception ex) { + + getLogger(FolderLinkDialog.class.getName()).log(SEVERE, null, ex); + + _mega_error = true; + } + + } + + private void _genDownloadLiks(){ + + String folder_id = findFirstRegex("#F!([^!]+)", _link, 1); + + _download_links.clear(); + + MegaMutableTreeNode root = (MegaMutableTreeNode)file_tree.getModel().getRoot(); + + Enumeration files_tree = root.depthFirstEnumeration(); + + _total_space = 0L; + + while(files_tree.hasMoreElements()) { + + MegaMutableTreeNode node = (MegaMutableTreeNode)files_tree.nextElement(); + + if(node.isLeaf() && node!=root && ((HashMap)node.getUserObject()).get("size") != null) { + + String path = ""; + + Object[] object_path = node.getUserObjectPath(); + + for(Object p:object_path) { + + path+="/"+((Map)p).get("name"); + } + + path = path.replaceAll("^/+", "").trim(); + + String url = "https://mega.nz/#N!" + ((Map) node.getUserObject()).get("h") + "!" + ((Map) node.getUserObject()).get("key") + "###n=" + folder_id ; + + HashMap download_link = new HashMap<>(); + + download_link.put("url", url); + + download_link.put("filename", path); + + download_link.put("filekey", ((Map) node.getUserObject()).get("key")); + + download_link.put("filesize", ((HashMap)node.getUserObject()).get("size")); + + _total_space+=(long)download_link.get("filesize"); + + _download_links.add(download_link); + } + } + + swingReflectionInvoke("setText", total_space_label, "["+formatBytes(_total_space)+"]"); + + if(_total_space == 0) { + + swingReflectionInvoke("setEnabled", dance_button, false); + swingReflectionInvoke("setEnabled", warning_label, false); + swingReflectionInvoke("setEnabled", skip_button, false); + swingReflectionInvoke("setEnabled", skip_rest_button, false); + swingReflectionInvoke("setEnabled", total_space_label, false); + + } else { + + swingReflectionInvoke("setEnabled", dance_button, true); + swingReflectionInvoke("setEnabled", warning_label, true); + swingReflectionInvoke("setEnabled", skip_button, true); + swingReflectionInvoke("setEnabled", skip_rest_button, true); + swingReflectionInvoke("setEnabled", total_space_label, true); + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton dance_button; + private javax.swing.JTree file_tree; + private javax.swing.JScrollPane file_tree_scrollpane; + private javax.swing.JLabel folder_link_label; + private javax.swing.JLabel link_detected_label; + private javax.swing.JButton restore_button; + private javax.swing.JButton skip_button; + private javax.swing.JButton skip_rest_button; + private javax.swing.JLabel total_space_label; + private javax.swing.JLabel warning_label; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/GlobalSpeedMeter.java b/src/megabasterd/GlobalSpeedMeter.java new file mode 100644 index 000000000..f621a6ef6 --- /dev/null +++ b/src/megabasterd/GlobalSpeedMeter.java @@ -0,0 +1,114 @@ +package megabasterd; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JLabel; +import static megabasterd.MiscTools.formatBytes; +import static megabasterd.MiscTools.swingReflectionInvoke; + +public final class GlobalSpeedMeter implements Runnable, SecureNotifiable +{ + private final JLabel _speed_label; + private final ConcurrentLinkedQueue _speedmeters; + private final Object _secure_notify_lock; + private boolean _notified; + + + GlobalSpeedMeter(JLabel sp_label) + { + _notified = false; + _secure_notify_lock = new Object(); + _speed_label = sp_label; + _speedmeters = new ConcurrentLinkedQueue<>(); + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(GlobalSpeedMeter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + public void attachSpeedMeter(SpeedMeter speed) { + _speedmeters.add(speed); + } + + public void detachSpeedMeter(SpeedMeter speed) { + _speedmeters.remove(speed); + } + + private long calcSpeed() { + + long sp = 0; + + for(SpeedMeter speed:_speedmeters) + { + sp+=speed.getLastSpeed(); + } + + return sp; + } + + + @Override + public void run() + { + long sp; + + swingReflectionInvoke("setText", _speed_label, "------"); + swingReflectionInvoke("setVisible", _speed_label, true); + + while(true) + { + secureWait(); + + sp = calcSpeed(); + + if(sp > 0) { + + swingReflectionInvoke("setText", _speed_label, formatBytes(sp)+"/s"); + + } + else + { + swingReflectionInvoke("setText", _speed_label, "------"); + + } + } + + } +} diff --git a/src/megabasterd/Gochi.ttf b/src/megabasterd/Gochi.ttf new file mode 100644 index 000000000..790ef006d Binary files /dev/null and b/src/megabasterd/Gochi.ttf differ diff --git a/src/megabasterd/KissVideoStreamServer.java b/src/megabasterd/KissVideoStreamServer.java new file mode 100644 index 000000000..78c947e39 --- /dev/null +++ b/src/megabasterd/KissVideoStreamServer.java @@ -0,0 +1,333 @@ +package megabasterd; + +import com.sun.net.httpserver.HttpServer; +import java.awt.Color; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; +import static megabasterd.MiscTools.swingReflectionInvoke; + + +public final class KissVideoStreamServer { + + public static final int TIMEOUT=30000; + public static final int DEFAULT_PORT=1337; + public static final int EXP_BACKOFF_BASE=2; + public static final int EXP_BACKOFF_SECS_RETRY=1; + public static final int EXP_BACKOFF_MAX_WAIT_TIME=128; + private HttpServer _httpserver; + private final MainPanelView _main_panel; + private final ConcurrentHashMap _link_cache; + private final ConcurrentHashMap _working; + private final ContentType _ctype; + private KissVideoStreamServerHandler _http_handler; + public KissVideoStreamServer(MainPanelView panel) { + _main_panel = panel; + _link_cache = new ConcurrentHashMap(); + _working = new ConcurrentHashMap(); + _ctype = new ContentType(); + } + + public KissVideoStreamServerHandler getHandler() + { + return _http_handler; + } + + public MainPanelView getPanel() + { + return _main_panel; + } + + public ContentType getCtype() + { + return _ctype; + } + + public ConcurrentHashMap getStreaming() + { + return _working; + } + + public boolean isWorking() + { + return !_working.isEmpty(); + } + + + public void start(int port, String context) throws IOException + { + _httpserver = HttpServer.create(new InetSocketAddress(port), 0); + printStatusOK("Kissvideostreamer on localhost:"+DEFAULT_PORT+" (Waiting for request...)"); + _httpserver.createContext(context, (_http_handler = new KissVideoStreamServerHandler(this, _main_panel))); + _httpserver.setExecutor(Executors.newCachedThreadPool()); + _httpserver.start(); + } + + public void stop() + { + _httpserver.stop(0); + } + + public void printStatusError(String message) + { + swingReflectionInvoke("setForeground", _main_panel.getKiss_server_status(), Color.red); + swingReflectionInvoke("setText", _main_panel.getKiss_server_status(), message); + } + + public void printStatusOK(String message) + { + swingReflectionInvoke("setForeground", _main_panel.getKiss_server_status(), new Color(0,128,0)); + swingReflectionInvoke("setText", _main_panel.getKiss_server_status(), message); + } + + public String[] getFromLinkCache(String link) + { + return _link_cache.containsKey(link)?_link_cache.get(link):null; + } + + public void updateLinkCache(String link, String[] info) { + + _link_cache.put(link, info); + } + + public void removeFromLinkCache(String link) { + _link_cache.remove(link); + } + + public String[] getMegaFileMetadata(String link, MainPanelView panel) throws IOException, InterruptedException + { + + + String[] file_info=null; + int retry=0, error_code=0; + boolean error; + + do + { + error=false; + + try + { + + + if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) + { + MegaAPI ma = new MegaAPI(); + + file_info = ma.getMegaFileMetadata(link); + } + else + { + file_info = MegaCrypterAPI.getMegaFileMetadata(link, panel); + } + + + } + catch(MegaAPIException | MegaCrypterAPIException e) + { + error=true; + + error_code = Integer.parseInt(e.getMessage()); + + switch(error_code) + { + case -2: + throw new IOException("Mega link is not valid!"); + + case -14: + throw new IOException("Mega link is not valid!"); + + case 22: + throw new IOException("MegaCrypter link is not valid!"); + + case 23: + throw new IOException("MegaCrypter link is blocked!"); + + case 24: + throw new IOException("MegaCrypter link has expired!"); + + default: + + for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) + { + if(error_code == -18) + { + printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); + } + else + { + printStatusError("Mega/MC APIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) {} + } + } + } catch(Exception ex) { + + } + + + }while(error); + + return file_info; + } + + public String getMegaFileDownloadUrl(String link, String pass_hash, String noexpire_token) throws IOException, InterruptedException + { + + + String dl_url=null; + int retry=0, error_code; + boolean error; + + do + { + error=false; + + try + { + + + + if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) + { + MegaAPI ma = new MegaAPI(); + + dl_url = ma.getMegaFileDownloadUrl(link); + } + else + { + dl_url = MegaCrypterAPI.getMegaFileDownloadUrl(link,pass_hash,noexpire_token); + } + + + } + catch(MegaAPIException e) + { + error=true; + + error_code = Integer.parseInt(e.getMessage()); + + for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) + { + if(error_code == -18) + { + printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); + } + else + { + printStatusError("MegaAPIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) {} + } + } + catch(MegaCrypterAPIException e) + { + error=true; + + error_code = Integer.parseInt(e.getMessage()); + + switch(error_code) + { + case 22: + throw new IOException("MegaCrypter link is not valid!"); + + case 23: + throw new IOException("MegaCrypter link is blocked!"); + + case 24: + throw new IOException("MegaCrypter link has expired!"); + + default: + for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) + { + if(error_code == -18) + { + printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); + } + else + { + printStatusError("MegaCrypterAPIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) {} + } + } + } + + }while(error); + + return dl_url; + } + + public boolean checkDownloadUrl(String string_url) + { + try { + URL url = new URL(string_url+"/0-0"); + URLConnection connection = url.openConnection(); + connection.setConnectTimeout(TIMEOUT); + connection.setRequestProperty("User-Agent", MainPanel.USER_AGENT); + connection.setRequestProperty("Connection", "close"); + try (InputStream is = connection.getInputStream()) { + while(is.read()!=-1); + } + + return true; + + }catch (Exception ex) { + + return false; + } + } + + public long[] parseRangeHeader(String header) + { + Pattern pattern = Pattern.compile("bytes\\=([0-9]+)\\-([0-9]+)?"); + + Matcher matcher = pattern.matcher(header); + + long[] ranges=new long[2]; + + if(matcher.find()) + { + ranges[0] = Long.valueOf(matcher.group(1)); + + if(matcher.group(2)!=null) { + ranges[1] = Long.valueOf(matcher.group(2)); + } else + { + ranges[1]=-1; + } + } + + return ranges; + } + + public String cookRangeUrl(String url, long[] ranges, int sync_bytes) + { + return url+"/"+String.valueOf(ranges[0]-sync_bytes)+(ranges[1]>=0?"-"+String.valueOf(ranges[1]):""); + } + + public void restoreMainWindow() { + + _main_panel.setExtendedState(javax.swing.JFrame.NORMAL); + swingReflectionInvoke("setVisible", _main_panel, true); + } + +} + diff --git a/src/megabasterd/KissVideoStreamServerHandler.java b/src/megabasterd/KissVideoStreamServerHandler.java new file mode 100644 index 000000000..5362f2000 --- /dev/null +++ b/src/megabasterd/KissVideoStreamServerHandler.java @@ -0,0 +1,301 @@ +package megabasterd; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.crypto.CipherInputStream; +import javax.crypto.NoSuchPaddingException; +import static megabasterd.KissVideoStreamServer.DEFAULT_PORT; +import static megabasterd.MiscTools.findFirstRegex; + + +public final class KissVideoStreamServerHandler implements HttpHandler { + + private final KissVideoStreamServer _httpserver; + + private final MainPanelView _view; + + private String _file_name; + + private long _file_size; + + private String _file_key; + + private String _pass_hash; + + private String _noexpire_token; + + public KissVideoStreamServerHandler(KissVideoStreamServer server, MainPanelView view) { + + _httpserver = server; + _view = view; + } + + @Override + public void handle(HttpExchange xchg) throws IOException { + + _httpserver.getStreaming().put(Thread.currentThread(), true); + + long clength; + + OutputStream os; + + CipherInputStream cis = null; + + String httpmethod = xchg.getRequestMethod(); + + _httpserver.printStatusOK("Kissvideostreamer (Request received! Dispatching it...)"); + + Headers reqheaders=xchg.getRequestHeaders(); + + Headers resheaders = xchg.getResponseHeaders(); + + String url_path = xchg.getRequestURI().getPath(); + + String link = url_path.substring(url_path.indexOf("/video/")+7); + + if(link.indexOf("mega/") == 0) + { + link = link.replaceAll("mega/", "https://mega.co.nz/#"); + } + else + { + String mc_host = findFirstRegex("^[^/]+/", link, 0); + + link = "http://" + mc_host + link; + } + + _httpserver.printStatusOK("Kissvideostreamer (Retrieving file metadata...)"); + + String[] cache_info, file_info=null; + + cache_info = _httpserver.getFromLinkCache(link); + + if(cache_info!=null) { + + file_info = new String[6]; + + System.arraycopy( cache_info, 0, file_info, 0, cache_info.length ); + + } else { + + try { + + file_info = _httpserver.getMegaFileMetadata(link, _view); + + } catch (InterruptedException ex) { + Logger.getLogger(KissVideoStreamServerHandler.class.getName()).log(Level.SEVERE, null, ex); + } + + cache_info = new String[6]; + + System.arraycopy( file_info, 0, cache_info, 0, file_info.length ); + + cache_info[5]=null; + } + + _file_name = file_info[0]; + + _file_size = Long.parseLong(file_info[1]); + + _file_key = file_info[2]; + + if(file_info.length >= 5) + { + _pass_hash = file_info[3]; + + _noexpire_token = file_info[4]; + + } else { + _pass_hash = null; + + _noexpire_token = null; + } + + String file_ext = _file_name.substring(_file_name.lastIndexOf('.')+1).toLowerCase(); + + URLConnection urlConn; + + try{ + + if(httpmethod.equals("HEAD")) { + + resheaders.add("Accept-Ranges", "bytes"); + + resheaders.add("transferMode.dlna.org", "Streaming"); + + resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); + + resheaders.add("Content-Type", _httpserver.getCtype().getMIME(file_ext)); + + resheaders.add("Content-Length", String.valueOf(_file_size)); + + resheaders.add("Connection", "close"); + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); + + } else if(httpmethod.equals("GET")) { + + resheaders.add("Accept-Ranges", "bytes"); + + resheaders.add("transferMode.dlna.org", "Streaming"); + + resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); + + resheaders.add("Content-Type", _httpserver.getCtype().getMIME(file_ext)); + + resheaders.add("Connection", "close"); + + byte[] buffer = new byte[16*1024]; + + int reads; + + _httpserver.printStatusOK("Kissvideostreamer (Retrieving mega temp url...)"); + + String temp_url; + + if(cache_info[5]!=null) { + + temp_url = cache_info[5]; + + if(!_httpserver.checkDownloadUrl(temp_url)) { + + temp_url = _httpserver.getMegaFileDownloadUrl(link,_pass_hash,_noexpire_token); + + cache_info[5] = temp_url; + + _httpserver.updateLinkCache(link, cache_info); + } + + } else { + temp_url = _httpserver.getMegaFileDownloadUrl(link,_pass_hash,_noexpire_token); + + cache_info[5] = temp_url; + + _httpserver.updateLinkCache(link, cache_info); + } + + _httpserver.printStatusOK("Kissvideostreamer (Connecting...)"); + + long[] ranges=new long[2]; + + int sync_bytes=0; + + String header_range=null; + + InputStream is; + + URL url; + + if(reqheaders.containsKey("Range")) + { + header_range = "Range"; + + } else if(reqheaders.containsKey("range")) { + + header_range = "range"; + } + + if(header_range != null) + { + List ranges_raw = reqheaders.get(header_range); + + String range_header=ranges_raw.get(0); + + ranges = _httpserver.parseRangeHeader(range_header); + + sync_bytes = (int)ranges[0] % 16; + + if(ranges[1]>=0 && ranges[1]>=ranges[0]) { + + clength = ranges[1]-ranges[0]+1; + + } else { + + clength = _file_size - ranges[0]; + } + + resheaders.add("Content-Range", "bytes "+ranges[0]+"-"+(ranges[1]>=0?ranges[1]:(_file_size-1))+"/"+_file_size); + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_PARTIAL, clength); + + url = new URL(_httpserver.cookRangeUrl(temp_url, ranges, sync_bytes)); + + } else { + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, _file_size); + + url = new URL(temp_url); + } + + urlConn = url.openConnection(); + urlConn.setConnectTimeout(KissVideoStreamServer.TIMEOUT); + urlConn.setRequestProperty("User-Agent", MainPanel.USER_AGENT); + urlConn.setRequestProperty("Connection", "close"); + is = urlConn.getInputStream(); + + byte[] iv = CryptTools.initMEGALinkKeyIV(_file_key); + + try { + + cis = new CipherInputStream(is, CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", CryptTools.initMEGALinkKey(_file_key), (header_range!=null && (ranges[0]-sync_bytes)>0)?CryptTools.forwardMEGALinkKeyIV(iv, ranges[0]-sync_bytes):iv)); + + } catch ( NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException ex) { + Logger.getLogger(KissVideoStreamServer.class.getName()).log(Level.SEVERE, null, ex); + } + + os = xchg.getResponseBody(); + + _httpserver.printStatusOK("Kissvideostreamer (Streaming file "+_file_name+" ...)"); + + //Skip sync bytes + cis.skip(sync_bytes); + + while((reads=cis.read(buffer))!=-1) { + + os.write(buffer, 0, reads); + } + + } + } + catch(Exception ex) + { + + } + finally + { + if(cis!=null) { + cis.close(); + } + + xchg.close(); + + _httpserver.printStatusOK("Kissvideostreamer on localhost:"+DEFAULT_PORT+" (Waiting for request...)"); + + _httpserver.getStreaming().remove(Thread.currentThread()); + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Logger.getLogger(KissVideoStreamServerHandler.class.getName()).log(Level.SEVERE, null, ex); + } + + if(!_httpserver.isWorking()) { + + _httpserver.restoreMainWindow(); + } + } + } +} diff --git a/src/megabasterd/LinkGrabberDialog.form b/src/megabasterd/LinkGrabberDialog.form new file mode 100644 index 000000000..35e2fdc47 --- /dev/null +++ b/src/megabasterd/LinkGrabberDialog.form @@ -0,0 +1,154 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/LinkGrabberDialog.java b/src/megabasterd/LinkGrabberDialog.java new file mode 100644 index 000000000..3edfe5306 --- /dev/null +++ b/src/megabasterd/LinkGrabberDialog.java @@ -0,0 +1,202 @@ +package megabasterd; + +import java.awt.Font; +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.JTextArea; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MiscTools.extractMegaLinksFromString; +import static megabasterd.MiscTools.extractStringFromClipboardContents; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.truncateText; +import static megabasterd.MiscTools.updateFont; + + +public final class LinkGrabberDialog extends javax.swing.JDialog implements ClipboardChangeObserver { + + private boolean _download; + private String _download_path; + private final ClipboardSpy _clipboardpy; + + public boolean isDownload() { + return _download; + } + + public String getDownload_path() { + return _download_path; + } + + public JTextArea getLinks_textarea() { + return links_textarea; + } + + + + public LinkGrabberDialog(java.awt.Frame parent, boolean modal, String download_path, ClipboardSpy clipboardspy) { + + super(parent, modal); + _download = false; + + initComponents(); + + updateFont(links_label, FONT_DEFAULT, Font.PLAIN); + updateFont(dance_button, FONT_DEFAULT, Font.PLAIN); + updateFont(down_dir_to_label, FONT_DEFAULT, Font.PLAIN); + updateFont(change_dir_button, FONT_DEFAULT, Font.PLAIN); + updateFont(download_dir_label, FONT_DEFAULT, Font.PLAIN); + + _download_path = download_path; + + _clipboardpy = clipboardspy; + + swingReflectionInvoke("setText", download_dir_label, truncateText(download_path,80)); + + swingReflectionInvoke("setText", links_textarea,extractMegaLinksFromString(extractStringFromClipboardContents(_clipboardpy.getContents()))); + } + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + links_scrollpane = new javax.swing.JScrollPane(); + links_textarea = new javax.swing.JTextArea(); + dance_button = new javax.swing.JButton(); + links_label = new javax.swing.JLabel(); + change_dir_button = new javax.swing.JButton(); + down_dir_to_label = new javax.swing.JLabel(); + download_dir_label = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("LinkGrabber"); + setModal(true); + + links_textarea.setColumns(20); + links_textarea.setFont(new java.awt.Font("Dialog", 0, 14)); // NOI18N + links_textarea.setRows(5); + links_textarea.setDoubleBuffered(true); + links_scrollpane.setViewportView(links_textarea); + links_textarea.addMouseListener(new ContextMenuMouseListener()); + + dance_button.setBackground(new java.awt.Color(102, 204, 255)); + dance_button.setFont(new java.awt.Font("Dialog", 1, 24)); // NOI18N + dance_button.setForeground(new java.awt.Color(255, 255, 255)); + dance_button.setText("Let's dance, baby"); + dance_button.setDoubleBuffered(true); + dance_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dance_buttonActionPerformed(evt); + } + }); + + links_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + links_label.setText("Put your MEGA/MegaCrypter link/s here (one per line):"); + links_label.setDoubleBuffered(true); + + change_dir_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + change_dir_button.setText("Change it"); + change_dir_button.setDoubleBuffered(true); + change_dir_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + change_dir_buttonActionPerformed(evt); + } + }); + + down_dir_to_label.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + down_dir_to_label.setText("Download to: "); + down_dir_to_label.setDoubleBuffered(true); + + download_dir_label.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + download_dir_label.setText("jLabel3"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(links_scrollpane, javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addComponent(links_label) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(change_dir_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(down_dir_to_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(download_dir_label, javax.swing.GroupLayout.PREFERRED_SIZE, 404, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 300, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(links_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(links_scrollpane, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(change_dir_button) + .addComponent(down_dir_to_label) + .addComponent(download_dir_label)) + .addGap(5, 5, 5))) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void dance_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dance_buttonActionPerformed + + _download=true; + dispose(); + }//GEN-LAST:event_dance_buttonActionPerformed + + private void change_dir_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_change_dir_buttonActionPerformed + + + javax.swing.JFileChooser filechooser = new javax.swing.JFileChooser(); + + filechooser.setCurrentDirectory(new java.io.File(_download_path)); + filechooser.setDialogTitle("Download directory"); + filechooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); + filechooser.setAcceptAllFileFilterUsed(false); + + if( filechooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION ) { + + File file = filechooser.getSelectedFile(); + + _download_path = file.getAbsolutePath(); + + swingReflectionInvoke("setText", download_dir_label, truncateText(_download_path,80)); + } + }//GEN-LAST:event_change_dir_buttonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton change_dir_button; + private javax.swing.JButton dance_button; + private javax.swing.JLabel down_dir_to_label; + private javax.swing.JLabel download_dir_label; + private javax.swing.JLabel links_label; + private javax.swing.JScrollPane links_scrollpane; + private javax.swing.JTextArea links_textarea; + // End of variables declaration//GEN-END:variables + + @Override + public void notifyClipboardChange() { + + swingReflectionInvoke("setText", links_textarea,extractMegaLinksFromString(extractStringFromClipboardContents(_clipboardpy.getContents()))); + } +} diff --git a/src/megabasterd/MainPanel.java b/src/megabasterd/MainPanel.java new file mode 100644 index 000000000..c172d0163 --- /dev/null +++ b/src/megabasterd/MainPanel.java @@ -0,0 +1,629 @@ +package megabasterd; + +import java.awt.AWTException; +import static java.awt.EventQueue.invokeLater; +import java.awt.Font; +import static java.awt.Font.BOLD; +import static java.awt.Frame.NORMAL; +import java.awt.Image; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import static java.awt.SystemTray.getSystemTray; +import static java.awt.SystemTray.isSupported; +import java.awt.Toolkit; +import static java.awt.Toolkit.getDefaultToolkit; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import static java.awt.event.WindowEvent.WINDOW_CLOSING; +import java.io.File; +import java.io.IOException; +import static java.lang.Integer.parseInt; +import static java.lang.System.exit; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.showOptionDialog; +import static megabasterd.DBTools.deleteUpload; +import static megabasterd.DBTools.selectDownloads; +import static megabasterd.DBTools.selectMegaAccounts; +import static megabasterd.DBTools.selectSettingValueFromDB; +import static megabasterd.DBTools.selectUploads; +import static megabasterd.DBTools.setupSqliteTables; +import static megabasterd.KissVideoStreamServer.DEFAULT_PORT; +import static megabasterd.MiscTools.BASE642Bin; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.createAndRegisterFont; +import static megabasterd.MiscTools.setNimbusLookAndFeel; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.Transference.LIMIT_TRANSFERENCE_SPEED_DEFAULT; +import static megabasterd.Transference.MAX_SIM_TRANSFERENCES; +import static megabasterd.Transference.MAX_TRANSFERENCE_SPEED_DEFAULT; + + + /** + * + * @author tonikelope + */ +public final class MainPanel { + + public static final String VERSION="1.0"; + public static final String LOCK_FILE="megabasterd.lock"; + public static final String USER_AGENT="Mozilla/5.0 (X11; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0"; + public static final int CONNECTION_TIMEOUT = 30000; + public static final int THROTTLE_SLICE_SIZE=16*1024; + public static final ExecutorService THREAD_POOL = newCachedThreadPool(); + public static final Font FONT_DEFAULT = createAndRegisterFont("Gochi.ttf"); + public static void main(String args[]) { + + setNimbusLookAndFeel(); + + final MainPanel main_panel = new MainPanel(); + + invokeLater(new Runnable() { + @Override + public void run() { + + main_panel.getView().setVisible(true); + } + }); + } + + private MainPanelView _view; + private final GlobalSpeedMeter _global_dl_speed, _global_up_speed; + private final DownloadManager _download_manager; + private final UploadManager _upload_manager; + private final StreamThrottlerSupervisor _stream_supervisor; + private int _max_dl, _max_ul, _default_slots_down, _default_slots_up, _max_dl_speed, _max_up_speed; + private boolean _use_slots_down, _use_slots_up, _limit_download_speed, _limit_upload_speed; + private String _default_download_path; + private HashMap _mega_accounts; + private final HashMap _mega_active_accounts; + private TrayIcon _trayicon; + private final ClipboardSpy _clipboardspy; + private KissVideoStreamServer _streamserver; + public MainPanel() { + + checkAppIsRunning(); + + try { + + trayIcon(); + + } catch (AWTException ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + + try { + + setupSqliteTables(); + + } catch (SQLException ex) { + getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); + } + + _view = null; //Lazy init (getter!) + + loadUserSettings(); + + _mega_active_accounts = new HashMap<>(); + + THREAD_POOL.execute((_global_dl_speed = new GlobalSpeedMeter(getView().getGlobal_speed_down_label()))); + + THREAD_POOL.execute((_global_up_speed = new GlobalSpeedMeter(getView().getGlobal_speed_up_label()))); + + THREAD_POOL.execute((_download_manager = new DownloadManager(this))); + + THREAD_POOL.execute((_upload_manager = new UploadManager(this))); + + THREAD_POOL.execute((_stream_supervisor = new StreamThrottlerSupervisor(_limit_download_speed?_max_dl_speed*1024:0, _limit_upload_speed?_max_up_speed*1024:0, THROTTLE_SLICE_SIZE))); + + THREAD_POOL.execute((_clipboardspy = new ClipboardSpy())); + + resumeDownloads(); + + resumeUploads(); + + _streamserver = new KissVideoStreamServer(getView()); + + try { + _streamserver.start(DEFAULT_PORT, "/video"); + } catch (IOException ex) { + getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); + } + } + + public MainPanelView getView() { + return _view == null?(_view = new MainPanelView(this)):_view; + } + + public GlobalSpeedMeter getGlobal_dl_speed() { + return _global_dl_speed; + } + + public GlobalSpeedMeter getGlobal_up_speed() { + return _global_up_speed; + } + + public DownloadManager getDownload_manager() { + return _download_manager; + } + + public UploadManager getUpload_manager() { + return _upload_manager; + } + + public StreamThrottlerSupervisor getStream_supervisor() { + return _stream_supervisor; + } + + public int getMax_dl() { + return _max_dl; + } + + public int getMax_ul() { + return _max_ul; + } + + public int getDefault_slots_down() { + return _default_slots_down; + } + + public int getDefault_slots_up() { + return _default_slots_up; + } + + public boolean isUse_slots_down() { + return _use_slots_down; + } + + public String getDefault_download_path() { + return _default_download_path; + } + + public HashMap getMega_accounts() { + return _mega_accounts; + } + + public HashMap getMega_active_accounts() { + return _mega_active_accounts; + } + + public TrayIcon getIcon() { + return _trayicon; + } + + public ClipboardSpy getClipboardspy() { + return _clipboardspy; + } + + public KissVideoStreamServer getStreamserver() { + return _streamserver; + } + + public boolean isUse_slots_up() { + return _use_slots_up; + } + + public int getMax_dl_speed() { + return _max_dl_speed; + } + + public int getMax_up_speed() { + return _max_up_speed; + } + + public boolean isLimit_download_speed() { + return _limit_download_speed; + } + + public boolean isLimit_upload_speed() { + return _limit_upload_speed; + } + + + public void loadUserSettings() + { + String def_slots = selectSettingValueFromDB("default_slots_down"); + + if(def_slots != null) { + _default_slots_down = parseInt(def_slots); + } else { + _default_slots_down = Download.WORKERS_DEFAULT; + } + + def_slots = selectSettingValueFromDB("default_slots_up"); + + if(def_slots != null) { + _default_slots_up = parseInt(def_slots); + } else { + _default_slots_up = Upload.WORKERS_DEFAULT; + } + + String use_slots = selectSettingValueFromDB("use_slots_down"); + + if(use_slots != null) { + _use_slots_down = use_slots.equals("yes"); + } else { + _use_slots_down = Download.USE_SLOTS_DEFAULT; + } + + use_slots = selectSettingValueFromDB("use_slots_up"); + + if(use_slots != null) { + _use_slots_up = use_slots.equals("yes"); + } else { + _use_slots_up = Upload.USE_SLOTS_DEFAULT; + } + + String max_downloads = selectSettingValueFromDB("max_downloads"); + + if(max_downloads != null) { + _max_dl = parseInt(max_downloads); + } else { + _max_dl=MAX_SIM_TRANSFERENCES; + } + + String max_uploads = selectSettingValueFromDB("max_uploads"); + + if(max_uploads != null) { + _max_ul = parseInt(max_uploads); + } else { + _max_ul=MAX_SIM_TRANSFERENCES; + } + + _default_download_path = selectSettingValueFromDB("default_down_dir"); + + if(_default_download_path == null) { + _default_download_path = "."; + } + + String limit_dl_speed = selectSettingValueFromDB("limit_download_speed"); + + if(limit_dl_speed != null) { + + _limit_download_speed = limit_dl_speed.equals("yes"); + + } else { + + _limit_download_speed = LIMIT_TRANSFERENCE_SPEED_DEFAULT; + } + + String limit_ul_speed = selectSettingValueFromDB("limit_upload_speed"); + + if(limit_ul_speed != null) { + + _limit_upload_speed = limit_ul_speed.equals("yes"); + + } else { + + _limit_upload_speed = LIMIT_TRANSFERENCE_SPEED_DEFAULT; + } + + String max_download_speed = selectSettingValueFromDB("max_download_speed"); + + if(max_download_speed != null) { + _max_dl_speed = parseInt(max_download_speed); + } else { + _max_dl_speed=MAX_TRANSFERENCE_SPEED_DEFAULT; + } + + String max_upload_speed = selectSettingValueFromDB("max_upload_speed"); + + if(max_upload_speed != null) { + _max_up_speed = parseInt(max_upload_speed); + } else { + _max_up_speed=MAX_TRANSFERENCE_SPEED_DEFAULT; + } + + try { + _mega_accounts = selectMegaAccounts(); + } catch (SQLException ex) { + getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); + } + + } + + public void _byebye() { + + if(_streamserver.isWorking()) { + + Object[] options = {"No", + "Yes"}; + + int n = showOptionDialog(getView(), + "It seems MegaBasterd is streaming video. Do you want to exit?", + "Warning!", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, + options, + options[0]); + + if(n==1) { + + exit(0); + } + + } else if(!getDownload_manager().getTransference_provision_queue().isEmpty() || !getUpload_manager().getTransference_provision_queue().isEmpty()) { + + Object[] options = {"No", + "Yes"}; + + int n = showOptionDialog(getView(), + "It seems MegaBasterd is provisioning down/uploads.\nIf you exit now, unprovisioned down/uploads will be lost.\nDo you want to continue?", + "Warning!", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, + options, + options[0]); + + if(n==1) { + + exit(0); + } + + } else { + + + exit(0); + + } + } + + private void checkAppIsRunning() + { + File lock = new File(LOCK_FILE); + + if(lock.exists()) { + + Object[] options = {"Yes, load it anyway", + "No"}; + + int n = showOptionDialog(getView(), + "It seems MegaBasterd is already running. Do you want to continue?", + "Warning!", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, + options, + options[1]); + + if(n==1) { + exit(0); + } + } + + try { + + lock.createNewFile(); + + lock.deleteOnExit(); + + } catch (IOException ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + } + + private void resumeDownloads() { + + swingReflectionInvoke("setText", getView().getStatus_down_label(), "Resuming previous downloads, please wait..."); + + swingReflectionInvoke("setEnabled", getView().getNew_download_menu(), false); + + final MainPanel main =this; + + THREAD_POOL.execute(new Runnable(){ + + @Override + public void run() { + + int conta_downloads = 0; + + try { + + ArrayList> res = selectDownloads(); + + for(HashMap o:res) { + + Download download = new Download(main, (String)o.get("url"), (String)o.get("path"), (String)o.get("filename"), (String)o.get("filekey"), (Long)o.get("filesize"), (String)o.get("filepass"), (String)o.get("filenoexpire"), main._use_slots_down, main._default_slots_down, false); + + main.getDownload_manager().getTransference_provision_queue().add(download); + + conta_downloads++; + } + + } catch (SQLException ex) { + + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + + if(conta_downloads>0) { + + swingReflectionInvoke("setText", main.getView().getStatus_down_label(), "Starting downloads provisioning, please wait..."); + + main.getDownload_manager().secureNotify(); + + main.getView().getjTabbedPane1().setSelectedIndex(0); + + } else { + + swingReflectionInvoke("setEnabled", main.getView().getNew_download_menu(), true); + } + + swingReflectionInvoke("setText", main.getView().getStatus_down_label(), ""); + + + }}); + + } + + public boolean trayIcon() throws AWTException { + + if (!isSupported()) { + return false; + } + + SystemTray tray = getSystemTray(); + + Toolkit toolkit = getDefaultToolkit(); + + Image image = toolkit.getImage(getClass().getResource("pica_roja.png")); + + PopupMenu menu = new PopupMenu(); + + menu.setFont(FONT_DEFAULT.deriveFont(BOLD, 18)); + + final javax.swing.JFrame myframe = getView(); + + MenuItem messageItem = new MenuItem("Restore window"); + + messageItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + swingReflectionInvokeAndWait("setExtendedState", myframe, NORMAL); + + swingReflectionInvoke("setVisible", myframe, true); + + } + }); + + menu.add(messageItem); + + MenuItem closeItem = new MenuItem("EXIT"); + + final MainPanel main=this; + + closeItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + main._byebye(); + } + }); + + menu.add(closeItem); + + ActionListener actionListener = new ActionListener() { + @Override + public void actionPerformed( ActionEvent e ) { + //Double click code here + + if(!(boolean)swingReflectionInvokeAndWaitForReturn("isVisible", myframe)) + { + swingReflectionInvokeAndWait("setExtendedState", myframe, NORMAL); + + swingReflectionInvoke("setVisible", myframe, true); + } + else + { + swingReflectionInvoke("dispatchEvent", myframe, new WindowEvent(myframe, WINDOW_CLOSING)); + + } + + } + }; + + _trayicon = new TrayIcon(image, "MegaBasterd", menu); + + _trayicon.setToolTip("MegaBasterd " + VERSION); + + _trayicon.setImageAutoSize(true); + + _trayicon.addActionListener(actionListener); + + tray.add(_trayicon); + + return true; + } + + + private void resumeUploads() { + + swingReflectionInvoke("setText", getView().getStatus_up_label(), "Resuming previous uploads, please wait..."); + + swingReflectionInvoke("setEnabled", getView().getNew_upload_menu(), false); + + final MainPanel main =this; + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + try { + + int conta_uploads = 0; + + ArrayList> res = selectUploads(); + + + + for(HashMap o:res) { + + String email = (String)o.get("email"); + + MegaAPI ma; + + if(main._mega_accounts.get(email) != null) { + + final HashMap account_info = (HashMap)main._mega_accounts.get(email); + + ma = main._mega_active_accounts.get(email); + + if(ma == null) { + + try { + + ma = new MegaAPI(); + + ma.login(email, bin2i32a(BASE642Bin((String)account_info.get("password_aes"))), (String)account_info.get("user_hash")); + + main._mega_active_accounts.put(email, ma); + + } catch (Exception ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + } + + Upload upload = new Upload(main, ma, (String)o.get("filename"), (String)o.get("parent_node"), (String)o.get("ul_key")!=null?bin2i32a(BASE642Bin((String)o.get("ul_key"))):null, (String)o.get("url"), (String)o.get("root_node"), BASE642Bin((String)o.get("share_key")), (String)o.get("folder_link"), main._use_slots_up, main._default_slots_up, false); + + main.getUpload_manager().getTransference_provision_queue().add(upload); + + conta_uploads++; + + } else { + + deleteUpload((String)o.get("filename"), email); + } + } + + if(conta_uploads>0) { + + swingReflectionInvoke("setText", main.getView().getStatus_up_label(), "Starting uploads provisioning, please wait..."); + + main.getUpload_manager().secureNotify(); + + main.getView().getjTabbedPane1().setSelectedIndex(1); + + } else { + swingReflectionInvoke("setEnabled", main.getView().getNew_upload_menu(), true); + } + + swingReflectionInvoke("setText", main.getView().getStatus_up_label(), ""); + + } catch (Exception ex) { + getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); + } + + }}); + + } + + +} diff --git a/src/megabasterd/MainPanelView.form b/src/megabasterd/MainPanelView.form new file mode 100644 index 000000000..7cac23859 --- /dev/null +++ b/src/megabasterd/MainPanelView.form @@ -0,0 +1,460 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/MainPanelView.java b/src/megabasterd/MainPanelView.java new file mode 100644 index 000000000..1e59c60ea --- /dev/null +++ b/src/megabasterd/MainPanelView.java @@ -0,0 +1,915 @@ +package megabasterd; + + +import java.awt.Color; +import static java.awt.Font.BOLD; +import static java.awt.Font.PLAIN; +import java.awt.event.WindowEvent; +import static java.awt.event.WindowEvent.WINDOW_CLOSING; +import java.io.File; +import static java.lang.System.out; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.showOptionDialog; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import static megabasterd.CryptTools.decryptMegaDownloaderLink; +import static megabasterd.DBTools.deleteMegaAccount; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MainPanel.VERSION; +import static megabasterd.MiscTools.BASE642Bin; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.findAllRegex; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.genID; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.updateFont; + + +public final class MainPanelView extends javax.swing.JFrame { + + private final MainPanel _main_panel; + + public JLabel getKiss_server_status() { + return kiss_server_status; + } + + public JMenuItem getClean_all_down_menu() { + return clean_all_down_menu; + } + + public JMenuItem getClean_all_up_menu() { + return clean_all_up_menu; + } + + public JButton getClose_all_finished_down_button() { + return close_all_finished_down_button; + } + + public JButton getClose_all_finished_up() { + return close_all_finished_up_button; + } + + public JLabel getGlobal_speed_down_label() { + return global_speed_down_label; + } + + public JLabel getGlobal_speed_up_label() { + return global_speed_up_label; + } + + public JPanel getjPanel_scroll_down() { + return jPanel_scroll_down; + } + + public JPanel getjPanel_scroll_up() { + return jPanel_scroll_up; + } + + public JMenuItem getNew_download_menu() { + return new_download_menu; + } + + public JMenuItem getNew_upload_menu() { + return new_upload_menu; + } + + public JButton getPause_all_down_button() { + return pause_all_down_button; + } + + public JButton getPause_all_up() { + return pause_all_up_button; + } + + public JLabel getStatus_down_label() { + return status_down_label; + } + + public JLabel getStatus_up_label() { + return status_up_label; + } + + public MainPanel getMain_panel() { + return _main_panel; + } + + public JTabbedPane getjTabbedPane1() { + return jTabbedPane1; + } + + + + public MainPanelView(MainPanel main_panel) { + + initComponents(); + + _main_panel = main_panel; + + setTitle("MegaBasterd " + VERSION); + + setIconImage(new ImageIcon(getClass().getResource("pica_roja.png")).getImage()); + + updateFont(file_menu, FONT_DEFAULT, PLAIN); + updateFont(edit_menu, FONT_DEFAULT, PLAIN); + updateFont(help_menu, FONT_DEFAULT, PLAIN); + updateFont(new_download_menu, FONT_DEFAULT, PLAIN); + updateFont(exit_menu, FONT_DEFAULT, PLAIN); + updateFont(settings_menu, FONT_DEFAULT, PLAIN); + updateFont(hide_tray_menu, FONT_DEFAULT, PLAIN); + updateFont(about_menu, FONT_DEFAULT, PLAIN); + updateFont(new_stream_menu, FONT_DEFAULT, PLAIN); + updateFont(new_upload_menu, FONT_DEFAULT, PLAIN); + updateFont(clean_all_up_menu, FONT_DEFAULT, PLAIN); + updateFont(clean_all_down_menu, FONT_DEFAULT, PLAIN); + updateFont(global_speed_down_label, FONT_DEFAULT, BOLD); + updateFont(global_speed_up_label, FONT_DEFAULT, BOLD); + updateFont(kiss_server_status, FONT_DEFAULT, BOLD); + updateFont(status_down_label, FONT_DEFAULT, BOLD); + updateFont(status_up_label, FONT_DEFAULT, BOLD); + updateFont(close_all_finished_down_button, FONT_DEFAULT, BOLD); + updateFont(close_all_finished_up_button, FONT_DEFAULT, BOLD); + updateFont(pause_all_down_button, FONT_DEFAULT, BOLD); + updateFont(pause_all_up_button, FONT_DEFAULT, BOLD); + updateFont(jTabbedPane1, FONT_DEFAULT, PLAIN); + + swingReflectionInvoke("setVisible", global_speed_down_label, false); + swingReflectionInvoke("setVisible", global_speed_up_label, false); + swingReflectionInvoke("setForeground", global_speed_down_label, getMain_panel().isLimit_download_speed()?new Color(255,0,0):new Color(0,128,255)); + swingReflectionInvoke("setForeground", global_speed_up_label, getMain_panel().isLimit_upload_speed()?new Color(255,0,0):new Color(0,128,255)); + swingReflectionInvoke("setVisible", close_all_finished_down_button, false); + swingReflectionInvoke("setVisible", pause_all_down_button, false); + swingReflectionInvoke("setEnabled", clean_all_down_menu, false); + swingReflectionInvoke("setEnabled", clean_all_up_menu, false); + + jScrollPane_down.getVerticalScrollBar().setUnitIncrement(20); + jScrollPane_up.getVerticalScrollBar().setUnitIncrement(20); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + logo_label = new javax.swing.JLabel(); + kiss_server_status = new javax.swing.JLabel(); + jTabbedPane1 = new javax.swing.JTabbedPane(); + downloads_panel = new javax.swing.JPanel(); + global_speed_down_label = new javax.swing.JLabel(); + status_down_label = new javax.swing.JLabel(); + close_all_finished_down_button = new javax.swing.JButton(); + jScrollPane_down = new javax.swing.JScrollPane(); + jPanel_scroll_down = new javax.swing.JPanel(); + pause_all_down_button = new javax.swing.JButton(); + uploads_panel = new javax.swing.JPanel(); + global_speed_up_label = new javax.swing.JLabel(); + status_up_label = new javax.swing.JLabel(); + close_all_finished_up_button = new javax.swing.JButton(); + jScrollPane_up = new javax.swing.JScrollPane(); + jPanel_scroll_up = new javax.swing.JPanel(); + pause_all_up_button = new javax.swing.JButton(); + main_menubar = new javax.swing.JMenuBar(); + file_menu = new javax.swing.JMenu(); + new_download_menu = new javax.swing.JMenuItem(); + jSeparator6 = new javax.swing.JPopupMenu.Separator(); + new_upload_menu = new javax.swing.JMenuItem(); + jSeparator1 = new javax.swing.JPopupMenu.Separator(); + new_stream_menu = new javax.swing.JMenuItem(); + jSeparator4 = new javax.swing.JPopupMenu.Separator(); + hide_tray_menu = new javax.swing.JMenuItem(); + jSeparator2 = new javax.swing.JPopupMenu.Separator(); + clean_all_down_menu = new javax.swing.JMenuItem(); + jSeparator5 = new javax.swing.JPopupMenu.Separator(); + clean_all_up_menu = new javax.swing.JMenuItem(); + jSeparator3 = new javax.swing.JPopupMenu.Separator(); + exit_menu = new javax.swing.JMenuItem(); + edit_menu = new javax.swing.JMenu(); + settings_menu = new javax.swing.JMenuItem(); + help_menu = new javax.swing.JMenu(); + about_menu = new javax.swing.JMenuItem(); + + setTitle("MegaBasterd"); + + logo_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/mbasterd.png"))); // NOI18N + logo_label.setDoubleBuffered(true); + + kiss_server_status.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + kiss_server_status.setDoubleBuffered(true); + + jTabbedPane1.setDoubleBuffered(true); + jTabbedPane1.setFont(new java.awt.Font("Dialog", 1, 22)); // NOI18N + + global_speed_down_label.setFont(new java.awt.Font("Dialog", 1, 54)); // NOI18N + global_speed_down_label.setText("Speed"); + global_speed_down_label.setDoubleBuffered(true); + + status_down_label.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + status_down_label.setDoubleBuffered(true); + + close_all_finished_down_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + close_all_finished_down_button.setText("Close all finished"); + close_all_finished_down_button.setDoubleBuffered(true); + close_all_finished_down_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + close_all_finished_down_buttonActionPerformed(evt); + } + }); + + jPanel_scroll_down.setLayout(new javax.swing.BoxLayout(jPanel_scroll_down, javax.swing.BoxLayout.Y_AXIS)); + jScrollPane_down.setViewportView(jPanel_scroll_down); + + pause_all_down_button.setBackground(new java.awt.Color(255, 153, 0)); + pause_all_down_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + pause_all_down_button.setForeground(new java.awt.Color(255, 255, 255)); + pause_all_down_button.setText("PAUSE ALL"); + pause_all_down_button.setDoubleBuffered(true); + pause_all_down_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pause_all_down_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout downloads_panelLayout = new javax.swing.GroupLayout(downloads_panel); + downloads_panel.setLayout(downloads_panelLayout); + downloads_panelLayout.setHorizontalGroup( + downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addComponent(global_speed_down_label, javax.swing.GroupLayout.PREFERRED_SIZE, 404, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 338, Short.MAX_VALUE) + .addComponent(pause_all_down_button)) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(status_down_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(close_all_finished_down_button) + .addGap(6, 6, 6)) + .addComponent(jScrollPane_down) + ); + downloads_panelLayout.setVerticalGroup( + downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, downloads_panelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(close_all_finished_down_button, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(status_down_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane_down, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(global_speed_down_label) + .addComponent(pause_all_down_button))) + ); + + jTabbedPane1.addTab("Downloads", downloads_panel); + + global_speed_up_label.setFont(new java.awt.Font("Dialog", 1, 54)); // NOI18N + global_speed_up_label.setText("Speed"); + global_speed_up_label.setDoubleBuffered(true); + + status_up_label.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + + close_all_finished_up_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + close_all_finished_up_button.setText("Close all finished"); + close_all_finished_up_button.setDoubleBuffered(true); + close_all_finished_up_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + close_all_finished_up_buttonActionPerformed(evt); + } + }); + + jPanel_scroll_up.setLayout(new javax.swing.BoxLayout(jPanel_scroll_up, javax.swing.BoxLayout.Y_AXIS)); + jScrollPane_up.setViewportView(jPanel_scroll_up); + + pause_all_up_button.setBackground(new java.awt.Color(255, 153, 0)); + pause_all_up_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + pause_all_up_button.setForeground(new java.awt.Color(255, 255, 255)); + pause_all_up_button.setText("PAUSE ALL"); + pause_all_up_button.setDoubleBuffered(true); + pause_all_up_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pause_all_up_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout uploads_panelLayout = new javax.swing.GroupLayout(uploads_panel); + uploads_panel.setLayout(uploads_panelLayout); + uploads_panelLayout.setHorizontalGroup( + uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addComponent(global_speed_up_label, javax.swing.GroupLayout.PREFERRED_SIZE, 404, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 338, Short.MAX_VALUE) + .addComponent(pause_all_up_button)) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(status_up_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(close_all_finished_up_button) + .addGap(6, 6, 6)) + .addComponent(jScrollPane_up) + ); + uploads_panelLayout.setVerticalGroup( + uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, uploads_panelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(close_all_finished_up_button, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(status_up_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane_up, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(global_speed_up_label) + .addComponent(pause_all_up_button))) + ); + + jTabbedPane1.addTab("Uploads", uploads_panel); + + file_menu.setText("File"); + file_menu.setDoubleBuffered(true); + file_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + + new_download_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + new_download_menu.setText("New download"); + new_download_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + new_download_menuActionPerformed(evt); + } + }); + file_menu.add(new_download_menu); + file_menu.add(jSeparator6); + + new_upload_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + new_upload_menu.setText("New upload"); + new_upload_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + new_upload_menuActionPerformed(evt); + } + }); + file_menu.add(new_upload_menu); + file_menu.add(jSeparator1); + + new_stream_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + new_stream_menu.setText("New stream"); + new_stream_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + new_stream_menuActionPerformed(evt); + } + }); + file_menu.add(new_stream_menu); + file_menu.add(jSeparator4); + + hide_tray_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + hide_tray_menu.setText("Hide to tray"); + hide_tray_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hide_tray_menuActionPerformed(evt); + } + }); + file_menu.add(hide_tray_menu); + file_menu.add(jSeparator2); + + clean_all_down_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + clean_all_down_menu.setText("Remove all waiting downloads"); + clean_all_down_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + clean_all_down_menuActionPerformed(evt); + } + }); + file_menu.add(clean_all_down_menu); + file_menu.add(jSeparator5); + + clean_all_up_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + clean_all_up_menu.setText("Remove all waiting uploads"); + clean_all_up_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + clean_all_up_menuActionPerformed(evt); + } + }); + file_menu.add(clean_all_up_menu); + file_menu.add(jSeparator3); + + exit_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + exit_menu.setText("EXIT"); + exit_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exit_menuActionPerformed(evt); + } + }); + file_menu.add(exit_menu); + + main_menubar.add(file_menu); + + edit_menu.setText("Edit"); + edit_menu.setDoubleBuffered(true); + edit_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + + settings_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + settings_menu.setText("Settings"); + settings_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + settings_menuActionPerformed(evt); + } + }); + edit_menu.add(settings_menu); + + main_menubar.add(edit_menu); + + help_menu.setText("Help"); + help_menu.setDoubleBuffered(true); + help_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + + about_menu.setFont(new java.awt.Font("Dialog", 0, 20)); // NOI18N + about_menu.setText("About"); + about_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + about_menuActionPerformed(evt); + } + }); + help_menu.add(about_menu); + + main_menubar.add(help_menu); + + setJMenuBar(main_menubar); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jTabbedPane1) + .addGroup(layout.createSequentialGroup() + .addComponent(kiss_server_status, javax.swing.GroupLayout.PREFERRED_SIZE, 440, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(logo_label))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jTabbedPane1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(kiss_server_status, javax.swing.GroupLayout.PREFERRED_SIZE, 11, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(logo_label)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void new_download_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_new_download_menuActionPerformed + + + swingReflectionInvoke("setEnabled", new_download_menu, false); + + final LinkGrabberDialog dialog = new LinkGrabberDialog(this, true, _main_panel.getDefault_download_path(), _main_panel.getClipboardspy()); + + _main_panel.getClipboardspy().attachObserver(dialog); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + _main_panel.getClipboardspy().detachObserver(dialog); + + final String dl_path = dialog.getDownload_path(); + + jTabbedPane1.setSelectedIndex(0); + + if(dialog.isDownload()) { + + final MainPanelView main = this; + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + swingReflectionInvoke("setText", main.status_down_label, "Pre-processing downloads, please wait..."); + + Set urls = new HashSet(findAllRegex("(?:https?|mega)://[^/]*/(#.*?)?!.+![^\r\n]+", dialog.getLinks_textarea().getText(), 0)); + + Set megadownloader = new HashSet(findAllRegex("mega://enc.*?[^\r\n]+", dialog.getLinks_textarea().getText(), 0)); + + for (String link : megadownloader ) { + + try { + + urls.add(decryptMegaDownloaderLink(link)); + + } catch (Exception ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + } + + if(!urls.isEmpty()) { + + int conta_downloads = 0; + + for (String url : urls ) { + + url = url.replaceAll("^mega://", "https://mega.nz"); + + Download download; + + if(findFirstRegex("#F!", url, 0) != null) { + + FolderLinkDialog fdialog = new FolderLinkDialog(main, true, url); + + if(!fdialog.isMega_error()) { + + swingReflectionInvokeAndWait("setLocationRelativeTo", fdialog, main); + + swingReflectionInvokeAndWait("setVisible", fdialog, true); + + if(fdialog.isDownload()) { + + List folder_links = fdialog.getDownload_links(); + + fdialog.dispose(); + + for(HashMap folder_link:folder_links) { + + download = new Download(main.getMain_panel(), (String)folder_link.get("url"), dl_path, (String)folder_link.get("filename"), (String)folder_link.get("filekey"), (long)folder_link.get("filesize"), null, null, main.getMain_panel().isUse_slots_down(), main.getMain_panel().getDefault_slots_down(), true); + + main.getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); + + conta_downloads++; + } + } + + } + + fdialog.dispose(); + + + } else { + + download = new Download(main.getMain_panel(), url, dl_path, null, null, null, null, null, main.getMain_panel().isUse_slots_down(), main.getMain_panel().getDefault_slots_down(), false); + + main.getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); + + conta_downloads++; + } + } + + if(conta_downloads > 0) { + + swingReflectionInvoke("setText", main.status_down_label, "Starting downloads provisioning, please wait..."); + + main.getMain_panel().getDownload_manager().secureNotify(); + + } else { + + swingReflectionInvoke("setEnabled", main.new_download_menu, true); + } + + } else { + + swingReflectionInvoke("setEnabled", main.new_download_menu, true); + } + + swingReflectionInvoke("setText", main.status_down_label, ""); + + }}); + } else { + + swingReflectionInvoke("setEnabled", new_download_menu, true); + } + + dialog.dispose(); + + }//GEN-LAST:event_new_download_menuActionPerformed + + private void settings_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settings_menuActionPerformed + + + SettingsDialog dialog = new SettingsDialog(this, true); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + if(dialog.isSettings_ok()) { + + for(String email:dialog.getDeleted_accounts()) { + + try { + deleteMegaAccount(email); + } catch (SQLException ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + + _main_panel.getMega_accounts().remove(email); + + _main_panel.getMega_active_accounts().remove(email); + } + + _main_panel.loadUserSettings(); + + _main_panel.getStream_supervisor().setMaxBytesPerSecInput(_main_panel.isLimit_download_speed()?_main_panel.getMax_dl_speed()*1024:0); + + _main_panel.getStream_supervisor().setMaxBytesPerSecOutput( _main_panel.isLimit_upload_speed()?_main_panel.getMax_up_speed()*1024:0); + + swingReflectionInvoke("setForeground", global_speed_down_label, _main_panel.isLimit_download_speed()?new Color(255,0,0):new Color(0,128,255)); + + swingReflectionInvoke("setForeground", global_speed_up_label, _main_panel.isLimit_upload_speed()?new Color(255,0,0):new Color(0,128,255)); + + _main_panel.getDownload_manager().secureNotify(); + + _main_panel.getUpload_manager().secureNotify(); + } + + dialog.dispose(); + }//GEN-LAST:event_settings_menuActionPerformed + + private void hide_tray_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hide_tray_menuActionPerformed + + + dispatchEvent(new WindowEvent(this, WINDOW_CLOSING)); + }//GEN-LAST:event_hide_tray_menuActionPerformed + + private void about_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_about_menuActionPerformed + + + AboutDialog dialog = new AboutDialog(this, true); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + }//GEN-LAST:event_about_menuActionPerformed + + private void exit_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exit_menuActionPerformed + + + _main_panel._byebye(); + }//GEN-LAST:event_exit_menuActionPerformed + + private void close_all_finished_down_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_close_all_finished_down_buttonActionPerformed + + _main_panel.getDownload_manager().closeAllFinished(); + }//GEN-LAST:event_close_all_finished_down_buttonActionPerformed + + private void clean_all_down_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clean_all_down_menuActionPerformed + + + Object[] options = {"No", + "Yes"}; + + int n = showOptionDialog(this, + "Remove all waiting downloads?", + "Warning!", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, + options, + options[0]); + + if(n==1) { + _main_panel.getDownload_manager().closeAllWaiting(); + } + }//GEN-LAST:event_clean_all_down_menuActionPerformed + + private void pause_all_down_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pause_all_down_buttonActionPerformed + + + _main_panel.getDownload_manager().pauseAll(); + }//GEN-LAST:event_pause_all_down_buttonActionPerformed + + private void new_stream_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_new_stream_menuActionPerformed + + StreamerDialog dialog = new StreamerDialog(this, true, _main_panel.getClipboardspy()); + + _main_panel.getClipboardspy().attachObserver(dialog); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + _main_panel.getClipboardspy().detachObserver(dialog); + }//GEN-LAST:event_new_stream_menuActionPerformed + + private void new_upload_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_new_upload_menuActionPerformed + + + swingReflectionInvoke("setEnabled", new_upload_menu, false); + + final FileGrabberDialog dialog = new FileGrabberDialog(this,true); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + if(dialog.isUpload() && dialog.getFiles().size() > 0) { + + try { + + swingReflectionInvoke("setText", status_up_label, "Pre-processing uploads, please wait..."); + + final String mega_account = (String)dialog.getAccount_combobox().getSelectedItem(); + + HashMap data_account = (HashMap)_main_panel.getMega_accounts().get(mega_account); + + final String base_path = dialog.getBase_path(); + + final String dir_name=dialog.getDir_name_textfield().getText(); + + final int[] mega_aes_pass = bin2i32a(BASE642Bin((String)data_account.get("password_aes"))); + + final String mega_user_hash = (String)data_account.get("user_hash"); + + final ArrayList files = dialog.getFiles(); + + final MainPanelView main = this; + + jTabbedPane1.setSelectedIndex(1); + + THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + MegaAPI ma=main.getMain_panel().getMega_active_accounts().get(mega_account)!=null?main.getMain_panel().getMega_active_accounts().get(mega_account):new MegaAPI(); + + try { + + ma.login(mega_account, mega_aes_pass, mega_user_hash); + + main.getMain_panel().getMega_active_accounts().put(mega_account, ma); + + byte[] parent_key = ma.genFolderKey(); + + byte[] share_key = ma.genShareKey(); + + HashMap res = ma.createDir(dir_name!=null?dir_name:files.get(0).getName()+"_"+genID(10), ma.getRoot_id(), parent_key, i32a2bin(ma.getMaster_key())); + + String parent_node = (String)((Map)((List)res.get("f")).get(0)).get("h"); + + out.println("Dir "+ parent_node+" created"); + + ma.shareFolder(parent_node, parent_key, share_key); + + String folder_link = ma.getPublicFolderLink(parent_node, share_key); + + MegaDirNode file_paths = new MegaDirNode(parent_node); + + int conta = 1; + + for(File f:files) { + + swingReflectionInvoke("setText", main.status_up_label, "Pre-processing ("+(conta++)+"/"+files.size()+") uploads, please wait..."); + + String file_path = f.getParentFile().getAbsolutePath().replace(base_path, ""); + + String[] dirs = file_path.split("/"); + + out.println(file_path); + + MegaDirNode current_node = file_paths; + + String file_parent = current_node.getNode_id(); + + for(String d:dirs) { + + if(!d.isEmpty()) { + + if(current_node.getChildren().get(d) != null) { + + current_node = current_node.getChildren().get(d); + + file_parent = current_node.getNode_id(); + + } else { + + res = ma.createDirInsideAnotherSharedDir(d, current_node.getNode_id(), ma.genFolderKey(), i32a2bin(ma.getMaster_key()), parent_node, share_key); + + file_parent = (String)((Map)((List)res.get("f")).get(0)).get("h"); + + current_node.getChildren().put(d, new MegaDirNode(file_parent)); + + current_node = current_node.getChildren().get(d); + } + } + } + + Upload upload = new Upload(main.getMain_panel(), ma, f.getAbsolutePath(), file_parent, null, null, parent_node, share_key, folder_link, main.getMain_panel().isUse_slots_up(), main.getMain_panel().getDefault_slots_up(), false); + + main.getMain_panel().getUpload_manager().getTransference_provision_queue().add(upload); + + } + + swingReflectionInvoke("setText", main.status_up_label, "Starting uploads provisioning, please wait..."); + + main.getMain_panel().getUpload_manager().secureNotify(); + + } catch (Exception ex) { + swingReflectionInvoke("setEnabled", main.new_upload_menu, true); + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + } + }); + + + } catch (Exception ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } + } else { + swingReflectionInvoke("setEnabled", new_upload_menu, true); + } + + dialog.dispose(); + }//GEN-LAST:event_new_upload_menuActionPerformed + + private void close_all_finished_up_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_close_all_finished_up_buttonActionPerformed + + + _main_panel.getUpload_manager().closeAllFinished(); + }//GEN-LAST:event_close_all_finished_up_buttonActionPerformed + + private void pause_all_up_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pause_all_up_buttonActionPerformed + + + _main_panel.getUpload_manager().pauseAll(); + }//GEN-LAST:event_pause_all_up_buttonActionPerformed + + private void clean_all_up_menuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clean_all_up_menuActionPerformed + + Object[] options = {"No", + "Yes"}; + + int n = showOptionDialog(this, + "Remove all waiting uploads?", + "Warning!", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, + options, + options[0]); + + if(n==1) { + _main_panel.getUpload_manager().closeAllWaiting(); + } + }//GEN-LAST:event_clean_all_up_menuActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JMenuItem about_menu; + private javax.swing.JMenuItem clean_all_down_menu; + private javax.swing.JMenuItem clean_all_up_menu; + private javax.swing.JButton close_all_finished_down_button; + private javax.swing.JButton close_all_finished_up_button; + private javax.swing.JPanel downloads_panel; + private javax.swing.JMenu edit_menu; + private javax.swing.JMenuItem exit_menu; + private javax.swing.JMenu file_menu; + private javax.swing.JLabel global_speed_down_label; + private javax.swing.JLabel global_speed_up_label; + private javax.swing.JMenu help_menu; + private javax.swing.JMenuItem hide_tray_menu; + protected javax.swing.JPanel jPanel_scroll_down; + protected javax.swing.JPanel jPanel_scroll_up; + private javax.swing.JScrollPane jScrollPane_down; + private javax.swing.JScrollPane jScrollPane_up; + private javax.swing.JPopupMenu.Separator jSeparator1; + private javax.swing.JPopupMenu.Separator jSeparator2; + private javax.swing.JPopupMenu.Separator jSeparator3; + private javax.swing.JPopupMenu.Separator jSeparator4; + private javax.swing.JPopupMenu.Separator jSeparator5; + private javax.swing.JPopupMenu.Separator jSeparator6; + private javax.swing.JTabbedPane jTabbedPane1; + private javax.swing.JLabel kiss_server_status; + protected javax.swing.JLabel logo_label; + private javax.swing.JMenuBar main_menubar; + private javax.swing.JMenuItem new_download_menu; + private javax.swing.JMenuItem new_stream_menu; + private javax.swing.JMenuItem new_upload_menu; + private javax.swing.JButton pause_all_down_button; + private javax.swing.JButton pause_all_up_button; + private javax.swing.JMenuItem settings_menu; + private javax.swing.JLabel status_down_label; + private javax.swing.JLabel status_up_label; + private javax.swing.JPanel uploads_panel; + // End of variables declaration//GEN-END:variables + + + + + +} diff --git a/src/megabasterd/MegaAPI.java b/src/megabasterd/MegaAPI.java new file mode 100644 index 000000000..1533c66b8 --- /dev/null +++ b/src/megabasterd/MegaAPI.java @@ -0,0 +1,878 @@ +package megabasterd; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import javax.crypto.Cipher; +import static megabasterd.MiscTools.Bin2UrlBASE64; +import static megabasterd.MiscTools.UrlBASE642Bin; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.cleanFilename; +import static megabasterd.MiscTools.findFirstRegex; +import static megabasterd.MiscTools.genID; +import static megabasterd.MiscTools.genRandomByteArray; +import static megabasterd.MiscTools.getWaitTimeExpBackOff; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.mpi2big; +import org.codehaus.jackson.map.ObjectMapper; + + + +public final class MegaAPI { + + public static final String API_URL = "https://g.api.mega.co.nz"; + + public static final int REQ_ID_LENGTH = 10; + public static int checkMEGAError(String data) + { + String error = findFirstRegex("^\\[?(\\-[0-9]+)\\]?$", data, 1); + + return error != null?Integer.parseInt(error):0; + } + + private int _seqno; + + private String _sid; + + private String _api_key; + + private int[] _master_key; + + private BigInteger[] _rsa_priv_key; + + private int[] _password_aes; + + private String _user_hash; + + private String _root_id; + + + private String _inbox_id; + + private String _email; + + + private String _trashbin_id; + + private String _req_id; + public MegaAPI() + { + _req_id = null; + _trashbin_id = null; + _email = null; + _inbox_id = null; + _root_id = null; + _user_hash = null; + _password_aes = null; + _rsa_priv_key = null; + _master_key = null; + _api_key = null; + _sid = null; + + Random randomno = new Random(); + + _seqno=randomno.nextInt(); + + _req_id = genID(REQ_ID_LENGTH); + } + public MegaAPI(String ak) + { + _req_id = null; + _trashbin_id = null; + _email = null; + _inbox_id = null; + _root_id = null; + _user_hash = null; + _password_aes = null; + _rsa_priv_key = null; + _master_key = null; + _api_key = null; + _sid = null; + + Random randomno = new Random(); + + _seqno=randomno.nextInt(); + + _api_key=ak; + + _req_id = genID(REQ_ID_LENGTH); + } + public String getEmail() { + return _email; + } + public int[] getPassword_aes() { + return _password_aes; + } + public String getUser_hash() { + return _user_hash; + } + + public String getSid() { + return _sid; + } + + public String getApi_key() { + return _api_key; + } + + public int[] getMaster_key() { + return _master_key; + } + + public BigInteger[] getRsa_priv_key() { + return _rsa_priv_key; + } + + public String getRoot_id() { + return _root_id; + } + + public String getInbox_id() { + return _inbox_id; + } + + public String getTrashbin_id() { + return _trashbin_id; + } + + + + private void _realLogin() throws Exception, MegaAPIException { + + String request = "[{\"a\":\"us\", \"user\":\""+_email+"\", \"uh\":\""+_user_hash+"\"}]"; + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")); + + String res = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(res, HashMap[].class); + + String k = (String)res_map[0].get("k"); + + String privk = (String)res_map[0].get("privk"); + + _master_key = bin2i32a(decryptKey(UrlBASE642Bin(k), i32a2bin(_password_aes))); + + String csid = (String)res_map[0].get("csid"); + + if(csid != null) { + + int[] enc_rsa_priv_key = bin2i32a(UrlBASE642Bin(privk)); + + byte[] privk_byte = decryptKey(i32a2bin(enc_rsa_priv_key), i32a2bin(_master_key)); + + _rsa_priv_key = _extractRSAPrivKey(privk_byte); + + + byte[] raw_sid = CryptTools.rsaDecrypt(mpi2big(UrlBASE642Bin(csid)), _rsa_priv_key[0], _rsa_priv_key[1], _rsa_priv_key[2]); + + _sid = Bin2UrlBASE64(Arrays.copyOfRange(raw_sid, 0, 43)); + } + + fetchNodes(); + } + + public void login(String email, String password) throws Exception, MegaAPIException { + + _email = email; + + _password_aes = CryptTools.MEGAPrepareMasterKey(bin2i32a(password.getBytes())); + + _user_hash = CryptTools.MEGAUserHash(email.toLowerCase().getBytes(), _password_aes); + + _realLogin(); + } + + public void login(String email, int[] password_aes, String user_hash) throws Exception, MegaAPIException { + + _email = email; + + _password_aes = password_aes; + + _user_hash = user_hash; + + _realLogin(); + } + + public Long[] getQuota() { + + Long[] quota = new Long[2]; + + try { + String request = "[{\"a\": \"uq\", \"xfer\": 1, \"strg\": 1}]"; + + URL url_api; + + url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(res, HashMap[].class); + + if(res_map[0].get("cstrg") instanceof Integer) { + + quota[0] = ((Number)res_map[0].get("cstrg")).longValue(); + + } else if(res_map[0].get("cstrg") instanceof Long) { + + quota[0] = (Long)res_map[0].get("cstrg"); + } + + if(res_map[0].get("mstrg") instanceof Integer) { + + quota[1] = ((Number)res_map[0].get("mstrg")).longValue(); + + } else if(res_map[0].get("mstrg") instanceof Long) { + + quota[1] = (Long)res_map[0].get("mstrg"); + } + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return quota; + } + + public void fetchNodes() throws IOException { + + String request = "[{\"a\":\"f\", \"c\":1}]"; + + URL url_api; + + try { + + url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res = _rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(res, HashMap[].class); + + for(Object o:((Iterable)res_map[0].get("f"))) { + + HashMap element = (HashMap)o; + + int file_type = (int)element.get("t"); + + switch (file_type) { + + case 2: + _root_id = (String)element.get("h"); + break; + case 3: + _inbox_id = (String)element.get("h"); + break; + case 4: + _trashbin_id = (String)element.get("h"); + break; + default: + break; + } + } + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + } + + private String _rawRequest(String request, URL url_api) throws IOException, MegaAPIException { + + boolean error; + + int conta_error=0; + + String response=null; + + do{ + error = false; + + _seqno++; + + HttpURLConnection conn = (HttpURLConnection) url_api.openConnection(); + conn.setConnectTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + //conn.setRequestProperty("User-Agent", ""); -> Skips 509 error MEGA BUG???? + conn.setRequestProperty("Connection", "close"); + + OutputStream out; + out = conn.getOutputStream(); + out.write(request.getBytes()); + out.close(); + + if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) + { + System.out.println("Failed : HTTP error code : " + conn.getResponseCode()); + + error = true; + + try { + Thread.sleep( getWaitTimeExpBackOff(conta_error)); + } catch (InterruptedException ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + } else { + + String content_encoding = conn.getContentEncoding(); + + InputStream is=(content_encoding!=null && content_encoding.equals("gzip"))?new GZIPInputStream(conn.getInputStream()):conn.getInputStream(); + + ByteArrayOutputStream byte_res = new ByteArrayOutputStream(); + + byte[] buffer = new byte[16*1024]; + + int reads; + + while( (reads=is.read(buffer)) != -1 ) { + + byte_res.write(buffer, 0, reads); + } + + response = new String(byte_res.toByteArray()); + + conn.disconnect(); + + int mega_error; + + if( (mega_error=checkMEGAError(response))!=0 ) + { + if(mega_error == -3) { + + error = true; + + try { + Thread.sleep(getWaitTimeExpBackOff(conta_error)); + } catch (InterruptedException ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + } else { + + throw new MegaAPIException(String.valueOf(mega_error)); + } + } + } + + }while(error); + + return response; + + } + + public String getMegaFileDownloadUrl(String link) throws IOException, MegaAPIException + { + link=link.replace("/#!N?", "/#N!"); + + String file_id = findFirstRegex("#.*?!([^!]+)", link, 1); + + String request; + + URL url_api; + + if(findFirstRegex("#N!", link, 0) != null) + { + String folder_id = findFirstRegex("###n=(.+)$", link, 1); + + request = "[{\"a\":\"g\", \"g\":\"1\", \"n\":\""+file_id+"\"}]"; + + url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")+"&n="+folder_id); + + } else { + + request = "[{\"a\":\"g\", \"g\":\"1\", \"p\":\""+file_id+"\"}]"; + url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")); + } + + String data = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(data, HashMap[].class); + + return (String)res_map[0].get("g"); + } + + + public String[] getMegaFileMetadata(String link) throws Exception, MegaAPIException + { + + link=link.replace("/#!N?", "/#N!"); + + String file_id = findFirstRegex("#.*?!([^!]+)", link, 1); + + String file_key = findFirstRegex("#.*?![^!]+!([^!#]+)", link, 1); + + String request; + + URL url_api; + + if(findFirstRegex("#N!", link, 0) != null) + { + String folder_id = findFirstRegex("###n=(.+)$", link, 1); + + request = "[{\"a\":\"g\", \"g\":\"1\", \"n\":\""+file_id+"\"}]"; + + url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")+"&n="+folder_id); + + } else { + + request = "[{\"a\":\"g\", \"p\":\""+file_id+"\"}]"; + + url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")); + } + + String data = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(data, HashMap[].class); + + String fsize = String.valueOf(res_map[0].get("s")); + + String at = (String)res_map[0].get("at"); + + String[] file_data = null; + + HashMap att_map = _decAttr(at, CryptTools.initMEGALinkKey(file_key)); + + if(att_map != null) { + + String fname = cleanFilename((String)att_map.get("n")); + + file_data = new String[]{fname, fsize, file_key}; + + } else { + + throw new MegaAPIException("-14"); + } + + return file_data; + } + + + + + private byte[] _encAttr(String attr, byte[] key) { + + byte[] attr_byte = ("MEGA"+attr).getBytes(); + + int l = (int)(16 * Math.ceil((double)attr_byte.length/16)); + + byte[] new_attr_byte = Arrays.copyOfRange(attr_byte, 0, l); + + byte[] ret = null; + + try { + + ret = CryptTools.aes_cbc_encrypt(new_attr_byte, key, CryptTools.AES_ZERO_IV); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return ret; + } + + private HashMap _decAttr(String encAttr, byte[] key) { + + HashMap res_map = null; + + byte[] decrypted_at = null; + + try { + + Cipher decrypter = CryptTools.genDecrypter("AES", "AES/CBC/NoPadding", key, CryptTools.AES_ZERO_IV); + + decrypted_at = decrypter.doFinal(UrlBASE642Bin(encAttr)); + + String att = new String(decrypted_at).replaceAll("[\0]+$","").replaceAll("^MEGA", ""); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(att, HashMap.class); + + } + catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + + } + + return res_map; + } + + public String initUploadFile(String filename) { + + String ul_url = null; + + try { + + File f = new File(filename); + + String request = "[{\"a\":\"u\", \"s\":"+String.valueOf(f.length())+"}]"; + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(res, HashMap[].class); + + ul_url = (String)res_map[0].get("p"); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return ul_url; + } + + public HashMap finishUploadFile(String fbasename, int[] ul_key, int[] fkey, int[] meta_mac, String completion_handle, String mega_parent, byte[] master_key, String root_node, byte[] share_key) { + + HashMap[] res_map = null; + + try { + + byte[] enc_att = _encAttr("{\"n\":\""+fbasename+"\"}", i32a2bin(Arrays.copyOfRange(ul_key, 0, 4))); + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String request = "[{\"a\":\"p\", \"t\":\""+mega_parent+"\", \"n\":[{\"h\":\""+completion_handle+"\", \"t\":0, \"a\":\""+Bin2UrlBASE64(enc_att)+"\", \"k\":\""+Bin2UrlBASE64(encryptKey(i32a2bin(fkey), master_key))+"\"}], \"i\":\""+_req_id+"\", \"cr\" : [ [\"" + root_node + "\"] , [\""+completion_handle+"\"] , [0,0, \""+Bin2UrlBASE64(encryptKey(i32a2bin(fkey), share_key))+"\"]]}]"; + + String res = _rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(res, HashMap[].class); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return res_map[0]; + } + + public byte[] encryptKey(byte[] a, byte[] key) throws Exception { + + return CryptTools.aes_ecb_encrypt(a, key); + } + + public byte[] decryptKey(byte[] a, byte[] key) throws Exception { + + return CryptTools.aes_ecb_decrypt(a, key); + } + + private BigInteger[] _extractRSAPrivKey(byte[] rsa_data) { + + BigInteger[] rsa_key = new BigInteger[4]; + + for(int i=0, offset=0; i<4; i++) { + + int l = ( ( 256*( (((int)rsa_data[offset]) & 0xFF )) + ( ((int)rsa_data[offset+1]) & 0xFF ) + 7 ) /8 ) + 2; + + rsa_key[i] = mpi2big(Arrays.copyOfRange(rsa_data, offset, offset+l)); + + offset+=l; + } + + return rsa_key; + } + + public HashMap createDir(String name, String parent_node, byte[] node_key, byte[] master_key) { + + HashMap[] res_map=null; + + try { + + byte[] enc_att = _encAttr("{\"n\":\""+name+"\"}", node_key); + + byte[] enc_node_key = encryptKey(node_key, master_key); + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String request="[{\"a\":\"p\", \"t\":\""+parent_node+"\", \"n\":[{\"h\":\"xxxxxxxx\",\"t\":1,\"a\":\""+Bin2UrlBASE64(enc_att)+"\",\"k\":\""+Bin2UrlBASE64(enc_node_key)+"\"}],\"i\":\""+_req_id+"\"}]"; + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(res, HashMap[].class); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return res_map[0]; + + } + + + public HashMap createDirInsideAnotherSharedDir(String name, String parent_node, byte[] node_key, byte[] master_key, String root_node, byte[] share_key) { + + HashMap[] res_map=null; + + try { + + byte[] enc_att = _encAttr("{\"n\":\""+name+"\"}", node_key); + + byte[] enc_node_key = encryptKey(node_key, master_key); + + byte[] enc_node_key_s = encryptKey(node_key, share_key); + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String request="[{\"a\":\"p\", \"t\":\""+parent_node+"\", \"n\":[{\"h\":\"xxxxxxxx\",\"t\":1,\"a\":\""+Bin2UrlBASE64(enc_att)+"\",\"k\":\""+Bin2UrlBASE64(enc_node_key)+"\"}],\"i\":\""+_req_id+"\", \"cr\" : [ [\"" + root_node + "\"] , [\"xxxxxxxx\"] , [0,0, \""+Bin2UrlBASE64(enc_node_key_s)+"\"]]}]"; + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(res, HashMap[].class); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return res_map[0]; + + } + + + public String getPublicFileLink(String node, byte[] node_key) { + + String public_link=null; + + try { + + String file_id = null; + + List res_map=null; + + String request = "[{\"a\":\"l\", \"n\":\""+node+"\"}]"; + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(res, List.class); + + file_id = (String)res_map.get(0); + + public_link = "https://mega.nz/#!"+file_id+"!"+Bin2UrlBASE64(node_key); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return public_link; + } + + public String getPublicFolderLink(String node, byte[] node_key) { + + String public_link=null; + + try { + + String folder_id = null; + + List res_map=null; + + String request = "[{\"a\":\"l\", \"n\":\""+node+"\", \"i\":\""+_req_id+"\"}]"; + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + res_map = objectMapper.readValue(res, List.class); + + folder_id = (String)res_map.get(0); + + public_link = "https://mega.nz/#F!"+folder_id+"!"+Bin2UrlBASE64(node_key); + + }catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return public_link; + } + + public int[] genUploadKey() { + + return bin2i32a(genRandomByteArray(24)); + } + + public byte[] genFolderKey() { + + return genRandomByteArray(16); + } + + public byte[] genShareKey() { + + return genRandomByteArray(16); + } + + + public void shareFolder(String node, byte[] node_key, byte[] share_key) { + + try { + + String ok=Bin2UrlBASE64(encryptKey(share_key, i32a2bin(getMaster_key()))); + + String enc_nk = Bin2UrlBASE64(encryptKey(node_key, share_key)); + + String ha=cryptoHandleauth(node); + + String request = "[{\"a\":\"s2\",\"n\":\""+node+"\",\"s\":[{\"u\":\"EXP\",\"r\":0}],\"i\":\""+_req_id+"\",\"ok\":\""+ok+"\",\"ha\":\""+ha+"\",\"cr\":[[\""+node+"\"],[\""+node+"\"],[0,0,\""+enc_nk+"\"]]}]"; + + System.out.println(request); + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+"&sid="+_sid+(_api_key!=null?"&ak="+_api_key:"")); + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + } + + + public String cryptoHandleauth(String h) { + + String ch = null; + + try { + + ch = Bin2UrlBASE64(encryptKey((h+h).getBytes(), i32a2bin(getMaster_key()))); + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return ch; + } + + + public HashMap getFolderNodes(String folder_id, String folder_key) throws Exception { + + HashMap folder_nodes = null; + + String request = "[{\"a\":\"f\", \"c\":\"1\", \"r\":\"1\"}]"; + + URL url_api = new URL(API_URL+"/cs?id="+_seqno+(_api_key!=null?"&ak="+_api_key:"")+"&n="+folder_id); + + String res=_rawRequest(request, url_api); + + System.out.println(res); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap[] res_map = objectMapper.readValue(res, HashMap[].class); + + List nodes = (List)res_map[0].get("f"); + + folder_nodes = new HashMap<>(); + + for(Object o:nodes) { + + HashMap node = (HashMap)o; + + String[] node_k = ((String)node.get("k")).split(":"); + + String dec_node_k = Bin2UrlBASE64(decryptKey(UrlBASE642Bin(node_k[1]), _urlBase64KeyDecode(folder_key))); + + HashMap at = _decAttr((String)node.get("a"), _urlBase64KeyDecode(dec_node_k)); + + HashMap the_node = new HashMap<>(); + + the_node.put("type", node.get("t")); + + the_node.put("parent", node.get("p")); + + the_node.put("key", dec_node_k); + + if(node.get("s") != null) { + + if(node.get("s") instanceof Integer) { + + long size = ((Number)node.get("s")).longValue(); + the_node.put("size", size); + + } else if(node.get("s") instanceof Long) { + + long size = (Long)node.get("s"); + the_node.put("size", size); + } + } + + the_node.put("name", at.get("n")); + + the_node.put("h", node.get("h")); + + folder_nodes.put((String)node.get("h"), the_node); + } + + return folder_nodes; + } + + private byte[] _urlBase64KeyDecode(String key) { + + try { + byte[] key_bin = UrlBASE642Bin(key); + + if(key_bin.length < 32) { + + return Arrays.copyOfRange(key_bin, 0, 16); + + } else { + + int[] key_i32a = bin2i32a(Arrays.copyOfRange(key_bin, 0, 32)); + + int[] k = {key_i32a[0] ^ key_i32a[4], key_i32a[1] ^ key_i32a[5], key_i32a[2] ^ key_i32a[6], key_i32a[3] ^ key_i32a[7]}; + + return i32a2bin(k); + } + + } catch (Exception ex) { + Logger.getLogger(MegaAPI.class.getName()).log(Level.SEVERE, null, ex); + } + + return null; + } + +} diff --git a/src/megabasterd/MegaAPIException.java b/src/megabasterd/MegaAPIException.java new file mode 100644 index 000000000..d58ad2f47 --- /dev/null +++ b/src/megabasterd/MegaAPIException.java @@ -0,0 +1,11 @@ +package megabasterd; + + +public final class MegaAPIException extends Exception { + + public MegaAPIException(String message) + { + super(message); + } + +} \ No newline at end of file diff --git a/src/megabasterd/MegaCrypterAPI.java b/src/megabasterd/MegaCrypterAPI.java new file mode 100644 index 000000000..372cf3b08 --- /dev/null +++ b/src/megabasterd/MegaCrypterAPI.java @@ -0,0 +1,306 @@ +package megabasterd; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.swing.JOptionPane; +import static megabasterd.MiscTools.BASE642Bin; +import static megabasterd.MiscTools.Bin2BASE64; +import static megabasterd.MiscTools.Bin2UrlBASE64; +import static megabasterd.MiscTools.cleanFilePath; +import static megabasterd.MiscTools.cleanFilename; +import static megabasterd.MiscTools.findFirstRegex; +import org.codehaus.jackson.map.ObjectMapper; + + +/** + * + * @author tonikelope + */ +public final class MegaCrypterAPI { + + private static String _rawRequest(String request, URL url_api) throws IOException, MegaCrypterAPIException { + + HttpURLConnection conn = (HttpURLConnection) url_api.openConnection(); + conn.setConnectTimeout(MainPanel.CONNECTION_TIMEOUT); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("User-Agent", MainPanel.USER_AGENT); + conn.setRequestProperty("Connection", "close"); + + OutputStream out; + out = conn.getOutputStream(); + out.write(request.getBytes()); + out.close(); + + if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) + { + throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); + } + + String content_encoding = conn.getContentEncoding(); + + InputStream is=(content_encoding!=null && content_encoding.equals("gzip"))?new GZIPInputStream(conn.getInputStream()):conn.getInputStream(); + + ByteArrayOutputStream byte_res = new ByteArrayOutputStream(); + + byte[] buffer = new byte[16*1024]; + + int reads; + + while( (reads=is.read(buffer)) != -1 ) { + + byte_res.write(buffer, 0, reads); + } + + String response = new String(byte_res.toByteArray()); + + conn.disconnect(); + + int mc_error; + + if((mc_error=MegaCrypterAPI.checkMCError(response))!=0) + { + throw new MegaCrypterAPIException(String.valueOf(mc_error)); + } + + return response; + + } + + public static String getMegaFileDownloadUrl(String link, String pass_hash, String noexpire_token) throws IOException, MegaCrypterAPIException + { + String request = noexpire_token != null?"{\"m\":\"dl\", \"link\": \""+link+"\", \"noexpire\": \""+noexpire_token+"\"}":"{\"m\":\"dl\", \"link\": \""+link+"\"}"; + + URL url_api = new URL(findFirstRegex("https?://[^/]+", link, 0)+"/api"); + + String res = MegaCrypterAPI._rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap res_map = objectMapper.readValue(res, HashMap.class); + + String dl_url = (String)res_map.get("url"); + + if(pass_hash != null) + { + try { + String pass = (String)res_map.get("pass"); + + byte[] iv = BASE642Bin(pass); + + Cipher decrypter = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", BASE642Bin(pass_hash),iv); + + byte[] decrypted_url = decrypter.doFinal(BASE642Bin(dl_url)); + + dl_url = new String(decrypted_url); + + } catch (Exception ex) { + Logger.getLogger(MegaCrypterAPI.class.getName()).log(Level.SEVERE, null, ex); + } + } + + return dl_url; + } + + public static String[] getMegaFileMetadata(String link, MainPanelView panel) throws Exception, MegaCrypterAPIException + { + String request = "{\"m\":\"info\", \"link\": \""+link+"\"}"; + + URL url_api = new URL(findFirstRegex("https?://[^/]+", link, 0)+"/api"); + + String res = MegaCrypterAPI._rawRequest(request, url_api); + + ObjectMapper objectMapper = new ObjectMapper(); + + HashMap res_map = objectMapper.readValue(res, HashMap.class); + + String fname = cleanFilename((String)res_map.get("name")); + + String fpath=null; + + Object fpath_val = res_map.get("path"); + + if(fpath_val instanceof Boolean) { + + fpath = null; + + } else if (fpath_val instanceof String) { + + fpath = cleanFilePath((String)fpath_val); + } + + String file_size; + + try { + + file_size = String.valueOf(res_map.get("size")); + + } catch(java.lang.ClassCastException ex) { + + file_size = String.valueOf(res_map.get("size")); + } + + String fkey = (String)res_map.get("key"); + + String noexpire_token=null; + + Object expire_val = res_map.get("expire"); + + if(expire_val instanceof Boolean) { + + noexpire_token = null; + + } else if (expire_val instanceof String) { + + String aux[] = ((String) expire_val).split("#"); + + noexpire_token = aux[1]; + } + + String pass=null; + + Object pass_val = res_map.get("pass"); + + if(pass_val instanceof Boolean) { + + pass = null; + + } else if (expire_val instanceof String) { + + pass = (String)pass_val; + } + + System.out.println(noexpire_token); + + if(pass != null) + { + String[] pass_items = pass.split("#"); + + if(pass_items.length != 4) + { + throw new MegaCrypterAPIException("Bad password data!"); + } + + int iterations = Integer.parseInt(pass_items[0]); + + byte[] key_check = BASE642Bin(pass_items[1]); + + byte[] salt = BASE642Bin(pass_items[2]); + + byte[] iv = BASE642Bin(pass_items[3]); + + String password; + + byte[] info_key = null; + + boolean bad_pass; + + Cipher decrypter; + + try { + do + { + bad_pass = false; + + password = JOptionPane.showInputDialog(panel, "Enter password:"); + + if(password!=null) { + + SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + + KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, (int)Math.pow(2, iterations), 256); + + try { + + info_key=f.generateSecret(ks).getEncoded(); + + decrypter = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv); + + try { + + bad_pass = !Arrays.equals(info_key, decrypter.doFinal(key_check)); + + } catch (IllegalBlockSizeException | BadPaddingException ex) { + + bad_pass=true; + } + + } catch (InvalidKeySpecException ex) { + + bad_pass=true; + } + } + + }while(password!=null && bad_pass); + + if(password==null) + { + return null; + } + else + { + decrypter = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv); + + byte[] decrypted_key = decrypter.doFinal(BASE642Bin(fkey)); + + fkey = Bin2UrlBASE64(decrypted_key); + + decrypter = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv); + + byte[] decrypted_name = decrypter.doFinal(BASE642Bin(fname)); + + fname = new String(decrypted_name); + + if(fpath != null) + { + byte[] decrypted_fpath = decrypter.doFinal(BASE642Bin(fpath)); + + fpath = new String(decrypted_fpath); + } + + pass=Bin2BASE64(info_key); + + } + + } catch (Exception ex) { + Logger.getLogger(MegaCrypterAPI.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if(fpath != null) + { + fname = fpath+fname; + } + + String file_data[] = {fname, file_size, fkey, pass, noexpire_token}; + + return file_data; + } + + private static int checkMCError(String data) + { + String error = findFirstRegex("\"error\" *: *([0-9-]+)", data, 1); + + return error != null?Integer.parseInt(error):0; + } + + private MegaCrypterAPI() { + } +} diff --git a/src/megabasterd/MegaCrypterAPIException.java b/src/megabasterd/MegaCrypterAPIException.java new file mode 100644 index 000000000..d6ca167e6 --- /dev/null +++ b/src/megabasterd/MegaCrypterAPIException.java @@ -0,0 +1,10 @@ +package megabasterd; + +public final class MegaCrypterAPIException extends Exception { + + public MegaCrypterAPIException(String message) + { + super(message); + } + +} diff --git a/src/megabasterd/MegaDirNode.java b/src/megabasterd/MegaDirNode.java new file mode 100644 index 000000000..3f61c2b1d --- /dev/null +++ b/src/megabasterd/MegaDirNode.java @@ -0,0 +1,30 @@ +package megabasterd; + +import java.util.HashMap; + +/** + * + * @author tonikelope + */ +public final class MegaDirNode { + + private final String _node_id; + + private final HashMap _children; + + public MegaDirNode(String node_id) { + + _node_id = node_id; + + _children = new HashMap<>(); + } + + public String getNode_id() { + return _node_id; + } + + public HashMap getChildren() { + return _children; + } + +} diff --git a/src/megabasterd/MegaMutableTreeNode.java b/src/megabasterd/MegaMutableTreeNode.java new file mode 100644 index 000000000..1397569f4 --- /dev/null +++ b/src/megabasterd/MegaMutableTreeNode.java @@ -0,0 +1,39 @@ +package megabasterd; + +import java.util.HashMap; +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * + * @author tonikelope + */ +public final class MegaMutableTreeNode extends DefaultMutableTreeNode { + + public MegaMutableTreeNode() { + super(); + } + + public MegaMutableTreeNode(Object o) { + super(o); + } + + @Override + public String toString() { + + if(userObject instanceof HashMap) { + + HashMap user_object = (HashMap)userObject; + + return user_object.get("name") + ((isLeaf() && user_object.get("size")!=null)?" ["+MiscTools.formatBytes((long)user_object.get("size"))+"]":""); + + } else if(userObject instanceof Object) { + + return userObject.toString(); + + } else { + + return ""; + } + } + +} diff --git a/src/megabasterd/MiscTools.java b/src/megabasterd/MiscTools.java new file mode 100644 index 000000000..4ce2baba5 --- /dev/null +++ b/src/megabasterd/MiscTools.java @@ -0,0 +1,820 @@ +package megabasterd; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.GraphicsEnvironment; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.MutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.xml.bind.DatatypeConverter; + +public final class MiscTools { + + public static final int EXP_BACKOFF_BASE=2; + public static final int EXP_BACKOFF_SECS_RETRY=1; + public static final int EXP_BACKOFF_MAX_WAIT_TIME=128; + private static final ConcurrentHashMap REFLECTION_METHOD_CACHE = new ConcurrentHashMap<>(); + private static final Comparator _treeNodeComparator = new Comparator< DefaultMutableTreeNode>() { + + @Override public int compare(DefaultMutableTreeNode a, DefaultMutableTreeNode b) { + + + if (a.isLeaf() && !b.isLeaf()) { + return 1; + } else if (!a.isLeaf() && b.isLeaf()) { + return -1; + } else { + String sa = a.getUserObject().toString(); + String sb = b.getUserObject().toString(); + return sa.compareToIgnoreCase(sb); + } + } + }; + + public static Font createAndRegisterFont(String name) { + + Font font = null; + + try { + + font = Font.createFont(Font.TRUETYPE_FONT, MiscTools.class.getResourceAsStream(name)); + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + + ge.registerFont(font); + + } catch (FontFormatException | IOException ex) { + Logger.getLogger(DownloadView.class.getName()).log(Level.SEVERE, null, ex); + } + + return font; + } + + public static void setNimbusLookAndFeel() { + + try { + for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + javax.swing.UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(MainPanelView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } + } + + public static int[] bin2i32a(byte[] bin) + { + int l = (int)(4 * Math.ceil((double)bin.length/4)); + + byte[] new_bin = Arrays.copyOfRange(bin, 0, l); + + bin = new_bin; + + ByteBuffer bin_buffer = ByteBuffer.wrap(bin); + IntBuffer int_buffer = bin_buffer.asIntBuffer(); + + if(int_buffer.hasArray()) { + return int_buffer.array(); + } + else + { + ArrayList list = new ArrayList(); + + while(int_buffer.hasRemaining()) { + list.add(int_buffer.get()); + } + + int[] aux = new int[list.size()]; + + for(int i=0; i list = new ArrayList(); + + while(int_buffer.hasRemaining()) { + list.add(bin_buffer.get()); + } + + byte[] aux = new byte[list.size()]; + + for(int i=0; i= 0; i--) { + b[i] = (byte) val; + val >>>= 8; + } + + return b; + } + + public static long bytearray2long(byte[] val) { + + long l=0; + + for (int i = 0; i <=7; i++) { + l+=val[i]; + l<<=8; + } + + return l; + } + + public static String findFirstRegex(String regex, String data, int group) + { + Pattern pattern = Pattern.compile(regex); + + Matcher matcher = pattern.matcher(data); + + return matcher.find()?matcher.group(group):null; + } + + public static ArrayList findAllRegex(String regex, String data, int group) + { + Pattern pattern = Pattern.compile(regex); + + Matcher matcher = pattern.matcher(data); + + ArrayList matches = new ArrayList<>(); + + while(matcher.find()) { + matches.add(matcher.group(group)); + } + + return matches; + } + + public static void updateFont(javax.swing.JComponent label, Font font, int layout) + { + label.setFont(font.deriveFont(layout, label.getFont().getSize())); + } + + + public static String HashString(String algo, String data) throws NoSuchAlgorithmException, UnsupportedEncodingException + { + MessageDigest md = MessageDigest.getInstance(algo); + + byte[] thedigest = md.digest(data.getBytes("UTF-8")); + + return bin2hex(thedigest); + } + + public static String HashString(String algo, byte[] data) throws NoSuchAlgorithmException + { + MessageDigest md = MessageDigest.getInstance(algo); + + byte[] thedigest = md.digest(data); + + return bin2hex(thedigest); + } + + public static byte[] HashBin(String algo, String data) throws NoSuchAlgorithmException, UnsupportedEncodingException + { + MessageDigest md = MessageDigest.getInstance(algo); + + return md.digest(data.getBytes("UTF-8")); + } + + public static byte[] HashBin(String algo, byte[] data) throws NoSuchAlgorithmException + { + MessageDigest md = MessageDigest.getInstance(algo); + + return md.digest(data); + } + + public static byte[] BASE642Bin(String data) throws Exception + { + return Base64.getDecoder().decode(data); + } + + public static String Bin2BASE64(byte[] data) throws Exception + { + return Base64.getEncoder().encodeToString(data); + } + + public static byte[] UrlBASE642Bin(String data) throws Exception + { + return Base64.getUrlDecoder().decode(data); + } + + public static String Bin2UrlBASE64(byte[] data) throws Exception + { + return Base64.getUrlEncoder().withoutPadding().encodeToString(data); + } + + + public static long getWaitTimeExpBackOff(int retryCount) { + + long waitTime = ((long) Math.pow(EXP_BACKOFF_BASE, retryCount) * EXP_BACKOFF_SECS_RETRY); + + return Math.min(waitTime, EXP_BACKOFF_MAX_WAIT_TIME); + } + + public static void swingReflectionInvoke(final String method_name, final Object obj, final Object... params) { + + _swingReflectionInvoke(method_name, obj, false, params); + } + + public static void swingReflectionInvokeAndWait(final String method_name, final Object obj, final Object... params) { + + _swingReflectionInvoke(method_name, obj, true, params); + } + + private static void _swingReflectionInvoke(final String method_name, final Object obj, final boolean wait, final Object... params) { + + Runnable r = new Runnable(){ + + @Override + public void run() { + + Method method; + + try { + + if( (method=REFLECTION_METHOD_CACHE.get( method_name+"#"+obj.getClass().toString()+"#"+String.valueOf(params.length))) != null) { + + try { + + method.invoke(obj, params); + + } catch(SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + + method = null; + } + } + + if(method == null) { + + for(Method m:obj.getClass().getMethods()) { + + if(m.getName().equals(method_name) && m.getParameterCount() == params.length) { + + try { + + m.invoke(obj, params); + + REFLECTION_METHOD_CACHE.put(method_name+"#"+obj.getClass().toString()+"#"+String.valueOf(params.length), m); + + method = m; + + break; + + }catch(SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex2) { + + } + } + } + + if(method == null) { + + throw new NoSuchMethodException(); + + } + } + + } catch (SecurityException | IllegalArgumentException | NoSuchMethodException ex) { + + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + + System.out.println("REFLECTION METHOD NOT FOUND -> "+method_name+"#"+obj.getClass().toString()+"#"+String.valueOf(params.length)); + } + } + }; + + swingInvokeIt(r, wait); + } + + + public static Object swingReflectionInvokeAndWaitForReturn(final String method_name, final Object obj, final Object... params) { + + Callable c = new Callable(){ + + @Override + public Object call() { + + Object ret = null; + + Method method; + + try { + + if( (method=REFLECTION_METHOD_CACHE.get( method_name+"#"+obj.getClass().toString()+"#"+String.valueOf(params.length))) != null) { + + try { + + ret = method.invoke(obj, params); + + } catch(SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + + method = null; + } + } + + if(method == null) { + + for(Method m:obj.getClass().getMethods()) { + + if(m.getName().equals(method_name) && m.getParameterCount() == params.length) { + + try { + + ret = m.invoke(obj, params); + + REFLECTION_METHOD_CACHE.put(method_name+"#"+obj.getClass().toString()+"#"+String.valueOf(params.length), m); + + method = m; + + break; + + }catch(SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex2) { + + } + } + } + + if(method == null) { + + throw new NoSuchMethodException(); + } + } + + } catch (SecurityException | IllegalArgumentException | NoSuchMethodException ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + + return ret; + } + }; + + return swingInvokeItAndWaitForReturn(c); + } + + + private static void swingInvokeIt(Runnable r, boolean wait) { + + if(wait) { + + if(SwingUtilities.isEventDispatchThread()) { + + r.run(); + + } else { + + try { + /* OJO!!! El thread que lanza esto NO PUEDE poseer locks que necesite el EDT o se producirá un DEADLOCK */ + SwingUtilities.invokeAndWait(r); + + } catch (InterruptedException | InvocationTargetException ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + } + + }else{ + + SwingUtilities.invokeLater(r); + } + } + + private static Object swingInvokeItAndWaitForReturn(Callable c) + { + Object ret=null; + + if(SwingUtilities.isEventDispatchThread()) { + + try { + ret = c.call(); + } catch (Exception ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + + } else { + + FutureTask futureTask = new FutureTask<>(c); + + SwingUtilities.invokeLater(futureTask); + + try { + ret = futureTask.get(); + } catch (InterruptedException | ExecutionException ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + } + + return ret; + } + + public static String bin2hex(byte[] b){ + + BigInteger bi = new BigInteger(1, b); + + return String.format("%0" + (b.length << 1) + "x", bi); + } + + public static byte[] hex2bin(String s) { + return DatatypeConverter.parseHexBinary(s); + } + + public static void copyTextToClipboard(String text) { + + StringSelection stringSelection = new StringSelection (text); + Clipboard clpbrd = Toolkit.getDefaultToolkit ().getSystemClipboard (); + clpbrd.setContents (stringSelection, null); + + } + + public static String deflateURL(String link) throws MalformedURLException, IOException { + + String urlString = "http://tinyurl.com/api-create.php?url="+URLEncoder.encode(link.trim(), "UTF-8"); + + URL url = new URL(urlString); + + URLConnection conn = url.openConnection(); + + conn.setRequestProperty("User-Agent", MainPanel.USER_AGENT); + + String content_encoding = conn.getContentEncoding(); + + InputStream is=(content_encoding!=null && content_encoding.equals("gzip"))?new GZIPInputStream(conn.getInputStream()):conn.getInputStream(); + + ByteArrayOutputStream byte_res = new ByteArrayOutputStream(); + + byte[] buffer = new byte[16*1024]; + + int reads; + + while( (reads=is.read(buffer)) != -1 ) { + + byte_res.write(buffer, 0, reads); + } + + String response = new String(byte_res.toByteArray()).trim(); + + return MiscTools.findFirstRegex("http", response, 0)!=null?response:link; + } + + public static String formatBytes(Long bytes) { + + String[] units = {"B", "KB", "MB", "GB", "TB"}; + + bytes = Math.max(bytes, 0L); + + int pow = Math.min((int)((bytes>0L?Math.log(bytes):0) / Math.log(1024)), units.length - 1); + + Double bytes_double = (double)bytes/(1 << (10 * pow)); + + DecimalFormat df = new DecimalFormat("#.##"); + + return df.format(bytes_double) + ' ' + units[pow]; + } + + public static DefaultMutableTreeNode sortTree(DefaultMutableTreeNode root) { + + Enumeration e = root.depthFirstEnumeration(); + + while (e.hasMoreElements()) { + + DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement(); + + if (!node.isLeaf()) { + + _sortTreeNode(node); + + } + } + + return root; + + } + + + private static void _sortTreeNode(DefaultMutableTreeNode parent) { + + int n = parent.getChildCount(); + + List children = new ArrayList<>(n); + + for (int i = 0; i < n; i++) { + + children.add((DefaultMutableTreeNode) parent.getChildAt(i)); + } + + Collections.sort(children, _treeNodeComparator); + + parent.removeAllChildren(); + + for (DefaultMutableTreeNode node: children) { + + parent.add(node); + + } + } + + public static boolean deleteSelectedTreeItems(JTree tree) { + + TreePath[] paths = tree.getSelectionPaths(); + + if(paths != null) { + + DefaultTreeModel model = (DefaultTreeModel) (tree.getModel()); + + DefaultMutableTreeNode node; + + for (TreePath path : paths) { + + node = (DefaultMutableTreeNode) (path.getLastPathComponent()); + + if(node != null && node != model.getRoot()) { + + DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); + + model.removeNodeFromParent(node); + + while(parent.isLeaf() && parent != model.getRoot()) { + + DefaultMutableTreeNode parent_aux = (DefaultMutableTreeNode) parent.getParent(); + + model.removeNodeFromParent(parent); + + parent = parent_aux; + } + } + } + + return true; + } + + return false; + } + + public static boolean deleteAllExceptSelectedTreeItems(JTree tree) { + + TreePath[] paths = tree.getSelectionPaths(); + + HashMap hashmap_old = new HashMap<>(); + + if(paths != null) { + + Class node_class = tree.getModel().getRoot().getClass(); + + Object new_root = null; + + try { + + new_root = node_class.newInstance(); + + ((MutableTreeNode)new_root).setUserObject( ((DefaultMutableTreeNode)tree.getModel().getRoot()).getUserObject() ); + + } catch (InstantiationException | IllegalAccessException ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + + for (TreePath path : paths) { + + Object parent = new_root; + + for(Object path_element:path.getPath()) { + + if((DefaultMutableTreeNode)path_element != (DefaultMutableTreeNode)tree.getModel().getRoot()) { + + if(hashmap_old.get(path_element) == null) { + + Object node=null; + + if((DefaultMutableTreeNode)path_element == (DefaultMutableTreeNode)path.getLastPathComponent()) { + + node = path_element; + + } else { + + try { + + node = node_class.newInstance(); + + ((MutableTreeNode)node).setUserObject( ((DefaultMutableTreeNode)path_element).getUserObject() ); + + } catch (InstantiationException | IllegalAccessException ex) { + Logger.getLogger(MiscTools.class.getName()).log(Level.SEVERE, null, ex); + } + } + + ((DefaultMutableTreeNode)parent).add((MutableTreeNode)node); + + if(!((TreeNode)path_element).isLeaf()) { + + hashmap_old.put((DefaultMutableTreeNode)path_element, (DefaultMutableTreeNode)node); + + parent = node; + } + + } else { + + parent = hashmap_old.get(path_element); + } + } + } + } + + tree.setModel(new DefaultTreeModel(MiscTools.sortTree((DefaultMutableTreeNode)new_root))); + + return true; + } + + return false; + } + + public static String truncateText(String text, int max_length) { + + String separator = " ... "; + + max_length-=separator.length(); + + if(max_length %2 != 0) { + + max_length--; + } + + return (text.length() > max_length)?text.replaceAll("^(.{1,"+(max_length/2)+"}).*?(.{1,"+(max_length/2)+"})$", "$1"+separator+"$2"):text; + } + + public static String cleanFilename(String filename) { + + return (System.getProperty("os.name").toLowerCase().contains("win")?filename.replaceAll("[<>:\"/\\\\\\|\\?\\*]+", "").replaceAll("[ \\.]{1,}/{1,}", "/"):filename).replaceAll("[\\.]{1,}$", "").trim(); + } + + public static String cleanFilePath(String path) { + + return path.equals(".")?((System.getProperty("os.name").toLowerCase().contains("win")?path.replaceAll("[<>:\"\\|\\?\\*]+", "").replaceAll("[ \\.]{1,}/{1,}", "/"):path).replaceAll("[\\.]{1,}$", "").trim()):path; + } + + public static byte[] genRandomByteArray(int length) { + + byte[] the_array = new byte[length]; + + Random randomno = new Random(); + + randomno.nextBytes(the_array); + + return the_array; + } + + public static String extractStringFromClipboardContents(Transferable contents) { + + String ret = null; + + if(contents != null) { + + try { + + Object o = contents.getTransferData(DataFlavor.stringFlavor); + + if(o instanceof String) { + + ret = (String)o; + } + + } catch (Exception ex) {} + } + + return ret; + + } + + public static String extractMegaLinksFromString(String data) { + + String res = ""; + + if(data != null) { + + ArrayList links = MiscTools.findAllRegex("(?:https?|mega)://[^/]*/(#.*?)?!.+![^\r\n]+", data, 0); + + links.addAll(MiscTools.findAllRegex("mega://enc.*?[^\r\n]+", data, 0)); + + for(String s:links) { + + res+=s+"\n"; + } + } + + return res.trim(); + } + + public static String extractFirstMegaLinkFromString(String data) { + + String res = ""; + + if(data != null) { + + ArrayList links = MiscTools.findAllRegex("(?:https?|mega)://[^/]*/(#.*?)?!.+![^\r\n]+", data, 0); + + links.addAll(MiscTools.findAllRegex("mega://enc.*?[^\r\n]+", data, 0)); + + if(links.size()>0) { + + res = links.get(0); + } + } + + return res; + } + + private MiscTools() { + } + + +} diff --git a/src/megabasterd/ProgressMeter.java b/src/megabasterd/ProgressMeter.java new file mode 100644 index 000000000..5eb0d8e5d --- /dev/null +++ b/src/megabasterd/ProgressMeter.java @@ -0,0 +1,88 @@ +package megabasterd; + +import static java.lang.System.out; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; + +public final class ProgressMeter implements Runnable, SecureNotifiable +{ + private final Transference _transference; + private volatile boolean _exit; + private final Object _secure_notify_lock; + private boolean _notified=false; + + ProgressMeter(Transference transference) + { + _secure_notify_lock = new Object(); + _transference = transference; + _exit = false; + } + + public void setExit(boolean value) + { + _exit = value; + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + getLogger(ProgressMeter.class.getName()).log(SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + @Override + public void run() + { + out.println("ProgressMeter hello!"); + + while(!_exit || !_transference.getPartialProgress().isEmpty()) + { + Integer reads; + + while( (reads=_transference.getPartialProgress().poll()) !=null ) + { + _transference.updateProgress(reads); + } + + if(!_exit) + { + secureWait(); + } + } + + } + +} diff --git a/src/megabasterd/SecureNotifiable.java b/src/megabasterd/SecureNotifiable.java new file mode 100644 index 000000000..59c5d7266 --- /dev/null +++ b/src/megabasterd/SecureNotifiable.java @@ -0,0 +1,14 @@ +package megabasterd; + +/** + * + * @author tonikelope + */ +public interface SecureNotifiable { + + public void secureNotify(); + + public void secureNotifyAll(); + + public void secureWait(); +} diff --git a/src/megabasterd/SettingsDialog.form b/src/megabasterd/SettingsDialog.form new file mode 100644 index 000000000..7683fccf8 --- /dev/null +++ b/src/megabasterd/SettingsDialog.form @@ -0,0 +1,542 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + +
+ diff --git a/src/megabasterd/SettingsDialog.java b/src/megabasterd/SettingsDialog.java new file mode 100644 index 000000000..431581ba4 --- /dev/null +++ b/src/megabasterd/SettingsDialog.java @@ -0,0 +1,925 @@ +package megabasterd; + +import java.awt.Font; +import java.io.File; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.table.DefaultTableModel; +import static megabasterd.DBTools.insertSettingValueInDB; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MiscTools.Bin2BASE64; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.truncateText; +import static megabasterd.MiscTools.updateFont; + + +public final class SettingsDialog extends javax.swing.JDialog { + + private String _download_path; + private boolean _settings_ok; + private final Set _deleted_accounts; + private final MainPanel _main_panel; + + public boolean isSettings_ok() { + return _settings_ok; + } + + public Set getDeleted_accounts() { + return _deleted_accounts; + } + + + + /** + * Creates new form Settings + * @param parent + * @param modal + */ + public SettingsDialog(javax.swing.JFrame parent, boolean modal) { + super(parent, modal); + initComponents(); + + updateFont(change_download_dir_button, FONT_DEFAULT, Font.PLAIN); + updateFont(down_dir_label, FONT_DEFAULT, Font.PLAIN); + updateFont(ok_button, FONT_DEFAULT, Font.PLAIN); + updateFont(cancel_button, FONT_DEFAULT, Font.PLAIN); + updateFont(default_slots_down_label, FONT_DEFAULT, Font.PLAIN); + updateFont(default_slots_down_spinner, FONT_DEFAULT, Font.PLAIN); + updateFont(default_slots_up, FONT_DEFAULT, Font.PLAIN); + updateFont(max_downloads_label, FONT_DEFAULT, Font.PLAIN); + updateFont(max_downloads_spinner, FONT_DEFAULT, Font.PLAIN); + updateFont(max_uploads_spinner, FONT_DEFAULT, Font.PLAIN); + updateFont(verify_file_down_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(multi_slot_down_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(multi_slot_up_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(jTabbedPane1, FONT_DEFAULT, Font.PLAIN); + updateFont(status, FONT_DEFAULT, Font.PLAIN); + updateFont(remove_account_button, FONT_DEFAULT, Font.PLAIN); + updateFont(add_account_button, FONT_DEFAULT, Font.PLAIN); + updateFont(accounts_label, FONT_DEFAULT, Font.PLAIN); + updateFont(defaut_slots_up_label, FONT_DEFAULT, Font.PLAIN); + updateFont(max_uploads_label, FONT_DEFAULT, Font.PLAIN); + + updateFont(max_down_speed_label, FONT_DEFAULT, Font.PLAIN); + updateFont(limit_download_speed_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(max_down_speed_spinner, FONT_DEFAULT, Font.PLAIN); + + updateFont(max_up_speed_spinner, FONT_DEFAULT, Font.PLAIN); + updateFont(max_up_speed_label, FONT_DEFAULT, Font.PLAIN); + updateFont(limit_upload_speed_checkbox, FONT_DEFAULT, Font.PLAIN); + + updateFont(default_dir_label, FONT_DEFAULT, Font.PLAIN); + + _main_panel = ((MainPanelView)parent).getMain_panel(); + + String default_download_dir = DBTools.selectSettingValueFromDB("default_down_dir"); + + if(default_download_dir == null) { + default_download_dir = "."; + } + + _download_path = default_download_dir; + + swingReflectionInvoke("setText", default_dir_label, truncateText(_download_path, 80)); + + String slots = DBTools.selectSettingValueFromDB("default_slots_down"); + + int default_slots=Download.WORKERS_DEFAULT; + + if(slots != null) { + default_slots = Integer.parseInt(slots); + } + + default_slots_down_spinner.setModel(new SpinnerNumberModel(default_slots, Download.MIN_WORKERS, Download.MAX_WORKERS, 1)); + ((JSpinner.DefaultEditor)default_slots_down_spinner.getEditor()).getTextField().setEditable(false); + + + slots = DBTools.selectSettingValueFromDB("default_slots_up"); + + default_slots=Upload.WORKERS_DEFAULT; + + if(slots != null) { + default_slots = Integer.parseInt(slots); + } + + default_slots_up.setModel(new SpinnerNumberModel(default_slots, Upload.MIN_WORKERS, Upload.MAX_WORKERS, 1)); + ((JSpinner.DefaultEditor)default_slots_up.getEditor()).getTextField().setEditable(false); + + String max_down = DBTools.selectSettingValueFromDB("max_downloads"); + + int max_dl = Download.SIM_TRANSFERENCES_DEFAULT; + + if(max_down != null) { + max_dl = Integer.parseInt(max_down); + } + + + this.max_downloads_spinner.setModel(new SpinnerNumberModel(max_dl, 1, Download.MAX_SIM_TRANSFERENCES, 1)); + ((JSpinner.DefaultEditor)this.max_downloads_spinner.getEditor()).getTextField().setEditable(false); + + + String max_up = DBTools.selectSettingValueFromDB("max_uploads"); + + int max_ul = Upload.SIM_TRANSFERENCES_DEFAULT; + + if(max_up != null) { + max_ul = Integer.parseInt(max_up); + } + + + this.max_uploads_spinner.setModel(new SpinnerNumberModel(max_ul, 1, Upload.MAX_SIM_TRANSFERENCES, 1)); + ((JSpinner.DefaultEditor)this.max_uploads_spinner.getEditor()).getTextField().setEditable(false); + + + + boolean limit_dl_speed = Download.LIMIT_TRANSFERENCE_SPEED_DEFAULT; + + String limit_download_speed = DBTools.selectSettingValueFromDB("limit_download_speed"); + + if(limit_download_speed != null) { + limit_dl_speed = limit_download_speed.equals("yes"); + } + + limit_download_speed_checkbox.setSelected(limit_dl_speed); + + if(!limit_dl_speed) { + + swingReflectionInvoke("setEnabled", max_down_speed_label, false); + swingReflectionInvoke("setEnabled", max_down_speed_spinner, false); + } else { + swingReflectionInvoke("setEnabled", max_down_speed_label, true); + swingReflectionInvoke("setEnabled", max_down_speed_spinner, true); + } + + String max_dl_speed = DBTools.selectSettingValueFromDB("max_download_speed"); + + int max_download_speed = Download.MAX_TRANSFERENCE_SPEED_DEFAULT; + + if(max_dl_speed != null) { + max_download_speed = Integer.parseInt(max_dl_speed); + } + + + max_down_speed_spinner.setModel(new SpinnerNumberModel(max_download_speed, 0, Integer.MAX_VALUE, 1)); + ((JSpinner.DefaultEditor)max_down_speed_spinner.getEditor()).getTextField().setEditable(true); + + + boolean limit_ul_speed = Upload.LIMIT_TRANSFERENCE_SPEED_DEFAULT; + + String limit_upload_speed = DBTools.selectSettingValueFromDB("limit_upload_speed"); + + if(limit_upload_speed != null) { + limit_ul_speed = limit_upload_speed.equals("yes"); + } + + limit_upload_speed_checkbox.setSelected(limit_ul_speed); + + if(!limit_ul_speed) { + + swingReflectionInvoke("setEnabled", max_up_speed_label, false); + swingReflectionInvoke("setEnabled", max_up_speed_spinner, false); + } else { + swingReflectionInvoke("setEnabled", max_up_speed_label, true); + swingReflectionInvoke("setEnabled", max_up_speed_spinner, true); + } + + String max_ul_speed = DBTools.selectSettingValueFromDB("max_upload_speed"); + + int max_upload_speed = Upload.MAX_TRANSFERENCE_SPEED_DEFAULT; + + if(max_ul_speed != null) { + max_upload_speed = Integer.parseInt(max_ul_speed); + } + + max_up_speed_spinner.setModel(new SpinnerNumberModel(max_upload_speed, 0, Integer.MAX_VALUE, 1)); + + ((JSpinner.DefaultEditor)max_up_speed_spinner.getEditor()).getTextField().setEditable(true); + + + boolean cbc_mac = Download.VERIFY_CBC_MAC_DEFAULT; + + String verify_file = DBTools.selectSettingValueFromDB("verify_down_file"); + + if(verify_file != null) { + cbc_mac = (verify_file.equals("yes")); + } + + verify_file_down_checkbox.setSelected(cbc_mac); + + boolean use_slots = Download.USE_SLOTS_DEFAULT; + + String use_slots_val = DBTools.selectSettingValueFromDB("use_slots_down"); + + if(use_slots_val != null) { + use_slots = use_slots_val.equals("yes"); + } + + multi_slot_down_checkbox.setSelected(use_slots); + + if(!use_slots) { + + swingReflectionInvoke("setEnabled", default_slots_down_label, false); + swingReflectionInvoke("setEnabled", default_slots_down_spinner, false); + } else { + swingReflectionInvoke("setEnabled", default_slots_down_label, true); + swingReflectionInvoke("setEnabled", default_slots_down_spinner, true); + } + + + use_slots = Upload.USE_SLOTS_DEFAULT; + + use_slots_val = DBTools.selectSettingValueFromDB("use_slots_up"); + + if(use_slots_val != null) { + use_slots = use_slots_val.equals("yes"); + } + + multi_slot_up_checkbox.setSelected(use_slots); + + if(!use_slots) { + + swingReflectionInvoke("setEnabled", defaut_slots_up_label, false); + swingReflectionInvoke("setEnabled", default_slots_up, false); + } else { + swingReflectionInvoke("setEnabled", max_uploads_label, true); + swingReflectionInvoke("setEnabled", default_slots_up, true); + } + + DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + + HashMap data = (HashMap)pair.getValue(); + + String[] new_row_data = {(String)pair.getKey(), (String)data.get("password")}; + + model.addRow(new_row_data); + } + + swingReflectionInvoke("setEnabled", remove_account_button, (jTable1.getRowCount()>0)); + + _deleted_accounts = new HashSet(); + + _settings_ok = false; + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + ok_button = new javax.swing.JButton(); + cancel_button = new javax.swing.JButton(); + jTabbedPane1 = new javax.swing.JTabbedPane(); + downloads_panel = new javax.swing.JPanel(); + default_slots_down_spinner = new javax.swing.JSpinner(); + max_downloads_label = new javax.swing.JLabel(); + max_downloads_spinner = new javax.swing.JSpinner(); + verify_file_down_checkbox = new javax.swing.JCheckBox(); + down_dir_label = new javax.swing.JLabel(); + change_download_dir_button = new javax.swing.JButton(); + default_slots_down_label = new javax.swing.JLabel(); + multi_slot_down_checkbox = new javax.swing.JCheckBox(); + limit_download_speed_checkbox = new javax.swing.JCheckBox(); + max_down_speed_label = new javax.swing.JLabel(); + max_down_speed_spinner = new javax.swing.JSpinner(); + default_dir_label = new javax.swing.JLabel(); + uploads_panel = new javax.swing.JPanel(); + accounts_scrollpane = new javax.swing.JScrollPane(); + jTable1 = new javax.swing.JTable(); + accounts_label = new javax.swing.JLabel(); + remove_account_button = new javax.swing.JButton(); + add_account_button = new javax.swing.JButton(); + defaut_slots_up_label = new javax.swing.JLabel(); + max_uploads_label = new javax.swing.JLabel(); + default_slots_up = new javax.swing.JSpinner(); + max_uploads_spinner = new javax.swing.JSpinner(); + multi_slot_up_checkbox = new javax.swing.JCheckBox(); + max_up_speed_label = new javax.swing.JLabel(); + max_up_speed_spinner = new javax.swing.JSpinner(); + limit_upload_speed_checkbox = new javax.swing.JCheckBox(); + status = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Settings"); + + ok_button.setFont(new java.awt.Font("Dialog", 1, 22)); // NOI18N + ok_button.setText("OK"); + ok_button.setDoubleBuffered(true); + ok_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + ok_buttonActionPerformed(evt); + } + }); + + cancel_button.setFont(new java.awt.Font("Dialog", 1, 22)); // NOI18N + cancel_button.setText("CANCEL"); + cancel_button.setDoubleBuffered(true); + cancel_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancel_buttonActionPerformed(evt); + } + }); + + jTabbedPane1.setDoubleBuffered(true); + jTabbedPane1.setFont(new java.awt.Font("Dialog", 1, 22)); // NOI18N + + default_slots_down_spinner.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + default_slots_down_spinner.setDoubleBuffered(true); + default_slots_down_spinner.setValue(2); + + max_downloads_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_downloads_label.setText("Max sim downloads:"); + max_downloads_label.setDoubleBuffered(true); + + max_downloads_spinner.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_downloads_spinner.setDoubleBuffered(true); + + verify_file_down_checkbox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + verify_file_down_checkbox.setText("Verify file integrity (when download is finished)"); + verify_file_down_checkbox.setDoubleBuffered(true); + + down_dir_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + down_dir_label.setText("Default downloads directory:"); + down_dir_label.setDoubleBuffered(true); + + change_download_dir_button.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + change_download_dir_button.setText("Change it"); + change_download_dir_button.setDoubleBuffered(true); + change_download_dir_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + change_download_dir_buttonActionPerformed(evt); + } + }); + + default_slots_down_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + default_slots_down_label.setText("Default slots per file:"); + default_slots_down_label.setDoubleBuffered(true); + + multi_slot_down_checkbox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + multi_slot_down_checkbox.setText("Use multi slot download mode (NOT recommended. Download restart needed.)"); + multi_slot_down_checkbox.setDoubleBuffered(true); + multi_slot_down_checkbox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + multi_slot_down_checkboxStateChanged(evt); + } + }); + + limit_download_speed_checkbox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + limit_download_speed_checkbox.setText("Limit download speed"); + limit_download_speed_checkbox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + limit_download_speed_checkboxStateChanged(evt); + } + }); + + max_down_speed_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_down_speed_label.setText("Max speed (KB/s):"); + + max_down_speed_spinner.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + + default_dir_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + + javax.swing.GroupLayout downloads_panelLayout = new javax.swing.GroupLayout(downloads_panel); + downloads_panel.setLayout(downloads_panelLayout); + downloads_panelLayout.setHorizontalGroup( + downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addComponent(change_download_dir_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(default_dir_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(verify_file_down_checkbox) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, downloads_panelLayout.createSequentialGroup() + .addComponent(max_down_speed_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(max_down_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addComponent(max_downloads_label) + .addGap(79, 79, 79) + .addComponent(max_downloads_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addComponent(default_slots_down_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(default_slots_down_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(limit_download_speed_checkbox) + .addComponent(multi_slot_down_checkbox) + .addComponent(down_dir_label)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + downloads_panelLayout.setVerticalGroup( + downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(downloads_panelLayout.createSequentialGroup() + .addGap(14, 14, 14) + .addComponent(down_dir_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(change_download_dir_button) + .addComponent(default_dir_label)) + .addGap(18, 18, 18) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(max_downloads_label) + .addComponent(max_downloads_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(multi_slot_down_checkbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(default_slots_down_label) + .addComponent(default_slots_down_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(limit_download_speed_checkbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(downloads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(max_down_speed_label) + .addComponent(max_down_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(verify_file_down_checkbox) + .addContainerGap(221, Short.MAX_VALUE)) + ); + + jTabbedPane1.addTab("Downloads", downloads_panel); + + jTable1.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N + jTable1.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Email", "Password" + } + )); + jTable1.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + jTable1.setDoubleBuffered(true); + jTable1.setRowHeight(24); + accounts_scrollpane.setViewportView(jTable1); + + accounts_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + accounts_label.setText("Your MEGA accounts:"); + accounts_label.setDoubleBuffered(true); + + remove_account_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + remove_account_button.setText("Remove selected"); + remove_account_button.setDoubleBuffered(true); + remove_account_button.setEnabled(false); + remove_account_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + remove_account_buttonActionPerformed(evt); + } + }); + + add_account_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + add_account_button.setText("Add account"); + add_account_button.setDoubleBuffered(true); + add_account_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + add_account_buttonActionPerformed(evt); + } + }); + + defaut_slots_up_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + defaut_slots_up_label.setText("Default slots per file:"); + defaut_slots_up_label.setDoubleBuffered(true); + + max_uploads_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_uploads_label.setText("Max sim uploads:"); + max_uploads_label.setDoubleBuffered(true); + + default_slots_up.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + default_slots_up.setDoubleBuffered(true); + default_slots_up.setValue(2); + + max_uploads_spinner.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_uploads_spinner.setDoubleBuffered(true); + + multi_slot_up_checkbox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + multi_slot_up_checkbox.setText("Use multi slot upload mode (Recommended. Upload restart needed.)"); + multi_slot_up_checkbox.setDoubleBuffered(true); + multi_slot_up_checkbox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + multi_slot_up_checkboxStateChanged(evt); + } + }); + + max_up_speed_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + max_up_speed_label.setText("Max speed (KB/s):"); + + max_up_speed_spinner.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + + limit_upload_speed_checkbox.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + limit_upload_speed_checkbox.setText("Limit upload speed"); + limit_upload_speed_checkbox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + limit_upload_speed_checkboxStateChanged(evt); + } + }); + + javax.swing.GroupLayout uploads_panelLayout = new javax.swing.GroupLayout(uploads_panel); + uploads_panel.setLayout(uploads_panelLayout); + uploads_panelLayout.setHorizontalGroup( + uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(accounts_scrollpane, javax.swing.GroupLayout.DEFAULT_SIZE, 924, Short.MAX_VALUE) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(accounts_label) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(defaut_slots_up_label) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(max_up_speed_label) + .addComponent(limit_upload_speed_checkbox))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(default_slots_up, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(max_up_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addComponent(max_uploads_label) + .addGap(123, 123, 123) + .addComponent(max_uploads_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(multi_slot_up_checkbox)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addComponent(remove_account_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(add_account_button))) + .addContainerGap()) + ); + uploads_panelLayout.setVerticalGroup( + uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(accounts_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(accounts_scrollpane, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(remove_account_button) + .addComponent(add_account_button)) + .addGap(18, 18, 18) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(max_uploads_label) + .addComponent(max_uploads_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(multi_slot_up_checkbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(defaut_slots_up_label) + .addComponent(default_slots_up, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(limit_upload_speed_checkbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(max_up_speed_label) + .addComponent(max_up_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(25, Short.MAX_VALUE)) + ); + + jTabbedPane1.addTab("Uploads", uploads_panel); + + status.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + status.setDoubleBuffered(true); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jTabbedPane1) + .addGroup(layout.createSequentialGroup() + .addComponent(status, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ok_button, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(cancel_button, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(ok_button) + .addComponent(cancel_button)) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(status) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + ); + + pack(); + }// //GEN-END:initComponents + + private void change_download_dir_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_change_download_dir_buttonActionPerformed + + + javax.swing.JFileChooser filechooser = new javax.swing.JFileChooser(); + + filechooser.setCurrentDirectory(new java.io.File(_download_path)); + filechooser.setDialogTitle("Default download directory"); + filechooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); + filechooser.setAcceptAllFileFilterUsed(false); + + if( filechooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION ) { + + File file = filechooser.getSelectedFile(); + + _download_path = file.getAbsolutePath(); + + swingReflectionInvoke("setText", default_dir_label, truncateText(_download_path, 80)); + + } + }//GEN-LAST:event_change_download_dir_buttonActionPerformed + + private void cancel_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancel_buttonActionPerformed + + + swingReflectionInvoke("setVisible", this, false); + }//GEN-LAST:event_cancel_buttonActionPerformed + + private void ok_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ok_buttonActionPerformed + + try { + + + _settings_ok = true; + + insertSettingValueInDB("default_down_dir", _download_path); + insertSettingValueInDB("default_slots_down", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", default_slots_down_spinner))); + insertSettingValueInDB("default_slots_up", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", default_slots_up))); + insertSettingValueInDB("use_slots_down", (boolean)swingReflectionInvokeAndWaitForReturn("isSelected", multi_slot_down_checkbox)?"yes":"no"); + insertSettingValueInDB("use_slots_up", (boolean)swingReflectionInvokeAndWaitForReturn("isSelected", multi_slot_up_checkbox)?"yes":"no"); + insertSettingValueInDB("max_downloads", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", max_downloads_spinner))); + insertSettingValueInDB("max_uploads", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", max_uploads_spinner))); + insertSettingValueInDB("verify_down_file", (boolean)swingReflectionInvokeAndWaitForReturn("isSelected", verify_file_down_checkbox)?"yes":"no"); + insertSettingValueInDB("limit_download_speed", (boolean)swingReflectionInvokeAndWaitForReturn("isSelected", limit_download_speed_checkbox)?"yes":"no"); + insertSettingValueInDB("max_download_speed", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", max_down_speed_spinner))); + insertSettingValueInDB("limit_upload_speed", (boolean)swingReflectionInvokeAndWaitForReturn("isSelected", limit_upload_speed_checkbox)?"yes":"no"); + insertSettingValueInDB("max_upload_speed", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", max_up_speed_spinner))); + + + final DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + + swingReflectionInvoke("setText", status, "Checking your MEGA accounts, please wait..."); + + swingReflectionInvoke("setEnabled", ok_button, false); + + swingReflectionInvoke("setEnabled", cancel_button, false); + + swingReflectionInvoke("setEnabled", remove_account_button, false); + + swingReflectionInvoke("setEnabled", add_account_button, false); + + swingReflectionInvoke("setEnabled", jTable1, false); + + final SettingsDialog dialog = this; + + MainPanel.THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + ArrayList email_error = new ArrayList<>(); + + for(int i=0; i mega_account_data = (HashMap)dialog._main_panel.getMega_accounts().get(email); + + if(!mega_account_data.get("password").equals(pass)) { + + ma = new MegaAPI(); + + try { + ma.login(email, pass); + + dialog._main_panel.getMega_active_accounts().put(email, ma); + + DBTools.insertMegaAccount(email, pass, Bin2BASE64(i32a2bin(ma.getPassword_aes())), ma.getUser_hash()); + + } catch (Exception ex) { + + email_error.add(email); + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + + } + } + } + } + } + + if(email_error.size() > 0) { + + String email_error_s = ""; + + for(String s:email_error) { + + email_error_s+=s+"\n"; + } + + swingReflectionInvoke("setText", dialog.status, ""); + + JOptionPane.showMessageDialog(dialog, "There were errors with some accounts. Please, check them:\n\n"+email_error_s); + + swingReflectionInvoke("setEnabled", dialog.ok_button, true); + + swingReflectionInvoke("setEnabled", dialog.cancel_button, true); + + swingReflectionInvoke("setEnabled", dialog.remove_account_button, true); + + swingReflectionInvoke("setEnabled", dialog.add_account_button, true); + + swingReflectionInvoke("setEnabled", dialog.jTable1, true); + + } else { + swingReflectionInvoke("setVisible", dialog, false); + } + + } + }); + } catch (SQLException ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + }//GEN-LAST:event_ok_buttonActionPerformed + + private void multi_slot_down_checkboxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_multi_slot_down_checkboxStateChanged + + + if(!(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", multi_slot_down_checkbox)) { + + swingReflectionInvoke("setEnabled", default_slots_down_spinner, false); + swingReflectionInvoke("setEnabled", default_slots_down_label, false); + } else { + swingReflectionInvoke("setEnabled", default_slots_down_spinner, true); + swingReflectionInvoke("setEnabled", default_slots_down_label, true); + } + }//GEN-LAST:event_multi_slot_down_checkboxStateChanged + + private void remove_account_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remove_account_buttonActionPerformed + + + DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + + int selected = jTable1.getSelectedRow(); + + while(selected >= 0) { + + String email = (String)model.getValueAt(jTable1.convertRowIndexToModel(selected),0); + + _deleted_accounts.add(email); + + model.removeRow(jTable1.convertRowIndexToModel(selected)); + + selected = jTable1.getSelectedRow(); + } + + jTable1.clearSelection(); + + if(model.getRowCount() == 0) { + + swingReflectionInvoke("setEnabled", remove_account_button, false); + } + }//GEN-LAST:event_remove_account_buttonActionPerformed + + private void add_account_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_add_account_buttonActionPerformed + + + DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + + model.addRow(new Object[]{"",""}); + + jTable1.clearSelection(); + + swingReflectionInvoke("setEnabled", ok_button, true); + }//GEN-LAST:event_add_account_buttonActionPerformed + + private void multi_slot_up_checkboxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_multi_slot_up_checkboxStateChanged + + + if(!(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", multi_slot_up_checkbox)) { + + swingReflectionInvoke("setEnabled", defaut_slots_up_label, false); + swingReflectionInvoke("setEnabled", default_slots_up, false); + } else { + swingReflectionInvoke("setEnabled", defaut_slots_up_label, true); + swingReflectionInvoke("setEnabled", default_slots_up, true); + } + }//GEN-LAST:event_multi_slot_up_checkboxStateChanged + + private void limit_download_speed_checkboxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_limit_download_speed_checkboxStateChanged + + + if(!(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", limit_download_speed_checkbox)) { + + swingReflectionInvoke("setEnabled", max_down_speed_label, false); + swingReflectionInvoke("setEnabled", max_down_speed_spinner, false); + } else { + swingReflectionInvoke("setEnabled", max_down_speed_label, true); + swingReflectionInvoke("setEnabled", max_down_speed_spinner, true); + } + }//GEN-LAST:event_limit_download_speed_checkboxStateChanged + + private void limit_upload_speed_checkboxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_limit_upload_speed_checkboxStateChanged + + + if(!(boolean)swingReflectionInvokeAndWaitForReturn("isSelected", limit_upload_speed_checkbox)) { + + swingReflectionInvoke("setEnabled", max_up_speed_label, false); + swingReflectionInvoke("setEnabled", max_up_speed_spinner, false); + } else { + swingReflectionInvoke("setEnabled", max_up_speed_label, true); + swingReflectionInvoke("setEnabled", max_up_speed_spinner, true); + } + }//GEN-LAST:event_limit_upload_speed_checkboxStateChanged + + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel accounts_label; + private javax.swing.JScrollPane accounts_scrollpane; + private javax.swing.JButton add_account_button; + private javax.swing.JButton cancel_button; + private javax.swing.JButton change_download_dir_button; + private javax.swing.JLabel default_dir_label; + private javax.swing.JLabel default_slots_down_label; + private javax.swing.JSpinner default_slots_down_spinner; + private javax.swing.JSpinner default_slots_up; + private javax.swing.JLabel defaut_slots_up_label; + private javax.swing.JLabel down_dir_label; + private javax.swing.JPanel downloads_panel; + private javax.swing.JTabbedPane jTabbedPane1; + private javax.swing.JTable jTable1; + private javax.swing.JCheckBox limit_download_speed_checkbox; + private javax.swing.JCheckBox limit_upload_speed_checkbox; + private javax.swing.JLabel max_down_speed_label; + private javax.swing.JSpinner max_down_speed_spinner; + private javax.swing.JLabel max_downloads_label; + private javax.swing.JSpinner max_downloads_spinner; + private javax.swing.JLabel max_up_speed_label; + private javax.swing.JSpinner max_up_speed_spinner; + private javax.swing.JLabel max_uploads_label; + private javax.swing.JSpinner max_uploads_spinner; + private javax.swing.JCheckBox multi_slot_down_checkbox; + private javax.swing.JCheckBox multi_slot_up_checkbox; + private javax.swing.JButton ok_button; + private javax.swing.JButton remove_account_button; + private javax.swing.JLabel status; + private javax.swing.JPanel uploads_panel; + private javax.swing.JCheckBox verify_file_down_checkbox; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/SpeedMeter.java b/src/megabasterd/SpeedMeter.java new file mode 100644 index 000000000..dff4af1b6 --- /dev/null +++ b/src/megabasterd/SpeedMeter.java @@ -0,0 +1,183 @@ +package megabasterd; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import static megabasterd.MiscTools.formatBytes; + +public final class SpeedMeter implements Runnable, SecureNotifiable +{ + public static final int SLEEP = 3000; + private long _progress; + private final Transference _transference; + private final GlobalSpeedMeter _gspeed; + private volatile long _lastSpeed; + private volatile boolean _exit; + private final Object _secure_notify_lock; + private boolean _notified=false; + + + SpeedMeter(Transference transference, GlobalSpeedMeter gspeed) + { + _secure_notify_lock = new Object(); + _transference = transference; + _progress = transference.getProgress(); + _lastSpeed=0; + _gspeed = gspeed; + _exit=false; + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(UploadMACGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + + public void setExit(boolean exit) { + _exit = exit; + } + + public long getLastSpeed() + { + return _lastSpeed; + } + + public void setLastSpeed(long speed) + { + _lastSpeed = speed; + } + + @Override + public void run() + { + System.out.println("SpeedMeter hello!"); + + long p, sp=0; + int no_data_count; + + _transference.getView().updateSpeed("------", true); + _transference.getView().updateRemainingTime("--d --:--:--", true); + + try + { + no_data_count = 0; + + while(!_exit) + { + Thread.sleep(SpeedMeter.SLEEP*(no_data_count+1)); + + if(!_exit) + { + p = _transference.getProgress(); + + if(_transference.isPaused()) { + + _transference.getView().updateSpeed("------", true); + + _transference.getView().updateRemainingTime("--d --:--:--", true); + + setLastSpeed(0); + + _gspeed.secureNotify(); + + secureWait(); + + } else if( p > _progress) { + + double sleep_time = ((double)SpeedMeter.SLEEP*(no_data_count+1))/1000 ; + + double current_speed = (p - _progress) / sleep_time; + + _progress = p; + + sp = Math.round(current_speed); + + if(sp > 0) { + + _transference.getView().updateSpeed(formatBytes(sp)+"/s", true); + + _transference.getView().updateRemainingTime(calculateRemTime((long)Math.floor((_transference.getFile_size()-p)/sp ) ), true); + + setLastSpeed(sp); + + _gspeed.secureNotify(); + } + + no_data_count=0; + + } else { + + _transference.getView().updateSpeed("------", true); + + _transference.getView().updateRemainingTime("--d --:--:--", true); + + setLastSpeed(0); + + _gspeed.secureNotify(); + + no_data_count++; + } + } + } + } + catch (InterruptedException ex) + { + + } + + } + + private String calculateRemTime(long seconds) + { + int days = (int) TimeUnit.SECONDS.toDays(seconds); + + long hours = TimeUnit.SECONDS.toHours(seconds) - + TimeUnit.DAYS.toHours(days); + + long minutes = TimeUnit.SECONDS.toMinutes(seconds) - + TimeUnit.DAYS.toMinutes(days) - + TimeUnit.HOURS.toMinutes(hours); + + long secs = TimeUnit.SECONDS.toSeconds(seconds) - + TimeUnit.DAYS.toSeconds(days) - + TimeUnit.HOURS.toSeconds(hours) - + TimeUnit.MINUTES.toSeconds(minutes); + + return String.format("%dd %d:%02d:%02d", days, hours, minutes, secs); + } +} diff --git a/src/megabasterd/SqliteSingleton.java b/src/megabasterd/SqliteSingleton.java new file mode 100644 index 000000000..73250ec18 --- /dev/null +++ b/src/megabasterd/SqliteSingleton.java @@ -0,0 +1,44 @@ +package megabasterd; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author tonikelope + */ +public final class SqliteSingleton { + + public static final String SQLITE_FILE="megabasterd.db"; + public static SqliteSingleton getInstance() { + + return LazyHolder.INSTANCE; + } + + private SqliteSingleton() {} + public Connection getConn() { + + Connection conn=null; + + try { + + Class.forName("org.sqlite.JDBC"); + + conn = DriverManager.getConnection("jdbc:sqlite:"+SQLITE_FILE); + + }catch(ClassNotFoundException | SQLException ex) { + Logger.getLogger(SqliteSingleton.class.getName()).log(Level.SEVERE, null, ex); + } + + return conn; + } + + private final static class LazyHolder { + + private static final SqliteSingleton INSTANCE = new SqliteSingleton(); + } + +} diff --git a/src/megabasterd/StreamThrottlerSupervisor.java b/src/megabasterd/StreamThrottlerSupervisor.java new file mode 100644 index 000000000..b7c08bb72 --- /dev/null +++ b/src/megabasterd/StreamThrottlerSupervisor.java @@ -0,0 +1,154 @@ +package megabasterd; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author tonikelope + */ +public final class StreamThrottlerSupervisor implements Runnable, SecureNotifiable { + + private final ConcurrentLinkedQueue _input_slice_queue; + + private final ConcurrentLinkedQueue _output_slice_queue; + + private final int _slice_size; + + private volatile int _maxBytesPerSecInput; + + private volatile int _maxBytesPerSecOutput; + + private final Object _secure_notify_lock; + + private boolean _notified=false; + public StreamThrottlerSupervisor(int maxBytesPerSecInput, int maxBytesPerSecOutput, int slice_size) { + + _secure_notify_lock = new Object(); + + _maxBytesPerSecInput = maxBytesPerSecInput; + + _maxBytesPerSecOutput = maxBytesPerSecOutput; + + _slice_size = slice_size; + + _input_slice_queue = new ConcurrentLinkedQueue<>(); + + _output_slice_queue = new ConcurrentLinkedQueue<>(); + } + + public int getMaxBytesPerSecInput() { + return _maxBytesPerSecInput; + } + + public void setMaxBytesPerSecInput(int _maxBytesPerSecInput) { + this._maxBytesPerSecInput = _maxBytesPerSecInput; + } + + public int getMaxBytesPerSecOutput() { + return _maxBytesPerSecOutput; + } + + public void setMaxBytesPerSecOutput(int _maxBytesPerSecOutput) { + this._maxBytesPerSecOutput = _maxBytesPerSecOutput; + } + + public ConcurrentLinkedQueue getInput_slice_queue() { + return _input_slice_queue; + } + + public ConcurrentLinkedQueue getOutput_slice_queue() { + return _output_slice_queue; + } + + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(UploadMACGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + @Override + public void run() { + + while(true) { + + if(_maxBytesPerSecInput > 0) { + + _input_slice_queue.clear(); + + int slice_num = (int)Math.floor((double)_maxBytesPerSecInput / _slice_size); + + for(int i=0; i 0) { + + _output_slice_queue.clear(); + + int slice_num = (int)Math.floor((double)_maxBytesPerSecOutput / _slice_size); + + for(int i=0; i + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/StreamerDialog.java b/src/megabasterd/StreamerDialog.java new file mode 100644 index 000000000..f0727d825 --- /dev/null +++ b/src/megabasterd/StreamerDialog.java @@ -0,0 +1,184 @@ +package megabasterd; + +import java.awt.Font; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; +import static megabasterd.MainPanel.FONT_DEFAULT; + +/** + * + * @author tonikelope + */ +public final class StreamerDialog extends javax.swing.JDialog implements ClipboardChangeObserver { + + private final ClipboardSpy _clipboardspy; + + /** + * Creates new form Streamer + */ + public StreamerDialog(java.awt.Frame parent, boolean modal, ClipboardSpy clipboardspy) { + super(parent, modal); + initComponents(); + + _clipboardspy = clipboardspy; + + MiscTools.updateFont(put_label, FONT_DEFAULT, Font.PLAIN); + MiscTools.updateFont(dance_button, FONT_DEFAULT, Font.PLAIN); + + MiscTools.swingReflectionInvoke("setText", original_link_textfield, MiscTools.extractFirstMegaLinkFromString(MiscTools.extractStringFromClipboardContents(clipboardspy.getContents()))); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + put_label = new javax.swing.JLabel(); + dance_button = new javax.swing.JButton(); + original_link_textfield = new javax.swing.JTextField(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Streamer"); + setResizable(false); + + put_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + put_label.setText("Put your mega/megacrypter link here in order to get a streaming link:"); + put_label.setDoubleBuffered(true); + + dance_button.setBackground(new java.awt.Color(102, 204, 255)); + dance_button.setFont(new java.awt.Font("Dialog", 1, 24)); // NOI18N + dance_button.setForeground(new java.awt.Color(255, 255, 255)); + dance_button.setText("Let's dance, baby"); + dance_button.setDoubleBuffered(true); + dance_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dance_buttonActionPerformed(evt); + } + }); + + original_link_textfield.setFont(new java.awt.Font("Dialog", 0, 14)); // NOI18N + original_link_textfield.setDoubleBuffered(true); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 300, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(put_label) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(original_link_textfield)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(put_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(original_link_textfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(dance_button, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + original_link_textfield.addMouseListener(new ContextMenuMouseListener()); + + pack(); + }// //GEN-END:initComponents + + private void dance_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dance_buttonActionPerformed + + + String link=((String)MiscTools.swingReflectionInvokeAndWaitForReturn("getText", original_link_textfield)).trim(); + + if(link.length() == 0) { + + JOptionPane.showMessageDialog(this, "Please, paste a mega/megacrypter link!"); + + MiscTools.swingReflectionInvoke("setText", original_link_textfield, ""); + + } else { + + try { + + link = CryptTools.decryptMegaDownloaderLink(link); + + } catch (Exception ex) { + Logger.getLogger(StreamerDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + String data; + + link=link.replace("/#!N?", "/#N!"); + + if( MiscTools.findFirstRegex("://mega(\\.co)?\\.nz/#[^fF]", link, 0) != null) + { + data=MiscTools.findFirstRegex("/#(N?!.+)", link, 1); + + _cookLink("http://localhost:1337/video/mega/"+data); + + } else if( (data=MiscTools.findFirstRegex("https?://([^/]+/![^!]+![0-9a-fA-F]+)", link, 1)) != null) { + + _cookLink("http://localhost:1337/video/"+data); + + } else { + + JOptionPane.showMessageDialog(this, "Please, paste a mega/megacrypter link!"); + + MiscTools.swingReflectionInvoke("setText", original_link_textfield, ""); + } + } + }//GEN-LAST:event_dance_buttonActionPerformed + + private void _cookLink(final String streamlink) + { + MiscTools.swingReflectionInvoke("setEnabled", dance_button, false); + MiscTools.swingReflectionInvoke("setEnabled", original_link_textfield, false); + + final StreamerDialog streamer_run = this; + + MainPanel.THREAD_POOL.execute(new Runnable(){ + @Override + public void run() { + + try { + MiscTools.copyTextToClipboard(MiscTools.deflateURL(streamlink)); + } catch (IOException ex) { + Logger.getLogger(StreamerDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + JOptionPane.showMessageDialog(streamer_run, "Streaming link was copied to clipboard!\n(Remember to keep MegaBasterd running in background while playing)"); + + streamer_run.dispose(); + + streamer_run.getParent().dispatchEvent(new WindowEvent(streamer_run, WindowEvent.WINDOW_CLOSING)); + + } + }); + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton dance_button; + private javax.swing.JTextField original_link_textfield; + private javax.swing.JLabel put_label; + // End of variables declaration//GEN-END:variables + + @Override + public void notifyClipboardChange() { + + MiscTools.swingReflectionInvoke("setText", original_link_textfield, MiscTools.extractFirstMegaLinkFromString(MiscTools.extractStringFromClipboardContents(_clipboardspy.getContents()))); } + } diff --git a/src/megabasterd/ThrottledInputStream.java b/src/megabasterd/ThrottledInputStream.java new file mode 100644 index 000000000..2fe008602 --- /dev/null +++ b/src/megabasterd/ThrottledInputStream.java @@ -0,0 +1,220 @@ +package megabasterd; + +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author tonikelope + */ +public final class ThrottledInputStream extends InputStream { + + private final InputStream _rawStream; + + private final StreamThrottlerSupervisor _stream_supervisor; + + private Integer _slice_size; + + private boolean _stream_finish; + + + public ThrottledInputStream(InputStream rawStream, StreamThrottlerSupervisor stream_supervisor) { + + _rawStream = rawStream; + + _stream_supervisor = stream_supervisor; + + _stream_finish = false; + + _slice_size = null; + } + + + @Override + public int read() throws IOException { + + if(_stream_supervisor.getMaxBytesPerSecInput() > 0) { + + if(!_stream_finish) { + + int r; + + throttle(1); + + if(_slice_size != null) { + + r = _rawStream.read(); + + if(r == -1) { + + _stream_finish = true; + } + + return r; + + } else { + + return _rawStream.read(); + } + + } else { + + return -1; + } + + } else { + + return _rawStream.read(); + } + + } + + + @Override + public int read(byte[] b) throws IOException { + + if(_stream_supervisor.getMaxBytesPerSecInput() > 0) { + + if(!_stream_finish) { + + int readLen = 0, readSlice, len=b.length, r=0; + + do { + + throttle(len - readLen); + + if(_slice_size != null) { + + readSlice=0; + + do{ + r = _rawStream.read(b, readLen+readSlice, _slice_size-readSlice ); + + if(r!=-1){ + + readSlice+=r; + + } else { + + _stream_finish = true; + } + + }while(r != -1 && readSlice < _slice_size); + + readLen+=readSlice; + + } else { + + return _rawStream.read(b); + } + + }while( r != -1 && readLen < len ); + + return readLen; + + } else { + + return -1; + } + + } else { + + return _rawStream.read(b); + } + } + + + @Override + public int read(byte[] b, int off, int len) throws IOException { + + if(_stream_supervisor.getMaxBytesPerSecInput() > 0) { + + if(!_stream_finish) { + + int readLen = 0, r=0; + + do { + + throttle(len-readLen); + + if(_slice_size != null) { + + int readSlice=0; + + do{ + r = _rawStream.read(b, off+readSlice+readLen, _slice_size - readSlice); + + if(r!=-1){ + + readSlice+=r; + + } else { + + _stream_finish = true; + } + + }while(r!=-1 && readSlice<_slice_size); + + readLen+=readSlice; + + } else { + + r = _rawStream.read(b, off+readLen, len-readLen); + + if(r!=-1){ + + readLen+=r; + + } else { + + _stream_finish = true; + } + } + + }while( r!=-1 && readLen < len ); + + return readLen; + + } else { + + return -1; + } + + } else { + + return _rawStream.read(b, off, len); + } + + } + + + @Override + public void reset() throws IOException { + + _stream_finish = false; + + _rawStream.reset(); + + } + + + private void throttle(int size) throws IOException { + + _slice_size = null; + + while(_stream_supervisor.getMaxBytesPerSecInput() > 0 && (_slice_size=_stream_supervisor.getInput_slice_queue().poll()) == null) { + + _stream_supervisor.secureWait(); + } + + if(_slice_size != null && size < _slice_size) { + + _stream_supervisor.getInput_slice_queue().add(_slice_size - size); + + _stream_supervisor.secureNotifyAll(); + + _slice_size = size; + } + } + +} diff --git a/src/megabasterd/ThrottledOutputStream.java b/src/megabasterd/ThrottledOutputStream.java new file mode 100644 index 000000000..7b8326a81 --- /dev/null +++ b/src/megabasterd/ThrottledOutputStream.java @@ -0,0 +1,97 @@ +package megabasterd; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author tonikelope + */ +public final class ThrottledOutputStream extends OutputStream { + + private final OutputStream _rawStream; + + private final StreamThrottlerSupervisor _stream_supervisor; + + private Integer slice_size; + + + public ThrottledOutputStream(OutputStream rawStream, StreamThrottlerSupervisor stream_supervisor) { + + _rawStream = rawStream; + + _stream_supervisor = stream_supervisor; + + slice_size = null; + + } + + + @Override + public void write(byte[] b, int off, int len) throws IOException { + + if(_stream_supervisor.getMaxBytesPerSecOutput() > 0) { + + int writeLen=0; + + do { + + throttle(len-writeLen); + + if(slice_size != null) { + + _rawStream.write(b, off+writeLen, slice_size); + + writeLen+=slice_size; + + } else { + + _rawStream.write(b, off+writeLen, len-writeLen); + + writeLen = len; + } + + }while( writeLen < len ); + + } else { + + _rawStream.write(b, off, len); + } + } + + @Override + public void write(int i) throws IOException { + + if(_stream_supervisor.getMaxBytesPerSecOutput() > 0) { + + throttle(1); + + _rawStream.write(i); + + } else { + + _rawStream.write(i); + } + } + + + private void throttle(int size) throws IOException { + + slice_size = null; + + while(_stream_supervisor.getMaxBytesPerSecOutput() > 0 && (slice_size=_stream_supervisor.getOutput_slice_queue().poll()) == null) { + + _stream_supervisor.secureWait(); + } + + if(slice_size != null && size < slice_size) { + + _stream_supervisor.getOutput_slice_queue().add(slice_size - size); + + _stream_supervisor.secureNotifyAll(); + + slice_size = size; + } + } + +} diff --git a/src/megabasterd/Transference.java b/src/megabasterd/Transference.java new file mode 100644 index 000000000..f9d2c08ed --- /dev/null +++ b/src/megabasterd/Transference.java @@ -0,0 +1,52 @@ +package megabasterd; + +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * + * @author tonikelope + */ +public interface Transference { + + int MIN_WORKERS = 1; + int MAX_WORKERS = 10; + int MAX_SIM_TRANSFERENCES=20; + int WORKERS_DEFAULT = 4; + int SIM_TRANSFERENCES_DEFAULT=2; + boolean LIMIT_TRANSFERENCE_SPEED_DEFAULT=false; + int MAX_TRANSFERENCE_SPEED_DEFAULT=1; + + void start(); + + void stop(); + + void pause(); + + void restart(); + + void close(); + + boolean isPaused(); + + boolean isStopped(); + + void checkSlotsAndWorkers(); + + ConcurrentLinkedQueue getPartialProgress(); + + long getProgress(); + + void updateProgress(int reads); + + String getFile_name(); + + long getFile_size(); + + ProgressMeter getProgress_meter(); + + SpeedMeter getSpeed_meter(); + + MainPanel getMain_panel(); + + TransferenceView getView(); +} diff --git a/src/megabasterd/TransferenceManager.java b/src/megabasterd/TransferenceManager.java new file mode 100644 index 000000000..6fff0e453 --- /dev/null +++ b/src/megabasterd/TransferenceManager.java @@ -0,0 +1,206 @@ +package megabasterd; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JPanel; + +/** + * + * @author tonikelope + */ +abstract public class TransferenceManager implements Runnable, SecureNotifiable { + + private final ConcurrentLinkedQueue _transference_provision_queue; + private final ConcurrentLinkedQueue _transference_start_queue; + private final ConcurrentLinkedQueue _transference_remove_queue; + private final ConcurrentLinkedQueue _transference_finished_queue; + private final ConcurrentLinkedQueue _transference_running_list; + private final javax.swing.JPanel _scroll_panel; + private final MainPanel _main_panel; + private final Object _secure_notify_lock; + private boolean _notified=false; + + public TransferenceManager(MainPanel main_panel, javax.swing.JPanel scroll_panel) { + + _main_panel = main_panel; + _scroll_panel = scroll_panel; + _secure_notify_lock = new Object(); + _transference_start_queue = new ConcurrentLinkedQueue(); + _transference_provision_queue = new ConcurrentLinkedQueue(); + _transference_remove_queue = new ConcurrentLinkedQueue(); + _transference_finished_queue = new ConcurrentLinkedQueue(); + _transference_running_list = new ConcurrentLinkedQueue(); + + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(TransferenceManager.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + public MainPanel getMain_panel() { + return _main_panel; + } + + public ConcurrentLinkedQueue getTransference_provision_queue() { + return _transference_provision_queue; + } + + public ConcurrentLinkedQueue getTransference_start_queue() { + return _transference_start_queue; + } + + public ConcurrentLinkedQueue getTransference_remove_queue() { + return _transference_remove_queue; + } + + public ConcurrentLinkedQueue getTransference_finished_queue() { + return _transference_finished_queue; + } + + public ConcurrentLinkedQueue getTransference_running_list() { + return _transference_running_list; + } + + public JPanel getScroll_panel() { + return _scroll_panel; + } + + public void closeAllFinished() + { + _transference_remove_queue.addAll(new ArrayList(_transference_finished_queue)); + + _transference_finished_queue.clear(); + + secureNotify(); + } + + public void closeAllWaiting() + { + _transference_remove_queue.addAll(new ArrayList(_transference_start_queue)); + + _transference_start_queue.clear(); + + secureNotify(); + } + + public void start(Transference transference) { + + _transference_running_list.add(transference); + + _scroll_panel.add((Component)transference.getView(), 0); + + transference.start(); + } + + public void pauseAll() + { + for(Transference transference:_transference_running_list) { + + if(!transference.isPaused()) { + + transference.pause(); + } + } + + secureNotify(); + } + + public void sortTransferenceStartQueue() + { + ArrayList trans_list = new ArrayList(_transference_start_queue); + + trans_list.sort(new Comparator () { + + @Override + public int compare(Transference o1, Transference o2) { + + return o1.getFile_name().compareToIgnoreCase(o2.getFile_name()); + } + }); + + _transference_start_queue.clear(); + + _transference_start_queue.addAll(trans_list); + } + + public void checkButtonsAndMenus(javax.swing.JButton close_all_finished_button, javax.swing.JButton pause_all_button, + javax.swing.MenuElement new_trans_menu, javax.swing.MenuElement clean_all_waiting_trans_menu) { + + if(!_transference_running_list.isEmpty()) { + + boolean show_pause_all = false; + + for(Transference trans:_transference_running_list) { + + if((show_pause_all = !trans.isPaused())) { + + break; + } + } + + MiscTools.swingReflectionInvoke("setVisible", pause_all_button, show_pause_all); + + } else { + + MiscTools.swingReflectionInvoke("setVisible", pause_all_button, false); + } + + MiscTools.swingReflectionInvoke("setEnabled", new_trans_menu, _transference_provision_queue.isEmpty()); + + MiscTools.swingReflectionInvoke("setEnabled", clean_all_waiting_trans_menu, !_transference_start_queue.isEmpty()); + + if(!_transference_finished_queue.isEmpty()) { + + MiscTools.swingReflectionInvoke("setText", close_all_finished_button, "Close all finished ("+_transference_finished_queue.size()+")" ); + + MiscTools.swingReflectionInvoke("setVisible", close_all_finished_button, true); + + } else { + + MiscTools.swingReflectionInvoke("setVisible", close_all_finished_button, false); + } + } + +} diff --git a/src/megabasterd/TransferenceView.java b/src/megabasterd/TransferenceView.java new file mode 100644 index 000000000..185e2a235 --- /dev/null +++ b/src/megabasterd/TransferenceView.java @@ -0,0 +1,19 @@ +package megabasterd; + +/** + * + * @author tonikelope + */ +public interface TransferenceView { + + void pause(); + void stop(); + void resume(); + void updateSpeed(String speed, Boolean visible); + void updateRemainingTime(String rem_time, Boolean visible); + void updateProgressBar(long progress, double bar_rate); + void printStatusNormal(String msg); + void printStatusOK(String msg); + void printStatusError(String msg); + +} diff --git a/src/megabasterd/Upload.java b/src/megabasterd/Upload.java new file mode 100644 index 000000000..44c9ccb4a --- /dev/null +++ b/src/megabasterd/Upload.java @@ -0,0 +1,969 @@ +package megabasterd; + +import java.awt.Color; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.BASE642Bin; +import static megabasterd.MiscTools.Bin2BASE64; +import static megabasterd.MiscTools.HashString; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.formatBytes; +import static megabasterd.MiscTools.i32a2bin; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.truncateText; + +/** + * + * @author tonikelope + */ +public final class Upload implements Transference, Runnable, SecureNotifiable { + + public static final boolean USE_SLOTS_DEFAULT=true; + public static final int WORKERS_DEFAULT = 2; + + private final MainPanel _main_panel; + private UploadView _view; + private String _exit_message; + private String _dir_name; + private volatile boolean _exit; + private final int _slots; + private final Object _secure_notify_lock; + private byte[] _byte_file_key; + private String _fatal_error; + private volatile long _progress; + private byte[] _byte_file_iv; + private final ConcurrentLinkedQueue _rejectedChunkIds; + private long _last_chunk_id_dispatched; + protected final ConcurrentLinkedQueue _partialProgressQueue; + private final ExecutorService _thread_pool; + private volatile int[] _file_meta_mac; + private boolean _finishing_upload; + private String _fid; + private SpeedMeter _speed_meter; + private ProgressMeter _progress_meter; + private boolean _notified; + private String _completion_handle; + private int _paused_workers; + private Double _progress_bar_rate; + private volatile boolean _pause; + protected final ArrayList _chunkworkers; + private long _file_size; + private UploadMACGenerator _mac_generator; + private boolean _create_dir; + private boolean _provision_ok; + private boolean _status_error; + private String _file_link; + private int[] _saved_file_mac; + private final MegaAPI _ma; + private final String _file_name; + private final String _parent_node; + private int[] _ul_key; + private String _ul_url; + private final String _root_node; + private final byte[] _share_key; + private final String _folder_link; + private final boolean _use_slots; + private final boolean _restart; + public Upload(MainPanel main_panel, MegaAPI ma, String filename, String parent_node, int[] ul_key, String ul_url, String root_node, byte[] share_key, String folder_link, boolean use_slots, int slots, boolean restart) { + _saved_file_mac = new int[]{0, 0, 0, 0}; + _notified = false; + _provision_ok = true; + _main_panel = main_panel; + _ma = ma; + _file_name = filename; + _parent_node = parent_node; + _ul_key = ul_key; + _ul_url = ul_url; + _root_node = root_node; + _share_key = share_key; + _folder_link = folder_link; + _use_slots = use_slots; + _slots = slots; + _restart = restart; + _secure_notify_lock = new Object(); + _chunkworkers = new ArrayList(); + _partialProgressQueue = new ConcurrentLinkedQueue(); + _rejectedChunkIds = new ConcurrentLinkedQueue(); + _thread_pool = Executors.newCachedThreadPool(); + _view = null; //Lazy init (getter!) + _speed_meter = null; //Lazy init (getter!) + _progress_meter = null; //Lazy init (getter!) + } + + public String getDir_name() { + return _dir_name; + } + + public boolean isExit() { + return _exit; + } + + public int getSlots() { + return _slots; + } + + public Object getSecure_notify_lock() { + return _secure_notify_lock; + } + + public byte[] getByte_file_key() { + return _byte_file_key; + } + + public String getFatal_error() { + return _fatal_error; + } + + @Override + public long getProgress() { + return _progress; + } + + public byte[] getByte_file_iv() { + return _byte_file_iv; + } + + public ConcurrentLinkedQueue getRejectedChunkIds() { + return _rejectedChunkIds; + } + + public long getLast_chunk_id_dispatched() { + return _last_chunk_id_dispatched; + } + + public ConcurrentLinkedQueue getPartialProgressQueue() { + return _partialProgressQueue; + } + + public ExecutorService getThread_pool() { + return _thread_pool; + } + + public int[] getFile_meta_mac() { + return _file_meta_mac; + } + + public boolean isFinishing_upload() { + return _finishing_upload; + } + + public String getFid() { + return _fid; + } + + public boolean isNotified() { + return _notified; + } + + public String getCompletion_handle() { + return _completion_handle; + } + + public int getPaused_workers() { + return _paused_workers; + } + + public Double getProgress_bar_rate() { + return _progress_bar_rate; + } + + public boolean isPause() { + return _pause; + } + + public ArrayList getChunkworkers() { + return _chunkworkers; + } + + @Override + public long getFile_size() { + return _file_size; + } + + public UploadMACGenerator getMac_generator() { + return _mac_generator; + } + + public boolean isCreate_dir() { + return _create_dir; + } + + public boolean isProvision_ok() { + return _provision_ok; + } + + public boolean isStatus_error() { + return _status_error; + } + + public String getFile_link() { + return _file_link; + } + + public int[] getSaved_file_mac() { + return _saved_file_mac; + } + + public MegaAPI getMa() { + return _ma; + } + + @Override + public String getFile_name() { + return _file_name; + } + + public String getParent_node() { + return _parent_node; + } + + public int[] getUl_key() { + return _ul_key; + } + + public String getUl_url() { + return _ul_url; + } + + public String getRoot_node() { + return _root_node; + } + + public byte[] getShare_key() { + return _share_key; + } + + public String getFolder_link() { + return _folder_link; + } + + public boolean isUse_slots() { + return _use_slots; + } + + public boolean isRestart() { + return _restart; + } + + public void setCompletion_handle(String completion_handle) { + _completion_handle = completion_handle; + } + + public void setFinishing_upload(boolean finishing_upload) { + _finishing_upload = finishing_upload; + } + + public void setFile_meta_mac(int[] file_meta_mac) { + _file_meta_mac = file_meta_mac; + } + + public void setPaused_workers(int paused_workers) { + _paused_workers = paused_workers; + } + + @Override + public ProgressMeter getProgress_meter() { + return _progress_meter == null?(_progress_meter = new ProgressMeter(this)):_progress_meter; + } + + + @Override + public SpeedMeter getSpeed_meter() { + return _speed_meter == null?(_speed_meter = new SpeedMeter(this, getMain_panel().getGlobal_up_speed())):_speed_meter; + } + + @Override + public UploadView getView() { + return _view == null?(_view = new UploadView(this)):_view; + } + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + + public void provisionIt() { + + printStatus("Provisioning upload, please wait..."); + + String exit_msg=null; + + File the_file = new File(_file_name); + + if(!the_file.exists()) { + + _provision_ok=false; + + exit_msg = "ERROR: FILE NOT FOUND -> "+_file_name; + + } else { + + try { + _file_size = the_file.length(); + + File temp_file; + + temp_file = new File("."+HashString("SHA-1", _file_name)); + + if(_ul_key!=null && temp_file.exists() && temp_file.length()>0) { + + FileInputStream fis = new FileInputStream(temp_file); + + byte[] data = new byte[(int)temp_file.length()]; + + fis.read(data); + + String[] fdata = new String(data).split("\\|"); + + _last_chunk_id_dispatched = Long.parseLong(fdata[0]); + + _progress = Long.parseLong(fdata[1]); + + _saved_file_mac = bin2i32a(BASE642Bin(fdata[2])); + + } else if(temp_file.exists()) { + + temp_file.delete(); + } + + if(_ul_key == null || _restart) { + + try { + + _ul_key = _ma.genUploadKey(); + + DBTools.insertUpload(_file_name, _ma.getEmail(), _parent_node, Bin2BASE64(i32a2bin(_ul_key)), _root_node, Bin2BASE64(_share_key), _folder_link); + + } catch (IOException | SQLException ex) { + + _provision_ok=false; + + exit_msg = ex.getMessage(); + } + } + + } catch (Exception ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if(!_provision_ok) { + + getView().hideAllExceptStatus(); + + if(_fatal_error != null) { + + printStatusError(_fatal_error); + + }else if(exit_msg!=null) { + + printStatusError(exit_msg); + } + + swingReflectionInvoke("setVisible", getView().getRestart_button(), true); + + } else { + + printStatus("Waiting to start..."); + + swingReflectionInvoke("setVisible", getView().getFile_name_label(), true); + + swingReflectionInvoke("setText", getView().getFile_name_label(), _file_name); + + swingReflectionInvoke("setText", getView().getFile_name_label(), truncateText(_file_name, 100)); + + swingReflectionInvoke("setToolTipText", getView().getFile_name_label(), _file_name); + + swingReflectionInvoke("setVisible", getView().getFile_size_label(), true); + + swingReflectionInvoke("setText", getView().getFile_size_label(), formatBytes(_file_size)); + } + + swingReflectionInvoke("setVisible", getView().getClose_button(), true); + } + + + @Override + public void start() { + + THREAD_POOL.execute(this); + } + + @Override + public void stop() { + if(!isExit()) { + stopUploader(); + } + } + + @Override + public void pause() { + + if(isPaused()) { + + setPause(false); + + getSpeed_meter().secureNotify(); + + for(ChunkUploader uploader:getChunkworkers()) { + + uploader.secureNotify(); + } + + setPaused_workers(0); + + getView().resume(); + + } else { + + setPause(true); + + getView().pause(); + } + + _main_panel.getUpload_manager().secureNotify(); + } + + @Override + public void restart() { + + Upload new_upload = new Upload(getMain_panel(), getMa(), getFile_name(), getParent_node(), getUl_key(), getUl_url(), getRoot_node(), getShare_key(), getFolder_link(), getMain_panel().isUse_slots_up(), getMain_panel().getDefault_slots_up(), true); + + getMain_panel().getUpload_manager().getTransference_remove_queue().add(this); + + getMain_panel().getUpload_manager().getTransference_provision_queue().add(new_upload); + + getMain_panel().getUpload_manager().secureNotify(); + } + + @Override + public void close() { + + _main_panel.getUpload_manager().getTransference_remove_queue().add(this); + + _main_panel.getUpload_manager().secureNotify(); + } + + @Override + public boolean isPaused() { + return isPause(); + } + + @Override + public boolean isStopped() { + return isExit(); + } + + @Override + public void checkSlotsAndWorkers() { + if(!isExit()) { + + int sl = (int)swingReflectionInvokeAndWaitForReturn("getValue", getView().getSlots_spinner()); + + int cworkers = getChunkworkers().size(); + + if(sl != cworkers) { + + if(sl > cworkers) { + + startSlot(); + + } else { + + swingReflectionInvoke("setEnabled", getView().getSlots_spinner(), false); + + swingReflectionInvoke("setText", getView().getSlot_status_label(), "Removing slot..."); + + stopLastStartedSlot(); + } + } + } + } + + @Override + public ConcurrentLinkedQueue getPartialProgress() { + return getPartialProgressQueue(); + } + + @Override + public void updateProgress(int reads) + { + _progress+=reads; + + getView().updateProgressBar(_progress, _progress_bar_rate); + } + + @Override + public MainPanel getMain_panel() { + return _main_panel; + } + + public synchronized void startSlot() + { + int chunkthiser_id = _chunkworkers.size()+1; + + ChunkUploader c = new ChunkUploader(chunkthiser_id, this); + + _chunkworkers.add(c); + + try { + + _thread_pool.execute(c); + + }catch(java.util.concurrent.RejectedExecutionException e){System.out.println(e.getMessage());} + } + + public void setPause(boolean pause) { + _pause = pause; + } + + public synchronized void stopLastStartedSlot() + { + if(!_chunkworkers.isEmpty()) { + + ChunkUploader chunkuploader = _chunkworkers.remove(_chunkworkers.size()-1); + chunkuploader.setExit(true); + } + } + + public void rejectChunkId(long chunk_id) + { + _rejectedChunkIds.add(chunk_id); + } + + @Override + public void run() { + + System.out.println("Uploader hello!"); + + swingReflectionInvoke("setVisible", getView().getClose_button(), false); + + printStatus("Starting upload, please wait..."); + + if(!_exit) + { + if(_ul_url == null) { + + _ul_url = _ma.initUploadFile(_file_name); + + try { + + DBTools.updateUploadUrl(_file_name, _ma.getEmail(), _ul_url); + } + catch (SQLException ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + } + + int[] file_iv = {_ul_key[4], _ul_key[5], 0, 0}; + + _byte_file_key = i32a2bin(Arrays.copyOfRange(_ul_key, 0, 4)); + + _byte_file_iv = i32a2bin(file_iv); + + if(!_exit) + { + + swingReflectionInvoke("setMinimum", getView().getProgress_pbar(), 0); + swingReflectionInvoke("setMaximum", getView().getProgress_pbar(), Integer.MAX_VALUE); + swingReflectionInvoke("setStringPainted", getView().getProgress_pbar(), true); + + if(_file_size > 0) { + + _progress_bar_rate = Integer.MAX_VALUE/(double)_file_size; + + swingReflectionInvoke("setValue", getView().getProgress_pbar(), 0); + + } else { + + swingReflectionInvoke("setValue", getView().getProgress_pbar(), Integer.MAX_VALUE); + } + + _thread_pool.execute(getProgress_meter()); + + _thread_pool.execute(getSpeed_meter()); + + getMain_panel().getGlobal_up_speed().attachSpeedMeter(getSpeed_meter()); + + getMain_panel().getGlobal_up_speed().secureNotify(); + + _mac_generator = new UploadMACGenerator(this); + + _thread_pool.execute(_mac_generator); + + if(_use_slots) { + + for(int t=1; t <= _slots; t++) + { + ChunkUploader c = new ChunkUploader(t, this); + + _chunkworkers.add(c); + + _thread_pool.execute(c); + } + + swingReflectionInvoke("setVisible", getView().getSlots_label(), true); + + swingReflectionInvoke("setVisible", getView().getSlots_spinner(), true); + + } else { + + ChunkUploader c = new ChunkUploader(1, this); + + _chunkworkers.add(c); + + _thread_pool.execute(c); + + swingReflectionInvoke("setVisible", getView().getSlots_label(), false); + + swingReflectionInvoke("setVisible", getView().getSlots_spinner(), false); + } + + printStatus("Uploading file to mega ("+_ma.getEmail()+") ..."); + + getMain_panel().getUpload_manager().secureNotify(); + + swingReflectionInvoke("setVisible", getView().getPause_button(), true); + + swingReflectionInvoke("setVisible", getView().getProgress_pbar(), true); + + secureWait(); + + System.out.println("Chunkuploaders finished!"); + + getSpeed_meter().setExit(true); + + getSpeed_meter().secureNotify(); + + getProgress_meter().setExit(true); + + getProgress_meter().secureNotify(); + + _thread_pool.shutdown(); + + while(!_thread_pool.isTerminated()) + { + try { + + _thread_pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); + + } catch (InterruptedException ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + } + + System.out.println("Uploader thread pool finished!"); + + getMain_panel().getGlobal_up_speed().detachSpeedMeter(getSpeed_meter()); + + getMain_panel().getGlobal_up_speed().secureNotify(); + + swingReflectionInvoke("setVisible", getView().getSpeed_label(), false); + swingReflectionInvoke("setVisible", getView().getRemtime_label(), false); + + swingReflectionInvoke("setVisible", getView().getPause_button(), false); + swingReflectionInvoke("setVisible", getView().getStop_button(), false); + + swingReflectionInvoke("setVisible", getView().getSlots_label(), false); + swingReflectionInvoke("setVisible", getView().getSlots_spinner(), false); + + getMain_panel().getUpload_manager().secureNotify(); + + if(!_exit) { + + if(_completion_handle != null) { + + File f = new File(_file_name); + + HashMap upload_res=null; + + int[] ul_key = _ul_key; + + int[] node_key = {ul_key[0] ^ ul_key[4], ul_key[1] ^ ul_key[5], ul_key[2] ^ _file_meta_mac[0], ul_key[3] ^ _file_meta_mac[1], ul_key[4], ul_key[5], _file_meta_mac[0], _file_meta_mac[1]}; + + upload_res = _ma.finishUploadFile(f.getName(), ul_key, node_key, _file_meta_mac, _completion_handle, _parent_node, i32a2bin(_ma.getMaster_key()), _root_node, _share_key); + + System.out.println(upload_res); + + List files = (List)upload_res.get("f"); + + _fid = (String)((Map)files.get(0)).get("h"); + + _exit_message = "File successfully uploaded! ("+_ma.getEmail()+")"; + + try { + + _file_link = _ma.getPublicFileLink(_fid, i32a2bin(node_key)); + + swingReflectionInvoke("setEnabled", getView().getFile_link_button(), true); + + } catch (Exception ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + + printStatusOK(_exit_message); + + } else { + + getView().hideAllExceptStatus(); + + _exit_message = "Upload failed!"; + + printStatusError(_exit_message); + + _status_error = true; + } + + } else if(_fatal_error != null) { + + getView().hideAllExceptStatus(); + + printStatusError(_fatal_error); + + _status_error = true; + + } else { + + getView().hideAllExceptStatus(); + + _exit_message = "Upload CANCELED!"; + + printStatusError(_exit_message); + + _status_error = true; + } + + } + else if(_fatal_error != null) + { + getView().hideAllExceptStatus(); + + printStatusError(_fatal_error); + + _status_error = true; + } + else + { + getView().hideAllExceptStatus(); + + _exit_message = "Upload CANCELED!"; + + printStatusError(_exit_message); + + _status_error = true; + + + } + + } + else if(_fatal_error != null) + { + getView().hideAllExceptStatus(); + + _exit_message = _fatal_error; + + printStatusError(_fatal_error); + + _status_error = true; + } + else + { + getView().hideAllExceptStatus(); + + _exit_message = "Upload CANCELED!"; + + printStatusError(_exit_message); + + _status_error = true; + + } + + if(!_exit) { + + try { + DBTools.deleteUpload(_file_name, _ma.getEmail()); + } catch (SQLException ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + + getMain_panel().getUpload_manager().getTransference_running_list().remove(this); + + getMain_panel().getUpload_manager().getTransference_finished_queue().add(this); + + getMain_panel().getView().jPanel_scroll_up.remove(getView()); + + getMain_panel().getView().jPanel_scroll_up.add(getView()); + + getMain_panel().getUpload_manager().secureNotify(); + } + + swingReflectionInvoke("setVisible", getView().getClose_button(), true); + + if(_status_error) { + swingReflectionInvoke("setVisible", getView().getRestart_button(), true); + } + + System.out.println("Uploader BYE BYE"); + } + + public synchronized boolean chunkUploadersRunning() + { + return !_chunkworkers.isEmpty(); + } + + public synchronized void pause_worker() { + + if(++_paused_workers >= _chunkworkers.size() && !_exit) { + + printStatus("Upload paused!"); + swingReflectionInvoke("setText", getView().getPause_button(), "RESUME UPLOAD"); + swingReflectionInvoke("setEnabled", getView().getPause_button(), true); + } + } + + public synchronized void stopThisSlot(ChunkUploader chunkuploader) + { + if(_chunkworkers.remove(chunkuploader)) + { + swingReflectionInvokeAndWait("setValue", getView().getSlots_spinner(), (int)swingReflectionInvokeAndWaitForReturn("getValue", getView().getSlots_spinner())-1); + + if(!_exit && _pause && _paused_workers == _chunkworkers.size()) { + + printStatus("Upload paused!"); + swingReflectionInvoke("setText", getView().getPause_button(), "RESUME UPLOAD"); + swingReflectionInvoke("setEnabled", getView().getPause_button(), true); + } + } + } + + public synchronized void emergencyStopUploader(String reason) + { + if(_fatal_error == null) + { + _fatal_error = reason!=null?reason:"FATAL ERROR!"; + + stopUploader(); + } + } + + public synchronized long nextChunkId() + { + Long next_id; + + if((next_id=_rejectedChunkIds.poll()) != null) { + return next_id; + } + else { + return ++_last_chunk_id_dispatched; + } + } + + public void setExit(boolean exit) { + _exit = exit; + } + + public synchronized void stopUploader() + { + if(!_exit) + { + setExit(true); + + try { + DBTools.deleteUpload(_file_name, _ma.getEmail()); + } catch (SQLException ex) { + Logger.getLogger(Upload.class.getName()).log(Level.SEVERE, null, ex); + } + + getMain_panel().getUpload_manager().getTransference_running_list().remove(this); + + getMain_panel().getUpload_manager().getTransference_finished_queue().add(this); + + _main_panel.getView().jPanel_scroll_up.remove(getView()); + + _main_panel.getView().jPanel_scroll_up.add(getView()); + + getMain_panel().getUpload_manager().secureNotify(); + + getView().stop(); + + for(ChunkUploader uploader:_chunkworkers) { + + uploader.secureNotify(); + } + } + } + + + protected void printStatusError(String message) + { + swingReflectionInvoke("setForeground", getView().getStatus_label(), Color.red); + swingReflectionInvoke("setText", getView().getStatus_label(), message); + } + + protected void printStatusOK(String message) + { + swingReflectionInvoke("setForeground", getView().getStatus_label(), new Color(0,128,0)); + swingReflectionInvoke("setText", getView().getStatus_label(), message); + } + + protected void printStatus(String message) + { + swingReflectionInvoke("setForeground", getView().getStatus_label(), Color.BLACK); + swingReflectionInvoke("setText", getView().getStatus_label(), message); + } + + + +} diff --git a/src/megabasterd/UploadMACGenerator.java b/src/megabasterd/UploadMACGenerator.java new file mode 100644 index 000000000..2aff781b3 --- /dev/null +++ b/src/megabasterd/UploadMACGenerator.java @@ -0,0 +1,216 @@ +package megabasterd; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import static megabasterd.MiscTools.Bin2BASE64; +import static megabasterd.MiscTools.HashString; +import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.i32a2bin; + +/** + * + * @author tonikelope + */ +public final class UploadMACGenerator implements Runnable, SecureNotifiable { + + private long _last_chunk_id_read; + private final ConcurrentHashMap _chunk_queue; + private final Upload _upload; + private final Object _secure_notify_lock; + private boolean _notified; + private volatile boolean _exit; + private long _bytes_read; + public UploadMACGenerator(Upload upload) { + _secure_notify_lock = new Object(); + _notified = false; + _upload = upload; + _chunk_queue = new ConcurrentHashMap(); + _bytes_read = _upload.getProgress(); + _last_chunk_id_read = _upload.getLast_chunk_id_dispatched(); + _exit=false; + } + + + @Override + public void secureNotify() + { + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } + } + + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + Logger.getLogger(UploadMACGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _notified = false; + } + } + + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + + public long getLast_chunk_id_read() { + return _last_chunk_id_read; + } + + public ConcurrentHashMap getChunk_queue() { + return _chunk_queue; + } + + public Upload getUpload() { + return _upload; + } + + public long getBytes_read() { + return _bytes_read; + } + + public boolean isExit() { + return _exit; + } + + public void setExit(boolean exit) { + _exit = exit; + } + + + @Override + public void run() { + + try{ + + File temp_file = new File("."+HashString("SHA-1", _upload.getFile_name())); + + FileOutputStream temp_file_out; + + Chunk chunk; + int[] file_iv = bin2i32a(_upload.getByte_file_iv()), int_block, file_mac = _upload.getSaved_file_mac(), mac_iv = CryptTools.AES_ZERO_IV_I32A; + int reads; + byte[] byte_block = new byte[16]; + String temp_file_data = ""; + boolean new_chunk=false; + + while(!_exit && (!_upload.isStopped() || _upload.chunkUploadersRunning()) && (_bytes_read < _upload.getFile_size() || (_upload.getFile_size() == 0 && _last_chunk_id_read < 1))) + { + while(_chunk_queue.containsKey(_last_chunk_id_read+1)) + { + chunk = _chunk_queue.get(_last_chunk_id_read+1); + + try + { + int[] chunk_mac = {file_iv[0], file_iv[1], file_iv[0], file_iv[1]}; + + InputStream chunk_is = chunk.getInputStream(); + + while( (reads=chunk_is.read(byte_block)) != -1 ) + { + if(reads "+(String.valueOf(_last_chunk_id_read)+"|"+String.valueOf(_bytes_read)+"|"+Bin2BASE64(i32a2bin(file_mac)))); + + temp_file_out = new FileOutputStream(temp_file); + + temp_file_out.write(temp_file_data.getBytes()); + + temp_file_out.close(); + + new_chunk = false; + } + + if(!_exit && (!_upload.isStopped() || _upload.chunkUploadersRunning()) && (_bytes_read < _upload.getFile_size() || (_upload.getFile_size() == 0 && _last_chunk_id_read < 1))) + { + System.out.println("METAMAC wait..."); + secureWait(); + } + } + + if(_bytes_read == _upload.getFile_size()) { + + int[] meta_mac={file_mac[0]^file_mac[1], file_mac[2]^file_mac[3]}; + + _upload.setFile_meta_mac(meta_mac); + } + + temp_file.delete(); + + _upload.secureNotify(); + + System.out.println("MAC GENERATOR BYE BYE..."); + + } catch (Exception ex) { + Logger.getLogger(UploadMACGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + + } + +} diff --git a/src/megabasterd/UploadManager.java b/src/megabasterd/UploadManager.java new file mode 100644 index 000000000..fee65ca70 --- /dev/null +++ b/src/megabasterd/UploadManager.java @@ -0,0 +1,168 @@ +package megabasterd; + +import java.awt.Component; +import java.io.File; +import java.sql.SQLException; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Logger.getLogger; +import static megabasterd.DBTools.deleteUpload; +import static megabasterd.MiscTools.HashString; +import static megabasterd.MiscTools.swingReflectionInvoke; + + + +/** + * + * @author tonikelope + */ +public final class UploadManager extends TransferenceManager { + + public UploadManager(MainPanel main_panel) { + + super(main_panel, main_panel.getView().jPanel_scroll_up); + } + + public void provision(Upload upload) + { + getScroll_panel().add(upload.getView()); + + upload.provisionIt(); + + if(upload.isProvision_ok()) { + + getTransference_start_queue().add(upload); + + if(getTransference_provision_queue().isEmpty()) { + + sortTransferenceStartQueue(); + + for(Transference up:getTransference_start_queue()) { + + getScroll_panel().remove((Component)up.getView()); + getScroll_panel().add((Component)up.getView()); + } + + for(Transference up:getTransference_finished_queue()) { + + getScroll_panel().remove((Component)up.getView()); + getScroll_panel().add((Component)up.getView()); + } + } + } else { + + getTransference_finished_queue().add(upload); + } + + if(getTransference_provision_queue().isEmpty()) { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_up_label(), ""); + + } else { + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_up_label(), getTransference_provision_queue().size() + " uploads waiting for provision..."); + } + + } + + + public void remove(Upload upload) { + + getScroll_panel().remove(upload.getView()); + + getTransference_start_queue().remove(upload); + + getTransference_running_list().remove(upload); + + getTransference_finished_queue().remove(upload); + + if(upload.isProvision_ok()) { + + try { + deleteUpload(upload.getFile_name(), upload.getMa().getEmail()); + } catch (SQLException ex) { + getLogger(UploadManager.class.getName()).log(SEVERE, null, ex); + } + + try { + + File temp_file = new File("."+HashString("SHA-1", upload.getFile_name())); + + if(temp_file.exists()) { + + temp_file.delete(); + } + + } catch (Exception ex) { + getLogger(UploadManager.class.getName()).log(SEVERE, null, ex); + } + } + + if(!getTransference_remove_queue().isEmpty()) { + + swingReflectionInvoke("setText", upload.getMain_panel().getView().getStatus_up_label(), "Removing "+getTransference_remove_queue().size()+" uploads, please wait..."); + + } else { + + swingReflectionInvoke("setText", upload.getMain_panel().getView().getStatus_up_label(), ""); + } + } + + + @Override + public void run() { + + while(true) + { + if(!getTransference_provision_queue().isEmpty()) + { + swingReflectionInvoke("setEnabled", getMain_panel().getView().getNew_upload_menu(), false); + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_up_label(), getTransference_provision_queue().size() + " uploads waiting for provision..."); + + while(!getTransference_provision_queue().isEmpty()) + { + Upload upload = (Upload)getTransference_provision_queue().poll(); + + if(upload != null) { + + provision(upload); + } + } + } + + if(!getTransference_remove_queue().isEmpty()){ + + swingReflectionInvoke("setEnabled", getMain_panel().getView().getNew_upload_menu(), false); + + swingReflectionInvoke("setText", getMain_panel().getView().getStatus_up_label(), "Removing "+getTransference_remove_queue().size()+" uploads, please wait..."); + + while(!getTransference_remove_queue().isEmpty()) { + + Upload upload = (Upload)getTransference_remove_queue().poll(); + + if(upload != null) { + + remove(upload); + } + } + } + + while(!getTransference_start_queue().isEmpty() && getTransference_running_list().size() < getMain_panel().getMax_ul()) { + + Upload upload = (Upload)getTransference_start_queue().poll(); + + if(upload != null) { + + start(upload); + } + } + + checkButtonsAndMenus(getMain_panel().getView().getClose_all_finished_up(), getMain_panel().getView().getPause_all_up(), getMain_panel().getView().getNew_upload_menu(), getMain_panel().getView().getClean_all_up_menu()); + + secureWait(); + } + + } + + +} \ No newline at end of file diff --git a/src/megabasterd/UploadView.form b/src/megabasterd/UploadView.form new file mode 100644 index 000000000..510d04b60 --- /dev/null +++ b/src/megabasterd/UploadView.form @@ -0,0 +1,292 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/UploadView.java b/src/megabasterd/UploadView.java new file mode 100644 index 000000000..d9e90fe4c --- /dev/null +++ b/src/megabasterd/UploadView.java @@ -0,0 +1,526 @@ +package megabasterd; + +import java.awt.Color; +import static java.awt.Font.BOLD; +import static java.awt.Font.PLAIN; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JProgressBar; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.copyTextToClipboard; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; +import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; +import static megabasterd.MiscTools.updateFont; +import static megabasterd.Transference.MAX_WORKERS; +import static megabasterd.Transference.MIN_WORKERS; + +/** + * + * @author tonikelope + */ +public final class UploadView extends javax.swing.JPanel implements TransferenceView { + + private final Upload _upload; + + public JLabel getSlot_status_label() { + return slot_status_label; + } + + public JSpinner getSlots_spinner() { + return slots_spinner; + } + + public JLabel getFile_name_label() { + return file_name_label; + } + + public JLabel getFile_size_label() { + return file_size_label; + } + + public JProgressBar getProgress_pbar() { + return progress_pbar; + } + + public JButton getRestart_button() { + return restart_button; + } + + public JButton getClose_button() { + return close_button; + } + + public JButton getPause_button() { + return pause_button; + } + + public JLabel getSlots_label() { + return slots_label; + } + + public JLabel getSpeed_label() { + return speed_label; + } + + public JLabel getRemtime_label() { + return remtime_label; + } + + public JButton getStop_button() { + return stop_button; + } + + public JButton getFile_link_button() { + return file_link_button; + } + + public JLabel getStatus_label() { + return status_label; + } + + + + + public UploadView(Upload upload){ + + initComponents(); + + _upload = upload; + + updateFont(status_label, FONT_DEFAULT, BOLD); + updateFont(remtime_label, FONT_DEFAULT, PLAIN); + updateFont(speed_label, FONT_DEFAULT, BOLD); + updateFont(progress_pbar, FONT_DEFAULT, PLAIN); + updateFont(slots_label, FONT_DEFAULT, BOLD); + updateFont(slots_spinner, FONT_DEFAULT, PLAIN); + updateFont(pause_button, FONT_DEFAULT, BOLD); + updateFont(stop_button, FONT_DEFAULT, BOLD); + updateFont(folder_link_button, FONT_DEFAULT, PLAIN); + updateFont(file_link_button, FONT_DEFAULT, PLAIN); + updateFont(file_name_label, FONT_DEFAULT, PLAIN); + updateFont(file_size_label, FONT_DEFAULT, BOLD); + updateFont(close_button, FONT_DEFAULT, PLAIN); + updateFont(restart_button, FONT_DEFAULT, PLAIN); + updateFont(slot_status_label, FONT_DEFAULT, PLAIN); + + swingReflectionInvokeAndWait("setModel", slots_spinner, new SpinnerNumberModel(_upload.getMain_panel().getDefault_slots_up(), MIN_WORKERS, MAX_WORKERS, 1)); + swingReflectionInvoke("setEditable", swingReflectionInvokeAndWaitForReturn("getTextField", swingReflectionInvokeAndWaitForReturn("getEditor", slots_spinner)), false); + swingReflectionInvoke("setVisible", slots_spinner, false); + swingReflectionInvoke("setVisible", slots_label, false); + swingReflectionInvoke("setVisible", pause_button, false); + swingReflectionInvoke("setVisible", stop_button, false); + swingReflectionInvoke("setForeground", speed_label, new Color(0,128,255)); + swingReflectionInvoke("setVisible", speed_label, false); + swingReflectionInvoke("setVisible", remtime_label, false); + swingReflectionInvoke("setVisible", progress_pbar, false); + swingReflectionInvoke("setVisible", file_name_label, false); + swingReflectionInvoke("setVisible", close_button, false); + swingReflectionInvoke("setVisible", restart_button, false); + swingReflectionInvoke("setVisible", file_size_label, false); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + status_label = new javax.swing.JLabel(); + slots_label = new javax.swing.JLabel(); + slots_spinner = new javax.swing.JSpinner(); + speed_label = new javax.swing.JLabel(); + remtime_label = new javax.swing.JLabel(); + progress_pbar = new javax.swing.JProgressBar(); + pause_button = new javax.swing.JButton(); + stop_button = new javax.swing.JButton(); + file_name_label = new javax.swing.JLabel(); + close_button = new javax.swing.JButton(); + restart_button = new javax.swing.JButton(); + file_size_label = new javax.swing.JLabel(); + slot_status_label = new javax.swing.JLabel(); + folder_link_button = new javax.swing.JButton(); + file_link_button = new javax.swing.JButton(); + + setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 204, 255), 4, true)); + + status_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + status_label.setText("status"); + status_label.setDoubleBuffered(true); + + slots_label.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + slots_label.setText("Slots"); + slots_label.setDoubleBuffered(true); + + slots_spinner.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + slots_spinner.setToolTipText("Slots"); + slots_spinner.setDoubleBuffered(true); + slots_spinner.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + slots_spinnerStateChanged(evt); + } + }); + + speed_label.setFont(new java.awt.Font("Verdana", 3, 26)); // NOI18N + speed_label.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + speed_label.setText("speed"); + speed_label.setDoubleBuffered(true); + + remtime_label.setFont(new java.awt.Font("Verdana", 1, 18)); // NOI18N + remtime_label.setText("remaining_time"); + remtime_label.setDoubleBuffered(true); + + progress_pbar.setFont(new java.awt.Font("Verdana", 1, 18)); // NOI18N + progress_pbar.setDoubleBuffered(true); + + pause_button.setBackground(new java.awt.Color(255, 153, 0)); + pause_button.setFont(new java.awt.Font("Verdana", 1, 16)); // NOI18N + pause_button.setForeground(java.awt.Color.white); + pause_button.setText("PAUSE UPLOAD"); + pause_button.setDoubleBuffered(true); + pause_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pause_buttonActionPerformed(evt); + } + }); + + stop_button.setBackground(new java.awt.Color(255, 0, 0)); + stop_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + stop_button.setForeground(java.awt.Color.white); + stop_button.setText("CANCEL UPLOAD"); + stop_button.setDoubleBuffered(true); + stop_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + stop_buttonActionPerformed(evt); + } + }); + + file_name_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + file_name_label.setForeground(new java.awt.Color(51, 51, 255)); + file_name_label.setText("file_name"); + file_name_label.setDoubleBuffered(true); + + close_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + close_button.setText("Close"); + close_button.setDoubleBuffered(true); + close_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + close_buttonActionPerformed(evt); + } + }); + + restart_button.setBackground(new java.awt.Color(51, 51, 255)); + restart_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + restart_button.setForeground(new java.awt.Color(255, 255, 255)); + restart_button.setText("Restart"); + restart_button.setDoubleBuffered(true); + restart_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + restart_buttonActionPerformed(evt); + } + }); + + file_size_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + file_size_label.setForeground(new java.awt.Color(51, 51, 255)); + file_size_label.setText("file_size"); + file_size_label.setDoubleBuffered(true); + + slot_status_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + slot_status_label.setDoubleBuffered(true); + + folder_link_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + folder_link_button.setText("Copy folder link"); + folder_link_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + folder_link_buttonActionPerformed(evt); + } + }); + + file_link_button.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + file_link_button.setText("Copy file link"); + file_link_button.setEnabled(false); + file_link_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + file_link_buttonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(file_size_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(folder_link_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(file_link_button)) + .addGroup(layout.createSequentialGroup() + .addComponent(remtime_label) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(progress_pbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(speed_label, javax.swing.GroupLayout.PREFERRED_SIZE, 340, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 106, Short.MAX_VALUE) + .addComponent(pause_button)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(close_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(restart_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(stop_button)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(status_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(slots_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(slots_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(file_name_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(slot_status_label))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(slots_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(slots_label) + .addComponent(status_label)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(file_name_label) + .addComponent(slot_status_label)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(file_size_label) + .addComponent(folder_link_button) + .addComponent(file_link_button)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(remtime_label) + .addGap(6, 6, 6) + .addComponent(progress_pbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(speed_label) + .addComponent(pause_button)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(stop_button) + .addComponent(close_button) + .addComponent(restart_button)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void slots_spinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_slots_spinnerStateChanged + + THREAD_POOL.execute(new Runnable(){ + + @Override + public void run() { + + _upload.checkSlotsAndWorkers(); + } + }); + }//GEN-LAST:event_slots_spinnerStateChanged + + private void close_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_close_buttonActionPerformed + _upload.close(); + }//GEN-LAST:event_close_buttonActionPerformed + + private void restart_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restart_buttonActionPerformed + + _upload.restart(); + }//GEN-LAST:event_restart_buttonActionPerformed + + private void stop_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stop_buttonActionPerformed + + _upload.stop(); + }//GEN-LAST:event_stop_buttonActionPerformed + + private void pause_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pause_buttonActionPerformed + + _upload.pause(); + + }//GEN-LAST:event_pause_buttonActionPerformed + + public void hideAllExceptStatus() + { + swingReflectionInvoke("setVisible", speed_label, false); + swingReflectionInvoke("setVisible", remtime_label, false); + swingReflectionInvoke("setVisible", slots_spinner, false); + swingReflectionInvoke("setVisible", slots_label, false); + swingReflectionInvoke("setVisible", slot_status_label, false); + swingReflectionInvoke("setVisible", pause_button, false); + swingReflectionInvoke("setVisible", stop_button, false); + swingReflectionInvoke("setVisible", progress_pbar, false); + + } + + private void folder_link_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_folder_link_buttonActionPerformed + + swingReflectionInvoke("setEnabled", folder_link_button, false); + + swingReflectionInvoke("setText", folder_link_button, "Please wait..."); + + copyTextToClipboard(_upload.getFolder_link()); + + swingReflectionInvoke("setText", folder_link_button, "Copy folder link"); + + JOptionPane.showMessageDialog(_upload.getMain_panel().getView(), "MEGA folder link was copied to clipboard!"); + + swingReflectionInvoke("setEnabled", folder_link_button, true); + }//GEN-LAST:event_folder_link_buttonActionPerformed + + private void file_link_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_file_link_buttonActionPerformed + + swingReflectionInvoke("setEnabled", file_link_button, false); + + swingReflectionInvoke("setText", file_link_button, "Please wait..."); + + copyTextToClipboard(_upload.getFile_link()); + + swingReflectionInvoke("setText", file_link_button, "Copy file link"); + + JOptionPane.showMessageDialog(_upload.getMain_panel().getView(), "MEGA file link was copied to clipboard!"); + + swingReflectionInvoke("setEnabled", file_link_button, true); + + }//GEN-LAST:event_file_link_buttonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton close_button; + private javax.swing.JButton file_link_button; + private javax.swing.JLabel file_name_label; + private javax.swing.JLabel file_size_label; + private javax.swing.JButton folder_link_button; + private javax.swing.JButton pause_button; + private javax.swing.JProgressBar progress_pbar; + private javax.swing.JLabel remtime_label; + private javax.swing.JButton restart_button; + private javax.swing.JLabel slot_status_label; + private javax.swing.JLabel slots_label; + private javax.swing.JSpinner slots_spinner; + private javax.swing.JLabel speed_label; + private javax.swing.JLabel status_label; + private javax.swing.JButton stop_button; + // End of variables declaration//GEN-END:variables + + + @Override + public void pause() { + + printStatusNormal("Pausing upload ..."); + + swingReflectionInvoke("setEnabled", pause_button, false); + swingReflectionInvoke("setEnabled", speed_label, false); + swingReflectionInvoke("setEnabled", slots_label, false); + swingReflectionInvoke("setEnabled", slots_spinner, false); + + swingReflectionInvoke("setVisible", stop_button, true); + + } + + @Override + public void stop() { + + printStatusNormal("Stopping upload safely, please wait..."); + + swingReflectionInvoke("setEnabled", speed_label, false); + swingReflectionInvoke("setEnabled", pause_button, false); + swingReflectionInvoke("setEnabled", stop_button, false); + swingReflectionInvoke("setEnabled", slots_label, false); + swingReflectionInvoke("setEnabled", slots_spinner, false); + + } + + @Override + public void resume() { + + printStatusNormal("Uploading file to mega ..."); + + swingReflectionInvoke("setEnabled", pause_button, false); + + swingReflectionInvoke("setEnabled", speed_label, true); + swingReflectionInvoke("setEnabled", slots_label, true); + swingReflectionInvoke("setEnabled", slots_spinner, true); + + swingReflectionInvoke("setVisible", stop_button, false); + + swingReflectionInvoke("setEnabled", pause_button, true); + + swingReflectionInvoke("setText", pause_button, "PAUSE UPLOAD"); + + swingReflectionInvoke("setVisible", _upload.getMain_panel().getView().getPause_all_up(), true); + } + + @Override + public void updateSpeed(String sp, Boolean visible) { + + if(sp != null) { + + swingReflectionInvoke("setText", speed_label, sp); + } + + if(visible != null) { + + swingReflectionInvoke("setVisible", speed_label, visible); + } + } + + @Override + public void updateRemainingTime(String remtime, Boolean visible) { + + if(remtime != null) { + + swingReflectionInvoke("setText", remtime_label, remtime); + } + + if(visible != null) { + + swingReflectionInvoke("setVisible", remtime_label, visible); + } + } + + @Override + public void updateProgressBar(long progress, double bar_rate) { + + swingReflectionInvoke("setValue", this.progress_pbar, (int)Math.ceil(bar_rate*progress)); + } + + @Override + public void printStatusNormal(String msg) { + swingReflectionInvoke("setForeground", status_label, Color.BLACK); + swingReflectionInvoke("setText", status_label, msg); + } + + @Override + public void printStatusOK(String msg) { + swingReflectionInvoke("setForeground", status_label, new Color(0,128,0)); + swingReflectionInvoke("setText", status_label, msg); + } + + @Override + public void printStatusError(String msg) { + + swingReflectionInvoke("setForeground", status_label, Color.red); + swingReflectionInvoke("setText", status_label, msg); + } + + +} diff --git a/src/megabasterd/dot_com.jpg b/src/megabasterd/dot_com.jpg new file mode 100644 index 000000000..b816b874d Binary files /dev/null and b/src/megabasterd/dot_com.jpg differ diff --git a/src/megabasterd/made_in_spain.jpg b/src/megabasterd/made_in_spain.jpg new file mode 100644 index 000000000..a805b55ab Binary files /dev/null and b/src/megabasterd/made_in_spain.jpg differ diff --git a/src/megabasterd/mbasterd.png b/src/megabasterd/mbasterd.png new file mode 100644 index 000000000..7b2a99c87 Binary files /dev/null and b/src/megabasterd/mbasterd.png differ diff --git a/src/megabasterd/mega_crypter.png b/src/megabasterd/mega_crypter.png new file mode 100644 index 000000000..579980aae Binary files /dev/null and b/src/megabasterd/mega_crypter.png differ diff --git a/src/megabasterd/pica_roja.ico b/src/megabasterd/pica_roja.ico new file mode 100644 index 000000000..5a991707b Binary files /dev/null and b/src/megabasterd/pica_roja.ico differ diff --git a/src/megabasterd/pica_roja.png b/src/megabasterd/pica_roja.png new file mode 100644 index 000000000..2af053051 Binary files /dev/null and b/src/megabasterd/pica_roja.png differ diff --git a/src/megabasterd/pica_roja_big.png b/src/megabasterd/pica_roja_big.png new file mode 100644 index 000000000..1f382b827 Binary files /dev/null and b/src/megabasterd/pica_roja_big.png differ