├── build.xml ├── faq.txt ├── plugin.xml ├── readme.txt └── src ├── Base64.java ├── CommandUpdate.java ├── Console.java ├── CookieManager.java ├── Download.java ├── DownloadHttpSession.java ├── FNTemplate.java ├── FileListDialog.java ├── FileListItem.java ├── HtmlPage.java ├── HtmlPageLoginForm.java ├── IDItem.java ├── IDManager.java ├── LocalCopyPane.java ├── LocalCopyVer.java ├── PolicyItem.java ├── PolicyManager.java └── ProgressDialog.java /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /faq.txt: -------------------------------------------------------------------------------- 1 | Q: Where are the downloaded files stored? 2 | ---------------------------------------------------------------------------------------------------- 3 | A: The plugin saves the files in the main file directory that can be set in the preferences of JabRef. 4 | If a per-database specific file directory is set, it is preferred over the global setting. 5 | The filename consists of the BibTeX key plus the extension ".pdf". 6 | 7 | 8 | 9 | Q: I need to use a proxy server to obtain access to an online journal pdf file. How do 10 | I configure JabRef to use a proxy server? 11 | ---------------------------------------------------------------------------------------------------- 12 | A: If you want to use, e.g., a SOCKS proxy, run JabRef with the following command line 13 | 14 | java -DsocksProxyHost=host -DsocksProxyPort=port -jar JabRef-X.jar , 15 | 16 | where host and port give the hostname and port number of your SOCKS proxy and X 17 | is the JabRef version you are using. 18 | 19 | A general documentation that also includes HTTP proxies (and how to specify a username/password) 20 | can be found here: 21 | 22 | http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html . 23 | 24 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 19 | 20 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 2 | localcopy - Plugin 3 | --------------------------------------- 4 | 5 | Download a local copy of an article from the preprint server arXiv and 6 | from journals linked by the DOI system. 7 | 8 | Author 9 | * Christoph Lehner (clehner // users.sourceforge.net) 10 | 11 | Build instructions: 12 | * Run build.xml with target jars (default) 13 | 14 | Changelog: 15 | 16 | * 2009-03-23 - V0.1 - Initial release. 17 | 18 | * 2009-03-24 - V0.2 - Respond to cancelation during download, show 19 | progress of individual file downloads, added 20 | backport of CookieManager, CookieStore, 21 | CookiePolicy and InMemoryCookieStore for 22 | compatibility with systems that only have JDK 23 | 1.5. 24 | 25 | * 2009-03-25 - V0.3 - Implemented a lightweight mechanism for 26 | handling cookies compatible with JDK 1.5, 27 | fixed a bug when renaming files on Windows 28 | machines. 29 | 30 | * 2009-03-26 - V0.4 - Added support for url field, fixed bug when 31 | redirection uses relative paths, support for 32 | pdf links that span over multiple lines, 33 | support for direct links to pdf files. 34 | 35 | * 2009-03-26 - V0.5 - Better heuristics for finding pdf links on a 36 | page. Several minor enhancements. 37 | 38 | * 2009-03-29 - V0.6 - Better robustness against incorrectly formed 39 | HTML and DOI links, minor changes in UI. 40 | 41 | * 2009-04-01 - V0.7 - Bug fix for tag without href, 42 | added warning before an external file is 43 | overwritten, added support for 44 | database-specific file directories, use main 45 | file directory by default. 46 | 47 | * 2009-04-05 - V0.8 - Implemented "update fields" button to check 48 | for updates on SPIRES. Allows for automatic 49 | download of journal pdf if a new DOI/URL link 50 | is found. 51 | 52 | * 2009-04-25 - V0.9 - Strip DOI: prefix in doi field if present. 53 | 54 | * 2009-06-01 - V1.0 - Implemented default choice for URL/DOI 55 | selection, added HTTPS support, implemented 56 | authentication system for HTTP-Auth and login 57 | forms, implemented policy system, implemented 58 | new cookie management system, major redesign 59 | of code layout. 60 | 61 | * 2009-06-08 - V1.1 - Bug fix in Cookie.appliesTo, allow multiple 62 | redirects of pdf links, use default policies 63 | as default choice in policy dialogs if 64 | available, changed package to localcopy, bug 65 | fix in HtmlPage.getPDFLinks handling malformed 66 | URLs (bug introduced in V1.0). 67 | 68 | * 2009-06-15 - V1.2 - Bug fix in Download.downloadFile. 69 | 70 | * 2009-07-10 - V1.3 - New feature: Select fields you want to update. 71 | 72 | * 2009-09-21 - V1.4 - Autogenerate BibTeX key before downloading if 73 | key is empty. Select alternative filename if 74 | file existed. 75 | 76 | * 2009-09-30 - V1.5 - Check if filename is supported by filesystem. 77 | If not offer to select new filename without 78 | new download. Automatically replace ':' with 79 | '_' in filename if on a windows platform. In 80 | future releases there may be a more 81 | sophisticated approach. 82 | 83 | * 2010-01-27 - V1.6 - New policy type: Text in caption of pdf link 84 | with caption. Updated policy 85 | for sciencedirect. 86 | 87 | 88 | * 2010-02-17 - V1.7 - Support for single ' in links. 89 | 90 | * 2010-03-13 - V1.8 - New feature: Delete local copies from the 91 | filesystem. 92 | 93 | 94 | * 2010-03-15 - V1.9 - Contribution by Zhi-Wei Huang: 95 | Update fields from SPIRES extended: use 96 | "doi" when "eprint" is not available for 97 | mapping, also update "eprint" field. 98 | 99 | * 2010-04-19 - V2.0 - Bug fix in HtmlPage.java: Convert & to & 100 | in links. 101 | 102 | * 2010-08-09 - V2.1 - Implement proper timeout in http-connections. 103 | User-defined filename templates implemented. 104 | 105 | * 2010-09-02 - V2.2 - Add new policies for link-matching: title and 106 | class of link (). 107 | Implement new policy for sciencedirect. 108 | 109 | * 2011-08-14 - V2.3 - Add changes by Julien Rioux (Use also DOI in 110 | update from SPIRES, handle arXiv: prefix in 111 | eprint). Improved Julien's code to download 112 | all versions from arXiv. Implement John 113 | Kehayias's proposal to use same filename 114 | restrictions on all OS to allow for easier 115 | sharing of files. Add Nicholas Jackson's 116 | patch to recognize application/xhtml. 117 | 118 | * 2011-09-19 - V2.4 - Bug fix in DownloadHttpSession.java: 119 | getHeaderFieldKey can be null 120 | 121 | * 2011-09-23 - V2.5 - Use INSPIRE plugin for CommandUpdate if the 122 | plugin is available. 123 | 124 | * 2012-01-24 - V2.6 - Fixes compatibility problem with JabRef 2.8b. 125 | -------------------------------------------------------------------------------- /src/Base64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | public class Base64 { 28 | 29 | private static final char[] map64 = { 30 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 31 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 32 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 33 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 34 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 35 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 36 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', 37 | '4', '5', '6', '7', '8', '9', '+', '/'}; 38 | 39 | private static int inverseMap64(byte c) { 40 | 41 | if (c >= 'A' && c <= 'Z') { 42 | return c-'A'; 43 | } else if (c >= 'a' && c <= 'z') { 44 | return c-'a' + 26; 45 | } else if (c >= '0' && c <= '9') { 46 | return c-'0' + 52; 47 | } else if (c == '+') { 48 | return 62; 49 | } else if (c == '/') { 50 | return 63; 51 | } 52 | 53 | return 64; 54 | } 55 | 56 | private static int toUnsignedByte(byte c) { 57 | return (c<0)?c+256:c; 58 | } 59 | 60 | private static byte toSignedByte(int c) { 61 | return (c>127)?((byte)(c-256)):((byte)c); 62 | } 63 | 64 | public static byte[] decode(String s) { 65 | return decode(s.getBytes()); 66 | } 67 | 68 | public static byte[] decode(byte[] in) { 69 | if (in.length == 0) 70 | return new byte[0]; 71 | 72 | if (in.length % 4 != 0) 73 | return null; 74 | 75 | int nFourByteChunks = in.length / 4; 76 | int inPos = 0; 77 | int outPos = 0; 78 | int nStripBytes = 0; 79 | 80 | if (in[in.length-1] == '=') { 81 | nStripBytes++; 82 | if (in[in.length-2] == '=') { 83 | nStripBytes++; 84 | } 85 | } 86 | 87 | byte[] out = new byte[nFourByteChunks * 3 - nStripBytes]; 88 | 89 | while (inPos < in.length) { 90 | if (in[inPos+3] == '=') { 91 | if (in[inPos+2] == '=') { 92 | int fourBytes = inverseMap64(in[inPos]) * 64 * 64 * 64 + 93 | inverseMap64(in[inPos+1]) * 64 * 64; 94 | fourBytes /= 256 * 256; 95 | out[outPos] = toSignedByte(fourBytes % 256); 96 | outPos += 1; 97 | } else { 98 | int fourBytes = inverseMap64(in[inPos]) * 64 * 64 * 64 + 99 | inverseMap64(in[inPos+1]) * 64 * 64 + 100 | inverseMap64(in[inPos+2]) * 64; 101 | fourBytes /= 256; 102 | out[outPos+1] = toSignedByte(fourBytes % 256); fourBytes /= 256; 103 | out[outPos] = toSignedByte(fourBytes % 256); 104 | outPos += 2; 105 | } 106 | } else { 107 | int fourBytes = inverseMap64(in[inPos]) * 64 * 64 * 64 + 108 | inverseMap64(in[inPos+1]) * 64 * 64 + 109 | inverseMap64(in[inPos+2]) * 64 + 110 | inverseMap64(in[inPos+3]); 111 | out[outPos+2] = toSignedByte(fourBytes % 256); fourBytes /= 256; 112 | out[outPos+1] = toSignedByte(fourBytes % 256); fourBytes /= 256; 113 | out[outPos] = toSignedByte(fourBytes % 256); 114 | outPos += 3; 115 | } 116 | inPos += 4; 117 | } 118 | 119 | return out; 120 | } 121 | 122 | public static String encode(String s) { 123 | return encode(s.getBytes()); 124 | } 125 | 126 | public static String encode(byte[] in) { 127 | 128 | if (in.length == 0) 129 | return new String(""); 130 | 131 | int nThreeByteChunks = ((in.length - 1) / 3) + 1; 132 | char[] out = new char[nThreeByteChunks * 4]; 133 | 134 | int threeBytes; 135 | int bits6; 136 | 137 | int outPos = 0; 138 | int inPos = 0; 139 | 140 | while ((inPos + 3) <= in.length) { 141 | 142 | threeBytes = (toUnsignedByte(in[inPos]) * 256 * 256 + toUnsignedByte(in[inPos+1]) * 256 + toUnsignedByte(in[inPos+2])); 143 | 144 | out[outPos+3] = map64[threeBytes % 64]; threeBytes /= 64; 145 | out[outPos+2] = map64[threeBytes % 64]; threeBytes /= 64; 146 | out[outPos+1] = map64[threeBytes % 64]; threeBytes /= 64; 147 | out[outPos] = map64[threeBytes % 64]; 148 | 149 | inPos += 3; 150 | outPos += 4; 151 | 152 | } 153 | 154 | if (inPos == in.length - 2) { 155 | 156 | threeBytes = (toUnsignedByte(in[inPos]) * 256 * 256 + toUnsignedByte(in[inPos+1]) * 256); 157 | 158 | out[outPos+3] = '='; threeBytes /= 64; 159 | out[outPos+2] = map64[threeBytes % 64]; threeBytes /= 64; 160 | out[outPos+1] = map64[threeBytes % 64]; threeBytes /= 64; 161 | out[outPos] = map64[threeBytes % 64]; 162 | 163 | } else if (inPos == in.length - 1) { 164 | 165 | threeBytes = (toUnsignedByte(in[inPos]) * 256 * 256); 166 | 167 | out[outPos+3] = '='; threeBytes /= 64; 168 | out[outPos+2] = '='; threeBytes /= 64; 169 | out[outPos+1] = map64[threeBytes % 64]; threeBytes /= 64; 170 | out[outPos] = map64[threeBytes % 64]; 171 | 172 | } 173 | return new String(out); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/CommandUpdate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Christoph Lehner 3 | * Modifications by: 4 | * - Zhi-Wei Huang (2010): added "eprint" field in update, 5 | * use "doi" field for mapping when "eprint" field is not available 6 | * - Julien Rioux (2010): If the SPIRES search with eprint fails, try again with doi. 7 | * Deal with "arXiv:" prefix in eprint. 8 | * 9 | * All programs in this directory and subdirectories are published under the GNU 10 | * General Public License as described below. 11 | * 12 | * This program is free software; you can redistribute it and/or modify it under 13 | * the terms of the GNU General Public License as published by the Free Software 14 | * Foundation; either version 2 of the License, or (at your option) any later 15 | * version. 16 | * 17 | * This program is distributed in the hope that it will be useful, but WITHOUT 18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 19 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 20 | * details. 21 | * 22 | * You should have received a copy of the GNU General Public License along with 23 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 24 | * Place, Suite 330, Boston, MA 02111-1307 USA 25 | * 26 | * Further information about the GNU GPL is available at: 27 | * http://www.gnu.org/copyleft/gpl.ja.html 28 | * 29 | */ 30 | package localcopy; 31 | 32 | import net.sf.jabref.gui.*; 33 | import net.sf.jabref.*; 34 | 35 | import net.sf.jabref.plugin.core.JabRefPlugin; 36 | import net.sf.jabref.plugin.PluginCore; 37 | import net.sf.jabref.imports.ImportInspector; 38 | import net.sf.jabref.imports.EntryFetcher; 39 | import net.sf.jabref.imports.SPIRESFetcher; 40 | import net.sf.jabref.plugin.core.generated._JabRefPlugin.EntryFetcherExtension; 41 | import javax.swing.*; 42 | import java.awt.*; 43 | import java.awt.event.*; 44 | import java.util.Arrays; 45 | import java.util.ArrayList; 46 | 47 | class FieldsDialog extends JPanel implements ActionListener, WindowListener { 48 | 49 | private JButton okButton, cancelButton; 50 | private String[] fields; 51 | private JDialog d; 52 | private JCheckBox[] cb; 53 | 54 | public String[] sel = null; 55 | 56 | public FieldsDialog(JDialog d, String[] fields) { 57 | super(new BorderLayout()); 58 | 59 | this.d = d; 60 | this.fields = fields; 61 | cb = new JCheckBox[fields.length]; 62 | 63 | Globals.prefs.putDefaultValue("localcopy-update-fields",""); 64 | ArrayList al = new ArrayList(Arrays.asList(Globals.prefs.getStringArray("localcopy-update-fields"))); 65 | 66 | okButton = new JButton("Ok"); 67 | okButton.addActionListener(this); 68 | 69 | cancelButton = new JButton("Cancel"); 70 | cancelButton.addActionListener(this); 71 | 72 | JPanel mainPane = new JPanel(); 73 | JPanel mainPaneCnt = new JPanel(); 74 | 75 | mainPaneCnt.setLayout(new GridLayout((int)Math.ceil((double)fields.length/2.0),2,10,0)); 76 | mainPaneCnt.setBackground(Color.white); 77 | mainPane.setBorder(BorderFactory.createLoweredBevelBorder()); 78 | mainPane.setBackground(Color.white); 79 | mainPaneCnt.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 80 | 81 | int i; 82 | for (i=0;i al = new ArrayList(); 117 | 118 | for (i=0;i sel = null; 183 | 184 | public CommandUpdate() { 185 | userChoiceFile = Choice.UNDECIDED; 186 | userChoiceKey = Choice.UNDECIDED; 187 | } 188 | 189 | public void setProgress(int current, int max) { 190 | console.setProgressBarMaximum(max); 191 | console.setProgressBarValue(current); 192 | } 193 | 194 | public void addEntry(BibtexEntry entry) { 195 | this.entry = entry; 196 | } 197 | 198 | public void toFront() { 199 | } 200 | 201 | public void setStatus(String s) { 202 | //console.output("- Status: " + s + "\n",false); 203 | } 204 | 205 | public void showMessage(Object message, String title, int msgType) { 206 | showMessage(message.toString()); 207 | } 208 | 209 | public void showMessage(String string) { 210 | console.output("- INSPIRE/SPIRES: " + string + "\n", false); 211 | } 212 | 213 | private boolean checkUpdate(BibtexEntry n,BibtexEntry o,String field) { 214 | 215 | if (sel.contains(field)==false) 216 | return false; 217 | 218 | String nf = n.getField(field); 219 | String of = o.getField(field); 220 | 221 | if (nf == null || nf.length() == 0) // new data is empty, do not use it 222 | return false; 223 | 224 | // new data is not empty 225 | if (of == null || of.length() == 0) // old data is empty, use new data 226 | return true; 227 | 228 | // old data is not empty 229 | if (!of.equals(nf)) 230 | return true; 231 | 232 | return false; 233 | } 234 | 235 | public boolean check(Component dialog, BasePanel panel, BibtexEntry[] bes) { 236 | String[] fields = {BibtexFields.KEY_FIELD,"eprint","doi","url","journal","year","volume","pages","title","author","slaccitation"}; 237 | Arrays.sort(fields); 238 | String[] sel = FieldsDialog.createAndShow(fields); 239 | if (sel != null) { 240 | if (JOptionPane.showOptionDialog(dialog, 241 | "Updating the fields may overwrite userdefined values. Do you really want to continue?", 242 | "Warning",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,null,null,null) 243 | ==JOptionPane.YES_OPTION) { 244 | this.sel = new ArrayList(); 245 | for (int i=0;i " 381 | + entry.getField(fields[i]) + " .\n",false); 382 | console.keepConsole("One or more updates have been made."); 383 | bes.setField(fields[i],entry.getField(fields[i])); 384 | key = bes.getField(fields[i]); 385 | panel.markBaseChanged(); 386 | } 387 | 388 | } else { 389 | console.output("+ " + fields[i] + ": " + bes.getField(fields[i]) + " -> " 390 | + entry.getField(fields[i]) + " .\n",false); 391 | console.keepConsole("One or more updates have been made."); 392 | bes.setField(fields[i],entry.getField(fields[i])); 393 | panel.markBaseChanged(); 394 | 395 | if (fields[i].equals("doi") || fields[i].equals("url")) { 396 | boolean download = false; 397 | if (userChoiceFile == Choice.UNDECIDED) { 398 | Object[] options = {"Download this entry.", 399 | "Select download on all future new-pdf-file events.", 400 | "Ignore all future new-pdf-file events."}; 401 | String s = (String)JOptionPane.showInputDialog(dialog,"The entry " + key + " has a new DOI or URL field. " + 402 | "Should I try to download the new journal pdf?", 403 | "New DOI or URL field", 404 | JOptionPane.QUESTION_MESSAGE, 405 | null,options,options[0]); 406 | if (s != null) { 407 | if (s.equals(options[0])) { 408 | download = true; 409 | } else if (s.equals(options[1])) { 410 | download = true; 411 | userChoiceFile = Choice.GET; 412 | } else { 413 | userChoiceFile = Choice.IGNORE; 414 | } 415 | } 416 | } else if (userChoiceFile == Choice.GET) { 417 | download = true; 418 | } 419 | 420 | if (download) { 421 | Download d = new Download(panel, dialog, console); 422 | if (fields[i].equals("doi")) { 423 | d.downloadFromPage(bes,"http://dx.doi.org/" + entry.getField("doi")); 424 | } else { 425 | d.downloadFromPage(bes,entry.getField("url")); 426 | } 427 | console.setProgressBarEnabled(false); 428 | console.setProgressBarIndeterminate(false); 429 | } 430 | } 431 | } 432 | } 433 | } 434 | } else { 435 | console.output("! entry " + key + " could not be found on INSPIRE/SPIRES.\n",true); 436 | } 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /src/Console.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import java.lang.String; 28 | 29 | public interface Console { 30 | public void output(String msg, boolean err); 31 | public void keepConsole(String title); 32 | public void setProgressBarEnabled(boolean b); 33 | public void setProgressBarIndeterminate(boolean b); 34 | public void setProgressBarValue(int v); 35 | public void setProgressBarMaximum(int v); 36 | public boolean isCanceled(); 37 | } 38 | -------------------------------------------------------------------------------- /src/CookieManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import java.io.*; 28 | import java.util.*; 29 | import java.text.*; 30 | import java.net.URL; 31 | 32 | class Cookie { 33 | 34 | private final static String[] COOKIE_DATE_FORMATS = { 35 | "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'", 36 | "EEE',' dd MMM yyyy HH:mm:ss 'GMT'", 37 | "EEE MMM dd yyyy HH:mm:ss 'GMT'Z" 38 | }; 39 | 40 | private static SimpleDateFormat[] cDateFormats = null; 41 | static { 42 | cDateFormats = new SimpleDateFormat[COOKIE_DATE_FORMATS.length]; 43 | for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) { 44 | cDateFormats[i] = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], Locale.US); 45 | cDateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT")); 46 | } 47 | } 48 | 49 | class CookieMap { 50 | public String key,value; 51 | public CookieMap(String k,String v) { 52 | key=k;value=v; 53 | } 54 | } 55 | 56 | ArrayList map = new ArrayList(); 57 | 58 | public Cookie(String cookie) { 59 | String[] cv = cookie.split(";"); 60 | for (int k=0;kpath.length()) 84 | len=path.length(); 85 | String cDomain = domain.substring(pos); 86 | String cPath = path.substring(0,len); 87 | //System.err.println(url.toString() + "\n" + domain + ":" + path + "|" + tDomain + ":" + tPath + "\n" + 88 | // (cDomain.equalsIgnoreCase(tDomain) && cPath.equals(tPath))); 89 | if (cDomain.equalsIgnoreCase(tDomain) && cPath.equals(tPath)) 90 | return true; 91 | return false; 92 | } 93 | 94 | public boolean isExpired() { 95 | String expires = getKey("expires",null); 96 | if (expires == null) 97 | return false; 98 | 99 | for (SimpleDateFormat df : cDateFormats) { 100 | try { 101 | 102 | Date date = df.parse(expires); 103 | Date now = new Date(); 104 | 105 | if (date.compareTo(now)<=0) { 106 | return true; 107 | } 108 | 109 | return false; 110 | 111 | } catch (Exception e) { 112 | } 113 | } 114 | 115 | System.err.println("Could not parse cookie date: " + getName() + ", " + expires); 116 | return false; 117 | 118 | } 119 | 120 | public boolean equals(Cookie c) { 121 | 122 | if (getName().equalsIgnoreCase(c.getName()) && 123 | getKey("domain","").equalsIgnoreCase(c.getKey("domain","")) && 124 | getKey("path","/").equals(c.getKey("path","/"))) 125 | return true; 126 | 127 | return false; 128 | } 129 | 130 | public String getKey(String key,String def) { 131 | CookieMap[] cm = map.toArray(new CookieMap[0]); 132 | for (int k=1;k cookies; 182 | 183 | public CookieManager() { 184 | cookies = new ArrayList(); 185 | } 186 | 187 | private void checkExpiration() { 188 | Iterator it = cookies.iterator(); 189 | while (it.hasNext()) { 190 | Cookie c = it.next(); 191 | if (c.isExpired()) { 192 | it.remove(); 193 | } 194 | } 195 | } 196 | 197 | public String getCookieString(URL url) { 198 | 199 | /* 200 | * first test if some cookies are expired 201 | */ 202 | checkExpiration(); 203 | 204 | /* 205 | * format string 206 | */ 207 | String szCookies = ""; 208 | Cookie[] cl = cookies.toArray(new Cookie[0]); 209 | for (int k=0;k 0) 212 | szCookies += "; "; 213 | szCookies += cl[k].toString(); 214 | } 215 | } 216 | return szCookies; 217 | } 218 | 219 | public String getCookieList(URL url) { 220 | String szCookies = ""; 221 | Cookie[] cl = cookies.toArray(new Cookie[0]); 222 | for (int k=0;k 0) 225 | szCookies += ", "; 226 | szCookies += cl[k].getName(); 227 | } 228 | } 229 | return szCookies; 230 | } 231 | 232 | public void setCookie(URL sender, String cookie) { 233 | Cookie c = new Cookie(cookie); 234 | if (c.getKey("domain","").length()==0) { 235 | c.setKey("domain",sender.getHost()); 236 | } 237 | setCookie(c); 238 | } 239 | 240 | public void setCookie(Cookie c) { 241 | if (c.getName() == null) 242 | return; 243 | 244 | for (int k=0;k cookie updated"); 248 | return; 249 | } 250 | } 251 | 252 | cookies.add(c); 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/Download.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Christoph Lehner 3 | * 4 | * Modifications by: 5 | * - Julien Rioux (2010): Download all arXiv versions (adapted from Julien's code), 6 | * add descriptions to files. 7 | * 8 | * All programs in this directory and subdirectories are published under the GNU 9 | * General Public License as described below. 10 | * 11 | * This program is free software; you can redistribute it and/or modify it under 12 | * the terms of the GNU General Public License as published by the Free Software 13 | * Foundation; either version 2 of the License, or (at your option) any later 14 | * version. 15 | * 16 | * This program is distributed in the hope that it will be useful, but WITHOUT 17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 19 | * details. 20 | * 21 | * You should have received a copy of the GNU General Public License along with 22 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 23 | * Place, Suite 330, Boston, MA 02111-1307 USA 24 | * 25 | * Further information about the GNU GPL is available at: 26 | * http://www.gnu.org/copyleft/gpl.ja.html 27 | * 28 | */ 29 | package localcopy; 30 | 31 | import net.sf.jabref.gui.*; 32 | import net.sf.jabref.*; 33 | import net.sf.jabref.labelPattern.LabelPatternUtil; 34 | 35 | import java.lang.reflect.*; 36 | import java.util.*; 37 | import java.util.regex.*; 38 | import java.io.*; 39 | import javax.swing.*; 40 | import java.awt.Component; 41 | 42 | import java.net.URL; 43 | import java.net.MalformedURLException; 44 | import java.net.HttpURLConnection; 45 | import java.net.URLConnection; 46 | import java.net.URLDecoder; 47 | import java.net.URLEncoder; 48 | 49 | public class Download { 50 | 51 | private BasePanel panel; 52 | private Console console; 53 | private Component dialog; 54 | private DownloadHttpSession session; 55 | 56 | public Download(BasePanel panel, Component dialog, Console console) { 57 | this.panel = panel; 58 | this.console = console; 59 | this.dialog = dialog; 60 | this.session = new DownloadHttpSession(console); 61 | } 62 | 63 | private boolean hasFile(BibtexEntry b, String fn) { 64 | FileListTableModel m = new FileListTableModel(); 65 | m.setContent(b.getField("file")); 66 | for (int j=0;j1) { 241 | // offer to download all versions 242 | Object[] options = {"Download latest version.", 243 | "Download all versions."}; 244 | String s = (String)JOptionPane.showInputDialog(dialog,"More than one arXiv version is available (" + maxver + ").", 245 | "Download arXiv version", 246 | JOptionPane.QUESTION_MESSAGE, 247 | null,options,options[0]); 248 | if (s != null) { 249 | if (s.equals(options[1])) { 250 | downloadAll = true; 251 | } 252 | } else 253 | return; 254 | } 255 | 256 | if (!downloadAll) { 257 | downloadEprintFile(b, osFormatFn(key + "v" + maxver + ".pdf"), 258 | "http://arxiv.org/pdf/" + id + "v" + maxver, "arXiv v" + maxver); 259 | } else { 260 | for (int vid=1;vid<=maxver;vid++) { 261 | downloadEprintFile(b, osFormatFn(key + "v" + vid + ".pdf"), 262 | "http://arxiv.org/pdf/" + id + "v" + vid, "arXiv v" + vid); 263 | } 264 | } 265 | } 266 | 267 | boolean isPDFLink(String urlFn) { 268 | 269 | try { 270 | URL url = new URL(urlFn); 271 | URLConnection con = session.openURL(url); 272 | if (con.getContentType().toLowerCase().startsWith("application/pdf")) { 273 | return true; 274 | } 275 | } catch (MalformedURLException e) { 276 | console.output("! test for pdf url '" + e.getLocalizedMessage() + "' is malformed.\n",true); 277 | } catch (SecurityException e) { 278 | console.output("! could not test format of file '" + urlFn + "' due to security problems.\n",true); 279 | console.output("! " + e.getLocalizedMessage() + "\n",true); 280 | } catch (Exception e) { 281 | console.output("! could not test format of file '" + urlFn + "'.\n",true); 282 | console.output("! " + e.getLocalizedMessage() + "\n",true); 283 | e.printStackTrace(); 284 | } 285 | 286 | return false; 287 | } 288 | 289 | private HttpURLConnection logIn(HtmlPageLoginForm lf) throws IOException { 290 | IDItem it = IDManager.getID(lf.baseUrl.getHost(),lf.userFieldName + ":" + lf.passwordFieldName); 291 | if (it == null) 292 | throw new IOException("operation canceled by user"); 293 | 294 | String query = lf.query; 295 | if (query.length() > 0) 296 | query += "&"; 297 | query+= URLEncoder.encode(lf.userFieldName,"UTF-8") + "=" + URLEncoder.encode(it.getUserID(),"UTF-8"); 298 | query+= "&" + URLEncoder.encode(lf.passwordFieldName,"UTF-8") + "=" + URLEncoder.encode(it.getPassword(),"UTF-8"); 299 | 300 | URL u = new URL(lf.baseUrl,lf.action); 301 | if (lf.method.equalsIgnoreCase("get")) { 302 | if (u.getQuery() != null && u.getQuery().length() > 0) { 303 | u = new URL(lf.baseUrl,lf.action + "&" + query); 304 | } else { 305 | u = new URL(lf.baseUrl,lf.action + "?" + query); 306 | } 307 | } else if (lf.method.equalsIgnoreCase("post")) { 308 | return session.openURL(u,query); 309 | } else { 310 | throw new IOException("unknown form method: " + lf.method); 311 | } 312 | 313 | return session.openURL(u); 314 | } 315 | 316 | private String selectPDFFile(URL baseUrl, String[] links, String[] captions, String[] titles, String[] classes) throws IOException { 317 | String list = ""; 318 | String[] opts = { "Present list of pdf files to user", 319 | "Select n-th pdf file", 320 | "Select n-th pdf file from inverse list", 321 | "Select pdf file that contains a certain text", 322 | "Select pdf link whose caption contains a certain text", 323 | "Select pdf link whose title contains a certain text", 324 | "Select pdf link whose class contains a certain text" 325 | }; 326 | 327 | int k; 328 | for (k=0;k= links.length) { 360 | console.output("- warning: policy states to select " + (k+1) + "-th pdf file, but only " + links.length 361 | + " are available!\n",false); 362 | k = links.length - 1; 363 | } 364 | return links[k]; 365 | 366 | } else if (po.getSel().equals(opts[2])) { 367 | po = PolicyManager.getPolicy(baseUrl.getHost(),"Index of pdf file", 368 | "Enter the index of the pdf file to be selected.\n" + 369 | "The count starts with the last pdf file, i.e.,\n" + 370 | "the last pdf files is 1.", "#|Index: |1|1|1000"); 371 | if (po == null) 372 | throw new IOException("operation canceled by user"); 373 | 374 | k = po.getVal() - 1; 375 | if (k < 0) 376 | k = 0; 377 | else if (k >= links.length) { 378 | console.output("- warning: policy states to select " + (k+1) + "-th pdf file (inverse list), but only " + links.length 379 | + " are available!\n",false); 380 | k = links.length - 1; 381 | } 382 | return links[links.length - 1 - k]; 383 | 384 | } else if (po.getSel().equals(opts[3])) { 385 | po = PolicyManager.getPolicy(baseUrl.getHost(),"Text within pdf file", 386 | "Enter the text that has to appear within a pdf file name.\n" + 387 | "The test is not case sensitive.", "$|Text: |article|50"); 388 | if (po == null) 389 | throw new IOException("operation canceled by user"); 390 | 391 | for (k=0;k1) { 507 | dlFile = selectPDFFile(page.getBaseURL(),pdfLinks,pdfCaptions,pdfTitles,pdfClasses); 508 | } else if (pdfLinks.length == 1) { 509 | dlFile = pdfLinks[0]; 510 | } 511 | 512 | /* 513 | * If we found a pdf file 514 | */ 515 | if (dlFile != null) { 516 | // console.output("DBG: Found a PDF link: " + dlFile + "\n" ,false); 517 | if (!isPDFLink(dlFile)) { 518 | if (lf != null && !stateLoggedIn) { 519 | stateLoggedIn = true; 520 | console.output("- Link is not a PDF file. Try to log in. " + dlFile + ".\n",false); 521 | page = new HtmlPage(session,logIn(lf),console); 522 | continue; 523 | } else if (stateFollowedPDFLink > 0) { 524 | stateFollowedPDFLink--; 525 | console.output("- Link is not a PDF file. Follow link to HTML page and look for log in form. " + dlFile + ".\n",false); 526 | page = new HtmlPage(session,dlFile,console); 527 | continue; 528 | } else { 529 | console.output("! No more options for current page.\n",true); 530 | } 531 | } else { 532 | downloadFile(b,fn,dlFile,"Published version"); 533 | } 534 | } else { 535 | 536 | console.output("! No PDF files passed test on current page.\n",true); 537 | 538 | } 539 | 540 | if (stateLoggedIn && logOutLink != null) { 541 | console.output("- Performing log-out with " + logOutLink + ".\n",false); 542 | page = new HtmlPage(session,logOutLink,console); 543 | } 544 | break; 545 | } 546 | 547 | } catch (MalformedURLException e) { 548 | console.output("! parse url '" + e.getLocalizedMessage() + "' is malformed.\n",true); 549 | e.printStackTrace(); 550 | } catch (IOException e) { 551 | console.output("! could not download file '" + urlFn + "'.\n",true); 552 | console.output("! " + e.getLocalizedMessage() + "\n",true); 553 | e.printStackTrace(); 554 | } 555 | } 556 | 557 | public void downloadFromPage(BibtexEntry b, String url) { 558 | String key = FNTemplate.getFN("Journal",panel,b); 559 | if (key == null) { 560 | console.output("! download canceled by user.\n",true); 561 | return; 562 | } 563 | String fn = osFormatFn(key + ".pdf"); 564 | 565 | if (hasFile(b,fn)) { 566 | Object[] options = {"Overwrite old file.", "Keep old file."}; 567 | int n = JOptionPane.showOptionDialog(dialog,"A file named '" + fn + "' is already linked. What shall I do?", 568 | "File already linked", 569 | JOptionPane.YES_NO_OPTION, 570 | JOptionPane.QUESTION_MESSAGE, 571 | null,options,options[1]); 572 | if (n == 0) { 573 | downloadFromPageFile(b,fn,url); 574 | } 575 | } else { 576 | downloadFromPageFile(b,fn,url); 577 | } 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /src/DownloadHttpSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import java.io.*; 28 | import java.util.*; 29 | 30 | import java.net.URL; 31 | import java.net.MalformedURLException; 32 | import java.net.HttpURLConnection; 33 | import java.net.URLConnection; 34 | import java.net.URLDecoder; 35 | import java.net.URLEncoder; 36 | 37 | import javax.net.ssl.HostnameVerifier; 38 | import javax.net.ssl.HttpsURLConnection; 39 | import javax.net.ssl.SSLContext; 40 | import javax.net.ssl.SSLSession; 41 | import javax.net.ssl.TrustManager; 42 | import javax.net.ssl.X509TrustManager; 43 | 44 | 45 | public class DownloadHttpSession { 46 | 47 | private Console console; 48 | private String authentication, referer; 49 | private static CookieManager cm = new CookieManager(); // keep cookies as long as LocalCopy runs 50 | 51 | public DownloadHttpSession(Console console) { 52 | this.console = console; 53 | this.authentication = ""; 54 | this.referer = ""; 55 | startupSSL(); 56 | } 57 | 58 | private void startupSSL() { 59 | try { 60 | System.setProperty( "java.protocol.handler.pkgs" , "javax.net.ssl" ); 61 | TrustManager[] trustAllCerts = new TrustManager[] { 62 | new X509TrustManager(){ 63 | public java.security.cert.X509Certificate[] getAcceptedIssuers(){ 64 | return null; 65 | } 66 | public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType ) { } 67 | public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType ) { } 68 | } 69 | }; 70 | 71 | SSLContext sc = SSLContext.getInstance( "SSL" ); 72 | sc.init( null, trustAllCerts, new java.security.SecureRandom() ); 73 | HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory() ); 74 | 75 | HostnameVerifier hv = new HostnameVerifier() { 76 | public boolean verify(String urlHostName, SSLSession session) { 77 | return true; 78 | } 79 | }; 80 | 81 | HttpsURLConnection.setDefaultHostnameVerifier(hv); 82 | } catch (Exception e) { 83 | console.output("! " + e.getMessage() + "\n",true); 84 | } 85 | } 86 | 87 | public File downloadFile(String dirFn, String urlFn) { 88 | 89 | File dir = new File(dirFn); 90 | File tmpFile; 91 | 92 | try { 93 | tmpFile = File.createTempFile("article",".tmp",dir); 94 | } catch (Exception e) { 95 | console.output("! could not create temporary file in directory '" + dirFn + 96 | "' for download of '" + urlFn + "'.\n",true); 97 | console.setProgressBarEnabled(false); 98 | return null; 99 | } 100 | 101 | try { 102 | URL url = new URL(urlFn); 103 | URLConnection con = openURL(url); 104 | 105 | console.output("- Downloading " + urlFn + "...\n",false); 106 | 107 | if (!con.getContentType().startsWith("application/pdf")) { 108 | 109 | console.output("! File is not of mime type application/pdf, but " + con.getContentType() + ".\n" 110 | + "! Usually this happens if authentication with the journal failed.\n" 111 | + "! Please follow the doi link with your web browser.\n",true); 112 | 113 | } else { 114 | 115 | InputStream in = new BufferedInputStream(con.getInputStream()); 116 | OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile)); 117 | 118 | console.setProgressBarEnabled(true); 119 | if (con.getContentLength() == -1) { 120 | console.setProgressBarIndeterminate(true); 121 | } else { 122 | console.setProgressBarIndeterminate(false); 123 | console.setProgressBarMaximum(con.getContentLength()); 124 | console.output("- File size is " + (con.getContentLength() / 1024) + " kB.\n",false); 125 | } 126 | 127 | byte[] buffer = new byte[512]; 128 | int totalBytesRead = 0; 129 | while(!console.isCanceled()) { 130 | int bytesRead = in.read(buffer); 131 | if(bytesRead == -1) 132 | break; 133 | totalBytesRead += bytesRead; 134 | console.setProgressBarValue(totalBytesRead); 135 | out.write(buffer, 0, bytesRead); 136 | } 137 | 138 | out.close(); 139 | 140 | if (!console.isCanceled()) { 141 | console.output("- Download completed.\n",false); 142 | console.setProgressBarValue(0); 143 | console.setProgressBarEnabled(false); 144 | return tmpFile; 145 | } else { 146 | console.output("- Download canceled.\n",false); 147 | } 148 | } 149 | 150 | } catch (MalformedURLException e) { 151 | console.output("! Download url '" + e.getLocalizedMessage() + "' is malformed.\n",true); 152 | e.printStackTrace(); 153 | } catch (SecurityException e) { 154 | console.output("! Could not download file '" + urlFn + "' due to security problems.\n",true); 155 | console.output("! " + e.getLocalizedMessage() + "\n",true); 156 | e.printStackTrace(); 157 | } catch (Exception e) { 158 | console.output("! Could not download file '" + urlFn + "'.\n",true); 159 | console.output("! " + e.getLocalizedMessage() + "\n",true); 160 | e.printStackTrace(); 161 | } 162 | 163 | tmpFile.delete(); 164 | console.setProgressBarValue(0); 165 | console.setProgressBarEnabled(false); 166 | return null; 167 | } 168 | 169 | private String getHeaderHelper(HttpURLConnection con, String name) { 170 | int i; 171 | for (i=1;con.getHeaderField(i) != null;i++) { 172 | if (name.equalsIgnoreCase(con.getHeaderFieldKey(i))) 173 | return con.getHeaderField(i); 174 | } 175 | return null; 176 | } 177 | 178 | public void setAuthentication(String auth) { 179 | authentication = auth; 180 | } 181 | 182 | public void setReferer(String ref) { 183 | referer = ref; 184 | } 185 | 186 | public HttpURLConnection openURL(URL url) throws IOException { 187 | return openURL(url, null); 188 | } 189 | 190 | public HttpURLConnection openURL(URL url, String postQuery) throws IOException { 191 | HttpURLConnection con; 192 | int n,r,i; 193 | 194 | if (postQuery == null) 195 | postQuery = ""; 196 | 197 | n=0; 198 | do { 199 | con = (HttpURLConnection)url.openConnection(); 200 | con.setConnectTimeout(15000); 201 | con.setReadTimeout(15000); 202 | con.setInstanceFollowRedirects(false); 203 | con.setRequestProperty("User-Agent", "Jabref"); 204 | 205 | if (postQuery.length() == 0) { 206 | console.output("- GET " + con.getURL().toString() + " with cookies {" + cm.getCookieList(url) + "}.\n",false); 207 | } else { 208 | console.output("- POST " + con.getURL().toString() + " with cookies {" + cm.getCookieList(url) + "}.\n",false); 209 | } 210 | 211 | String cookies = cm.getCookieString(url); 212 | if (cookies.length() > 0) { 213 | con.setRequestProperty("Cookie", cookies); 214 | } 215 | if (authentication.length() > 0) { 216 | con.setRequestProperty("Authorization" , authentication); 217 | } 218 | if (referer.length() > 0) { 219 | con.setRequestProperty("Referer", referer); 220 | } 221 | 222 | if (postQuery.length() > 0) { 223 | con.setDoOutput(true); 224 | OutputStream outStream = con.getOutputStream(); 225 | outStream.write(postQuery.getBytes()); 226 | outStream.flush(); 227 | outStream.close(); 228 | } 229 | 230 | n++; 231 | 232 | r = con.getResponseCode(); 233 | 234 | for (i=1;con.getHeaderField(i) != null && con.getHeaderFieldKey(i) != null;i++) { 235 | if (con.getHeaderFieldKey(i).equalsIgnoreCase("set-cookie")) { 236 | //console.output("- cookie received: " + con.getHeaderField(i) + ".\n",false); 237 | cm.setCookie(con.getURL(),con.getHeaderField(i)); 238 | } else if (con.getHeaderFieldKey(i).equalsIgnoreCase("set-cookie2")) { 239 | console.output("- set-cookie2 request not handled!\n",false); 240 | } 241 | } 242 | 243 | if (r == HttpURLConnection.HTTP_MOVED_TEMP || 244 | r == HttpURLConnection.HTTP_MOVED_PERM || 245 | r == HttpURLConnection.HTTP_SEE_OTHER) { 246 | String newURL = getHeaderHelper(con,"location"); 247 | if (newURL == null) 248 | throw new IOException("empty redirect"); 249 | URL urlRedir = new URL(url,newURL); 250 | url = urlRedir; 251 | /* 252 | * important: after redirect, don't post same values again 253 | */ 254 | postQuery = ""; 255 | } else if (r == HttpURLConnection.HTTP_OK) { 256 | break; 257 | } else if (r == HttpURLConnection.HTTP_UNAUTHORIZED) { 258 | String realm = getHeaderHelper(con,"WWW-Authenticate"); 259 | console.output("- authentication required: " + realm + "\n",false); 260 | 261 | IDItem m = IDManager.getID(con.getURL().getHost(),realm); 262 | if (m == null) 263 | throw new IOException("authentication canceled"); 264 | 265 | String userPassword = m.getUserID() + ":" + m.getPassword(); 266 | String encoding = Base64.encode(userPassword.getBytes()); 267 | String newAuth = "Basic " + encoding; 268 | if (newAuth.equals(authentication)) { 269 | // In case the suggested authentication is the same as just tried, 270 | // invalidate login for this realm and ask id manager again. 271 | IDManager.invalidate(con.getURL().getHost(),realm); 272 | m = IDManager.getID(con.getURL().getHost(),realm); 273 | if (m == null) 274 | throw new IOException("authentication canceled"); 275 | 276 | userPassword = m.getUserID() + ":" + m.getPassword(); 277 | encoding = Base64.encode(userPassword.getBytes()); 278 | authentication = "Basic " + encoding; 279 | } else { 280 | authentication = newAuth; 281 | } 282 | 283 | } else { 284 | for (i=0;con.getHeaderField(i) != null;i++) { 285 | console.output("! " + con.getHeaderFieldKey(i) + ": " + con.getHeaderField(i) + "\n",true); 286 | } 287 | throw new IOException("http request rejected"); 288 | } 289 | 290 | if (console.isCanceled()) { 291 | throw new IOException("user canceled during resolution of doi"); 292 | } 293 | 294 | } while (n<20); 295 | 296 | if (n==20) { 297 | throw new IOException("redirected too often (>20 times)"); 298 | } 299 | 300 | return con; 301 | } 302 | 303 | public void dumpStream(URLConnection con) { 304 | try { 305 | InputStream input = new BufferedInputStream(con.getInputStream()); 306 | BufferedReader html = new BufferedReader(new InputStreamReader(input)); 307 | String line; 308 | 309 | System.err.println("------ STREAM DUMP ------"); 310 | while( (line = html.readLine()) != null ) { 311 | System.err.println(line); 312 | } 313 | System.err.println("-------------------------"); 314 | 315 | } catch (IOException e) { 316 | e.printStackTrace(); 317 | System.err.println(e.getMessage()); 318 | } 319 | } 320 | 321 | } 322 | -------------------------------------------------------------------------------- /src/FNTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.*; 30 | import java.security.*; 31 | import java.util.Set; 32 | import java.util.Iterator; 33 | 34 | import net.sf.jabref.*; 35 | import net.sf.jabref.labelPattern.LabelPatternUtil; 36 | 37 | public class FNTemplate { 38 | 39 | private static String getPref(String c) { 40 | String d = "${BIBTEXKEY}"; 41 | if (c.equals("arXiv")) { 42 | d += "-eprint"; 43 | } 44 | 45 | if (Globals.prefs.hasKey("localcopy-fntemplate-" + c)) { 46 | return new String(Base64.decode(Globals.prefs.get("localcopy-fntemplate-" + c))); 47 | } 48 | return d; 49 | } 50 | 51 | private static void setPref(String c,String v) { 52 | Globals.prefs.put("localcopy-fntemplate-" + c,Base64.encode(v)); 53 | } 54 | 55 | private static String getKey(BasePanel panel, BibtexEntry b) { 56 | String k = b.getField(BibtexFields.KEY_FIELD); 57 | if (k == null || k.length()==0) { 58 | LabelPatternUtil.makeLabel(Globals.prefs.getKeyPattern(), panel.database(), b); 59 | k = b.getField(BibtexFields.KEY_FIELD); 60 | b.setField(BibtexFields.KEY_FIELD,""); 61 | 62 | k = (String)JOptionPane.showInputDialog(panel.frame(),"Please specify the BibTeX key used for this entry.\n"+ 63 | "It will be used as base for the filename.", 64 | "BibTeX key is not set", 65 | JOptionPane.PLAIN_MESSAGE, 66 | null, 67 | null, 68 | k); 69 | 70 | if (k == null) 71 | return null; 72 | 73 | b.setField(BibtexFields.KEY_FIELD,k); 74 | panel.markBaseChanged(); 75 | } 76 | return k; 77 | } 78 | 79 | public static void setTemplate(BasePanel panel, String c) { 80 | String t = getPref(c); 81 | t = (String)JOptionPane.showInputDialog(panel.frame(),"Please specify the filename template for " + c + "-downloads.\n"+ 82 | "${FIELD} will be replaced with the BibTex field entry named FIELD.\n" + 83 | "Note that FIELD has to be in upper-case letters. Examples:\n"+ 84 | " ${BIBTEXKEY} = BibTex key\n"+ 85 | " ${AUTHOR} = Author\n"+ 86 | " ${TITLE} = Title\n"+ 87 | " ${YEAR} = Year", "Specify filename template", 88 | JOptionPane.PLAIN_MESSAGE, 89 | null, 90 | null, 91 | t); 92 | if (t != null) 93 | setPref(c,t); 94 | } 95 | 96 | public static String getFN(String c, BasePanel panel, BibtexEntry e) { 97 | String t = getPref(c); 98 | 99 | if (t.indexOf("${BIBTEXKEY}")!=-1) { 100 | t = t.replace("${BIBTEXKEY}",getKey(panel,e)); 101 | } 102 | 103 | Set set = e.getAllFields(); 104 | Iterator i = set.iterator(); 105 | while (i.hasNext()) { 106 | String f = i.next(); 107 | if (f != null) { 108 | String v = e.getField(f); 109 | if (v != null) { 110 | t = t.replace("${" + f.toUpperCase().trim() + "}",v); 111 | } 112 | } 113 | } 114 | 115 | return t; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/FileListDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import javax.swing.*; 28 | import javax.swing.table.*; 29 | import java.awt.*; 30 | import java.awt.event.*; 31 | import java.security.*; 32 | import java.util.TreeSet; 33 | import java.util.ArrayList; 34 | import net.sf.jabref.*; 35 | import net.sf.jabref.gui.*; 36 | import java.net.URLDecoder; 37 | import java.net.URLEncoder; 38 | import java.io.*; 39 | 40 | public class FileListDialog extends JPanel 41 | implements ActionListener, WindowListener { 42 | 43 | private JButton okButton, cancelButton; 44 | private JDialog d; 45 | private JTable table; 46 | private FLTableModel tm; 47 | private Boolean canceled = false; 48 | public FileListItem[] fli = null; 49 | 50 | public FileListDialog(JDialog d, BibtexEntry[] bes) { 51 | super(new BorderLayout()); 52 | 53 | this.d = d; 54 | 55 | setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 56 | 57 | okButton = new JButton("Detach and delete"); 58 | okButton.addActionListener(this); 59 | 60 | cancelButton = new JButton("Cancel"); 61 | cancelButton.addActionListener(this); 62 | 63 | tm = new FLTableModel(bes); 64 | table = new JTable(tm); 65 | table.setPreferredScrollableViewportSize(new Dimension(500, 70)); 66 | table.setFillsViewportHeight(true); 67 | 68 | // table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); 69 | table.getColumnModel().getColumn(0).setPreferredWidth(10); 70 | table.getColumnModel().getColumn(1).setPreferredWidth(100); 71 | table.getColumnModel().getColumn(2).setPreferredWidth(100); 72 | table.getColumnModel().getColumn(3).setPreferredWidth(300); 73 | table.getColumnModel().getColumn(4).setPreferredWidth(200); 74 | 75 | 76 | JPanel listPane = new JPanel(); 77 | listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS)); 78 | JLabel label = new JLabel("Please select the files to be detached from the BibTeX entry and to be deleted from the filesystem:"); 79 | listPane.add(label); 80 | listPane.add(Box.createRigidArea(new Dimension(0,5))); 81 | listPane.add(new JScrollPane(table)); 82 | listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 83 | 84 | JPanel buttonPane = new JPanel(); 85 | buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); 86 | buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); 87 | 88 | buttonPane.add(Box.createHorizontalGlue()); 89 | buttonPane.add(okButton); 90 | buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); 91 | buttonPane.add(cancelButton); 92 | 93 | add(listPane, BorderLayout.CENTER); 94 | add(buttonPane, BorderLayout.PAGE_END); 95 | } 96 | 97 | class FLTableModel extends AbstractTableModel { 98 | private String[] columnNames = {"","Key","Author", "Title", "File"}; 99 | public Object[][] data; 100 | 101 | public FLTableModel(BibtexEntry[] b) { 102 | int i,j,N = 0; 103 | for (i=0;i al = new ArrayList(); 168 | for (int i = 0;i < tm.getRowCount();i++) { 169 | Boolean sel = (Boolean)tm.getValueAt(i,0); 170 | FileListItem it = new FileListItem(); 171 | if (sel) { 172 | it.m = (FileListTableModel)tm.getValueAt(i,5); 173 | it.id = ((Integer)tm.getValueAt(i,6)).intValue(); 174 | it.fn = (String)tm.getValueAt(i,4); 175 | it.b = (BibtexEntry)tm.getValueAt(i,7); 176 | al.add(it); 177 | } 178 | } 179 | fli = (FileListItem[])al.toArray(new FileListItem[0]); 180 | d.dispose(); 181 | } else if (evt.getSource() == cancelButton) { 182 | canceled = true; 183 | d.dispose(); 184 | } 185 | } 186 | 187 | public void windowClosing(WindowEvent e) { 188 | d.dispose(); 189 | canceled = true; 190 | } 191 | 192 | public void windowClosed(WindowEvent e) { 193 | } 194 | 195 | public void windowOpened(WindowEvent e) { 196 | } 197 | 198 | public void windowIconified(WindowEvent e) { 199 | } 200 | 201 | public void windowDeiconified(WindowEvent e) { 202 | } 203 | 204 | public void windowActivated(WindowEvent e) { 205 | } 206 | 207 | public void windowDeactivated(WindowEvent e) { 208 | } 209 | 210 | static FileListItem[] createAndShow(BibtexEntry[] bes) { 211 | JDialog d = new JDialog((Frame)null,"Delete local copies",true); 212 | FileListDialog p = new FileListDialog(d,bes); 213 | d.setResizable(true); 214 | d.getRootPane().setDefaultButton(p.okButton); 215 | p.setOpaque(true); 216 | d.setContentPane(p); 217 | d.addWindowListener(p); 218 | d.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 219 | d.pack(); 220 | d.setLocationRelativeTo(null); 221 | d.setVisible(true); 222 | return p.fli; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/FileListItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import net.sf.jabref.*; 28 | import net.sf.jabref.gui.*; 29 | 30 | public class FileListItem { 31 | public FileListTableModel m; 32 | public int id; 33 | public String fn; 34 | public BibtexEntry b; 35 | } 36 | -------------------------------------------------------------------------------- /src/HtmlPage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import net.sf.jabref.gui.*; 28 | import net.sf.jabref.*; 29 | 30 | import java.lang.reflect.*; 31 | import java.util.*; 32 | import java.util.regex.*; 33 | import java.io.*; 34 | import javax.swing.*; 35 | import java.awt.Component; 36 | 37 | import java.net.URL; 38 | import java.net.MalformedURLException; 39 | import java.net.HttpURLConnection; 40 | import java.net.URLConnection; 41 | import java.net.URLDecoder; 42 | import java.net.URLEncoder; 43 | 44 | class FormInputItem { 45 | public enum Type { UNKNOWN, HIDDEN, TEXT, PASSWORD }; 46 | public String typeSZ, name, value; 47 | public Type type = Type.UNKNOWN; 48 | FormInputItem(String typeSZ,String name,String value) { 49 | this.typeSZ = typeSZ; 50 | this.name = name; 51 | this.value = value; 52 | if (typeSZ == null) { 53 | type = Type.TEXT; 54 | } else { 55 | if (typeSZ.equalsIgnoreCase("text")) 56 | type = Type.TEXT; 57 | else if (typeSZ.equalsIgnoreCase("password")) 58 | type = Type.PASSWORD; 59 | else if (typeSZ.equalsIgnoreCase("hidden")) 60 | type = Type.HIDDEN; 61 | } 62 | } 63 | } 64 | 65 | class FrameStatus { 66 | public ArrayList checkedPages; 67 | public Stack pagesToCheck; 68 | 69 | FrameStatus() { 70 | checkedPages = new ArrayList(); 71 | pagesToCheck = new Stack(); 72 | } 73 | 74 | private boolean hasChecked(String page) { 75 | if (checkedPages.contains(page)) 76 | return true; 77 | return false; 78 | } 79 | 80 | public void checkPage(String page) { 81 | if (!hasChecked(page)) 82 | pagesToCheck.push(page); 83 | } 84 | 85 | public String nextPageToCheck() { 86 | if (pagesToCheck.empty()) 87 | return null; 88 | return pagesToCheck.pop(); 89 | } 90 | } 91 | 92 | public class HtmlPage { 93 | 94 | private ArrayList pdfLinks = new ArrayList(); 95 | private ArrayList pdfCaptions = new ArrayList(); 96 | private ArrayList allLinks = new ArrayList(); 97 | private ArrayList pdfTitles = new ArrayList(); 98 | private ArrayList pdfClasses = new ArrayList(); 99 | 100 | private HtmlPageLoginForm lf = null; 101 | private URL baseURL = null; 102 | private String logOutLink = null; 103 | private String title = null; 104 | private boolean hasCaptcha = false; 105 | 106 | public HtmlPage(DownloadHttpSession session, HttpURLConnection con, Console console) { 107 | 108 | String urlFn; 109 | FrameStatus state = new FrameStatus(); 110 | baseURL = con.getURL(); 111 | try { 112 | processConnection(session,con,console,state); 113 | } catch (IOException e) { 114 | console.output("! Error processConnection: " + e.getMessage() + "\n",true); 115 | } 116 | 117 | while ((urlFn = state.nextPageToCheck()) != null && !console.isCanceled()) { 118 | try { 119 | con = session.openURL(new URL(urlFn)); 120 | processConnection(session,con,console,state); 121 | } catch (IOException e) { 122 | console.output("! Error accessing " + urlFn + ": " + e.getMessage() + "\n",true); 123 | } 124 | } 125 | 126 | } 127 | 128 | public HtmlPage(DownloadHttpSession session, String pageURL, Console console) { 129 | 130 | FrameStatus state = new FrameStatus(); 131 | HttpURLConnection con; 132 | String urlFn; 133 | 134 | state.checkPage(pageURL); 135 | while ((urlFn = state.nextPageToCheck()) != null && !console.isCanceled()) { 136 | try { 137 | con = session.openURL(new URL(urlFn)); 138 | if (baseURL == null) 139 | baseURL = con.getURL(); 140 | processConnection(session,con,console,state); 141 | } catch (IOException e) { 142 | console.output("! Error accessing " + urlFn + ": " + e.getMessage() + "\n",true); 143 | } 144 | } 145 | 146 | } 147 | 148 | public URL getBaseURL() { 149 | return baseURL; 150 | } 151 | 152 | public String getLogOutLink() { 153 | return logOutLink; 154 | } 155 | 156 | public String getTitle() { 157 | return title; 158 | } 159 | 160 | public boolean hasCaptcha() { 161 | return hasCaptcha; 162 | } 163 | 164 | private static int nr = 0; 165 | 166 | private void processConnection(DownloadHttpSession session, HttpURLConnection con, Console console, 167 | FrameStatus state) throws IOException, MalformedURLException { 168 | 169 | InputStream input = new BufferedInputStream(con.getInputStream()); 170 | BufferedReader html = new BufferedReader(new InputStreamReader(input)); 171 | String fullHTML = "", line, urlFn; 172 | URL url = con.getURL(); 173 | urlFn = url.toString(); 174 | 175 | console.output("- Process " + urlFn + " ...\n",false); 176 | 177 | /* 178 | * if this link is a pdf file, add a pdf link 179 | */ 180 | if (con.getContentType().startsWith("application/pdf")) { 181 | addPdfLink(urlFn,"","",""); 182 | return; 183 | } else if (con.getContentType().startsWith("text/html") || 184 | con.getContentType().startsWith("application/xhtml")) { 185 | // proceed with html parsing 186 | } else { 187 | console.output("- Unknown content type: " + con.getContentType() + "\n",false); 188 | return; 189 | } 190 | 191 | /* 192 | * We have a text/html page, parse links and forms 193 | */ 194 | while( (line = html.readLine()) != null ) { 195 | fullHTML += " " + line; 196 | 197 | if (console.isCanceled()) { 198 | console.output("- Download canceled by user.\n",false); 199 | return; 200 | } 201 | } 202 | 203 | parseLinks(url,fullHTML); 204 | if (lf == null) 205 | lf = findLoginForm(url, fullHTML); 206 | else if (findLoginForm(url, fullHTML) != null) { 207 | console.output("- Warning: multiple login forms detected.\n",false); 208 | } 209 | 210 | /* 211 | * Gather further information about site 212 | */ 213 | title = findTitle(fullHTML); 214 | console.output("- Title: " + title + "\n",false); 215 | if (!hasCaptcha) 216 | hasCaptcha = siteHasCaptcha(fullHTML); 217 | 218 | ArrayList frames = parseFrames(url, fullHTML); 219 | Iterator it = frames.iterator(); 220 | while (it.hasNext()) { 221 | urlFn = it.next(); 222 | state.checkPage(urlFn); 223 | console.output("- Frame " + urlFn + " detected.\n",false); 224 | } 225 | } 226 | 227 | public String[] getPdfLinks() { 228 | return pdfLinks.toArray(new String[0]); 229 | } 230 | 231 | public String[] getPdfCaptions() { 232 | return pdfCaptions.toArray(new String[0]); 233 | } 234 | 235 | public String[] getPdfTitles() { 236 | return pdfTitles.toArray(new String[0]); 237 | } 238 | 239 | public String[] getPdfClasses() { 240 | return pdfClasses.toArray(new String[0]); 241 | } 242 | 243 | public String[] getLinks() { 244 | return allLinks.toArray(new String[0]); 245 | } 246 | 247 | public HtmlPageLoginForm getLoginForm() { 248 | return lf; 249 | } 250 | 251 | private String formatHref(String a) { 252 | return a.replaceAll("&","&"); 253 | } 254 | 255 | private void addPdfLink(String href,String caption,String a_title,String a_class) { 256 | href = formatHref(href); 257 | if (!pdfLinks.contains(href)) { 258 | pdfLinks.add(href); 259 | pdfCaptions.add(caption); 260 | pdfTitles.add(a_title); 261 | pdfClasses.add(a_class); 262 | } 263 | } 264 | 265 | private void addLink(String href) { 266 | href = formatHref(href); 267 | if (!allLinks.contains(href)) 268 | allLinks.add(href); 269 | } 270 | 271 | private boolean looksLikePDF(String href, String content) { 272 | Pattern pd = Pattern.compile("pdf",Pattern.CASE_INSENSITIVE); 273 | Matcher md = pd.matcher(href); 274 | if (md.find()) 275 | return true; 276 | md = pd.matcher(content); 277 | if (md.find()) 278 | return true; 279 | return false; 280 | } 281 | 282 | private boolean looksLikeLogout(String href, String content) { 283 | Pattern pd = Pattern.compile("logout",Pattern.CASE_INSENSITIVE); 284 | Matcher md = pd.matcher(href); 285 | if (md.find()) 286 | return true; 287 | md = pd.matcher(content); 288 | if (md.find()) 289 | return true; 290 | return false; 291 | } 292 | 293 | private String decodeHTML(String h) { 294 | return h; 295 | } 296 | 297 | private String parseProp(String ln, String prop) { 298 | 299 | Matcher m; 300 | 301 | m = Pattern.compile(prop + "[ ]*=[ ]*\"([^\"]*)\"",Pattern.CASE_INSENSITIVE).matcher(ln); 302 | if (m.find()) { 303 | return m.group(1); 304 | } 305 | 306 | m = Pattern.compile(prop + "[ ]*=[ ]*'([^']*)'",Pattern.CASE_INSENSITIVE).matcher(ln); 307 | if (m.find()) { 308 | return m.group(1); 309 | } 310 | 311 | m = Pattern.compile(prop + "[ ]*=[ ]*([^ >]*)",Pattern.CASE_INSENSITIVE).matcher(ln); 312 | if (m.find()) { 313 | return m.group(1); 314 | } 315 | 316 | return null; 317 | 318 | } 319 | 320 | private void parseLinks(URL baseURL, String fullHTML) throws MalformedURLException { 321 | Pattern p = Pattern.compile("]*?)>(.*?)",Pattern.CASE_INSENSITIVE); 322 | Matcher m = p.matcher(fullHTML); 323 | 324 | /*try { 325 | OutputStream out = new BufferedOutputStream(new FileOutputStream(new File("test.html"))); 326 | out.write(fullHTML.getBytes()); 327 | out.close(); 328 | } catch (Exception e) { 329 | }*/ 330 | 331 | while (m.find()) { 332 | String href = decodeHTML(parseProp(m.group(1),"href")); 333 | String a_title = decodeHTML(parseProp(m.group(1),"title")); 334 | String a_class = decodeHTML(parseProp(m.group(1),"class")); 335 | String content = m.group(2); 336 | 337 | if (href != null) { 338 | try { 339 | URL newHref = new URL(baseURL,href); 340 | String nh = newHref.toString(); 341 | 342 | addLink(nh); 343 | if (looksLikePDF(href,content)) { 344 | addPdfLink(nh,content,a_title,a_class); 345 | } else if (looksLikeLogout(href,content)) { 346 | if (logOutLink != null && !logOutLink.equals(nh)) { 347 | System.err.println("Multiple logout links detected: " + logOutLink + ", " + nh); 348 | } 349 | logOutLink = nh; 350 | } 351 | } catch (MalformedURLException e) { 352 | // a malformed link encountered 353 | } 354 | } 355 | } 356 | } 357 | 358 | private ArrayList parseFrames(URL baseURL, String fullHTML) throws MalformedURLException { 359 | ArrayList frames = new ArrayList(); 360 | Pattern p = Pattern.compile("]*?)>",Pattern.CASE_INSENSITIVE); 361 | Matcher m = p.matcher(fullHTML); 362 | while (m.find()) { 363 | String src = decodeHTML(parseProp(m.group(1),"src")); 364 | if (src != null) { 365 | URL newHref = new URL(baseURL,src); 366 | src = newHref.toString(); 367 | if (!frames.contains(src)) 368 | frames.add(src); 369 | } 370 | } 371 | return frames; 372 | } 373 | 374 | // the following is a very naive test for a captcha, improve in the future! 375 | private boolean siteHasCaptcha(String fullHTML) { 376 | Pattern pf = Pattern.compile("captcha",Pattern.CASE_INSENSITIVE); 377 | Matcher mf = pf.matcher(fullHTML); 378 | return mf.find(); 379 | } 380 | 381 | private String findTitle(String fullHTML) { 382 | Pattern pf = Pattern.compile("(.*?)",Pattern.CASE_INSENSITIVE); 383 | Matcher mf = pf.matcher(fullHTML); 384 | if (mf.find()) { 385 | return mf.group(1).trim(); 386 | } 387 | return null; 388 | } 389 | 390 | private HtmlPageLoginForm findLoginForm(URL baseUrl, String fullHTML) { 391 | Pattern pf = Pattern.compile("
]*?)>(.*?)
",Pattern.CASE_INSENSITIVE); 392 | Matcher mf = pf.matcher(fullHTML); 393 | String method = "",action = "",query = "",userFieldName = "",passwordFieldName = ""; 394 | boolean hasUserField = false, hasPasswordField = false; 395 | 396 | while (mf.find()) { 397 | method = parseProp(mf.group(1),"method"); 398 | action = parseProp(mf.group(1),"action"); 399 | query = ""; 400 | 401 | if (action != null) { 402 | hasUserField = false; 403 | hasPasswordField = false; 404 | userFieldName = ""; 405 | passwordFieldName = ""; 406 | 407 | Pattern pi = Pattern.compile("]*?)>",Pattern.CASE_INSENSITIVE); 408 | Matcher mi = pi.matcher(mf.group(2)); 409 | ArrayList in = new ArrayList(); 410 | while (mi.find()) { 411 | String type = parseProp(mi.group(1),"type"); 412 | String name = parseProp(mi.group(1),"name"); 413 | String value = parseProp(mi.group(1),"value"); 414 | 415 | if (name != null) { 416 | in.add(new FormInputItem(type,name,value)); 417 | } 418 | } 419 | 420 | /* 421 | * test if form has user / password fields 422 | */ 423 | FormInputItem[] fields = (FormInputItem[])in.toArray(new FormInputItem[0]); 424 | int i; 425 | for (i=0;i 0) 461 | query += "&"; 462 | 463 | try { 464 | query += URLEncoder.encode(fields[i].name,"UTF-8") + "=" + URLEncoder.encode(fields[i].value,"UTF-8"); 465 | } catch (UnsupportedEncodingException e) { 466 | // utf-8 should always be supported 467 | } 468 | } 469 | } 470 | 471 | if (hasUserField && hasPasswordField) 472 | return new HtmlPageLoginForm(baseUrl,method,action,query,userFieldName,passwordFieldName); 473 | } 474 | } 475 | 476 | return null; 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /src/HtmlPageLoginForm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import java.net.URL; 28 | 29 | public class HtmlPageLoginForm { 30 | public URL baseUrl; 31 | public String method, action, query, userFieldName, passwordFieldName; 32 | 33 | HtmlPageLoginForm(URL baseUrl,String method,String action,String query, 34 | String userFieldName,String passwordFieldName) { 35 | this.baseUrl = baseUrl; 36 | this.method = method; 37 | this.action = action; 38 | this.query = query; 39 | this.userFieldName = userFieldName; 40 | this.passwordFieldName = passwordFieldName; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/IDItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | public class IDItem { 28 | 29 | private String user, pass; 30 | 31 | public IDItem(String user, String pass) { 32 | this.user = user; 33 | this.pass = pass; 34 | } 35 | 36 | public String getUserID() { 37 | return user; 38 | } 39 | 40 | public String getPassword() { 41 | return pass; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/IDManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.*; 30 | import java.security.*; 31 | import java.util.TreeSet; 32 | 33 | import net.sf.jabref.*; 34 | 35 | class AuthID { 36 | public String user, pass; 37 | public boolean remember, canceled; 38 | }; 39 | 40 | class AuthDialog extends JPanel 41 | implements ActionListener, WindowListener { 42 | 43 | private JButton okButton, cancelButton; 44 | private JPasswordField passField; 45 | private JTextField userField; 46 | private JCheckBox saveID; 47 | private JDialog d; 48 | 49 | public AuthID authID = new AuthID(); 50 | 51 | public AuthDialog(JDialog d, String title, String user, String pass) { 52 | super(new BorderLayout()); 53 | 54 | this.d = d; 55 | 56 | Globals.prefs.putDefaultValue("localcopy-auth-remember",false); 57 | authID.user = user; 58 | authID.pass = pass; 59 | authID.remember = Globals.prefs.getBoolean("localcopy-auth-remember"); 60 | authID.canceled = false; 61 | 62 | passField = new JPasswordField(pass,10); 63 | passField.addActionListener(this); 64 | 65 | JLabel labelP = new JLabel("Password: "); 66 | labelP.setLabelFor(passField); 67 | 68 | userField = new JTextField(user,10); 69 | userField.addActionListener(this); 70 | 71 | JLabel labelU = new JLabel("Username: "); 72 | labelU.setLabelFor(userField); 73 | 74 | okButton = new JButton("Ok"); 75 | okButton.addActionListener(this); 76 | 77 | cancelButton = new JButton("Cancel"); 78 | cancelButton.addActionListener(this); 79 | 80 | JLabel labelInfo = new JLabel(title); 81 | 82 | saveID = new JCheckBox("Remember username and password"); 83 | saveID.setSelected(authID.remember); 84 | 85 | JPanel mainPane = new JPanel(); 86 | mainPane.setLayout(new BoxLayout(mainPane,BoxLayout.LINE_AXIS)); 87 | JPanel leftPane = new JPanel(new GridLayout(5,1,10,10)); 88 | JPanel rightPane = new JPanel(new GridLayout(5,1,10,10)); 89 | 90 | leftPane.add(new JLabel(GUIGlobals.getImage("integrityFail"))); 91 | leftPane.add(labelU); 92 | leftPane.add(labelP); 93 | leftPane.add(new JLabel("")); 94 | leftPane.add(new JLabel("")); 95 | 96 | rightPane.add(labelInfo); 97 | rightPane.add(userField); 98 | rightPane.add(passField); 99 | rightPane.add(saveID); 100 | 101 | JPanel buttonPane = new JPanel(new GridLayout(1,2,10,10)); 102 | buttonPane.add(okButton); 103 | buttonPane.add(cancelButton); 104 | 105 | rightPane.add(buttonPane); 106 | 107 | mainPane.add(leftPane); 108 | mainPane.add(rightPane); 109 | 110 | JPanel dlgPane = new JPanel(); 111 | dlgPane.setLayout(new BoxLayout(dlgPane,BoxLayout.PAGE_AXIS)); 112 | dlgPane.add(mainPane); 113 | 114 | add(dlgPane); 115 | setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); 116 | } 117 | 118 | public void actionPerformed(ActionEvent evt) { 119 | if (evt.getSource() == okButton || 120 | evt.getSource() == userField || 121 | evt.getSource() == passField) { 122 | authID.user = userField.getText(); 123 | authID.pass = new String(passField.getPassword()); 124 | authID.remember = saveID.isSelected(); 125 | authID.canceled = false; 126 | Globals.prefs.putBoolean("localcopy-auth-remember",authID.remember); 127 | d.dispose(); 128 | } else if (evt.getSource() == cancelButton) { 129 | authID.canceled = true; 130 | d.dispose(); 131 | } 132 | } 133 | 134 | public void windowClosing(WindowEvent e) { 135 | d.dispose(); 136 | authID.canceled = true; 137 | } 138 | 139 | public void windowClosed(WindowEvent e) { 140 | } 141 | 142 | public void windowOpened(WindowEvent e) { 143 | } 144 | 145 | public void windowIconified(WindowEvent e) { 146 | } 147 | 148 | public void windowDeiconified(WindowEvent e) { 149 | } 150 | 151 | public void windowActivated(WindowEvent e) { 152 | } 153 | 154 | public void windowDeactivated(WindowEvent e) { 155 | } 156 | 157 | static AuthID createAndShow(String title, String user, String pass) { 158 | JDialog d = new JDialog((Frame)null,"Authentication",true); 159 | AuthDialog p = new AuthDialog(d,title,user,pass); 160 | d.setResizable(false); 161 | d.getRootPane().setDefaultButton(p.okButton); 162 | p.setOpaque(true); 163 | d.setContentPane(p); 164 | d.addWindowListener(p); 165 | d.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 166 | d.pack(); 167 | d.setLocationRelativeTo(null); 168 | d.setVisible(true); 169 | return p.authID; 170 | } 171 | } 172 | 173 | 174 | public class IDManager { 175 | 176 | private static String getPref(String c,String n,String d) { 177 | if (Globals.prefs.hasKey("localcopy-auth-" + c + "-" + n)) { 178 | return new String(Base64.decode(Globals.prefs.get("localcopy-auth-" + c + "-" + n))); 179 | } 180 | return d; 181 | } 182 | 183 | private static void setPref(String c,String n,String v) { 184 | Globals.prefs.put("localcopy-auth-" + c + "-" + n,Base64.encode(v)); 185 | } 186 | 187 | private static void remPref(String c,String n) { 188 | if (Globals.prefs.hasKey("localcopy-auth-" + c + "-" + n)) { 189 | Globals.prefs.remove("localcopy-auth-" + c + "-" + n); 190 | } 191 | } 192 | 193 | private static boolean hasPref(String c,String n) { 194 | return Globals.prefs.hasKey("localcopy-auth-" + c + "-" + n); 195 | } 196 | 197 | public static IDItem getID(String host, String realm) { 198 | String c = code(host,realm); 199 | 200 | IDItem it = getDefaultID(host, realm); 201 | if (it != null) 202 | return it; 203 | 204 | String user = getPref(c,"user",""); 205 | 206 | AuthID id = AuthDialog.createAndShow(host + ", " + realm,user,""); 207 | if (id.canceled) 208 | return null; 209 | 210 | it = new IDItem(id.user,id.pass); 211 | if (id.remember) { 212 | setDefaultID(host, realm, it); 213 | } 214 | 215 | return it; 216 | } 217 | 218 | public static void invalidate(String host, String realm) { 219 | String c = code(host,realm); 220 | remPref(c,"pass"); 221 | } 222 | 223 | public static boolean isInvalidated(String host, String realm) { 224 | String c = code(host,realm); 225 | if (hasPref(c,"user") && !hasPref(c,"pass")) 226 | return true; 227 | return false; 228 | } 229 | 230 | public static void remove(String host, String realm) { 231 | String c = code(host,realm); 232 | remPref(c,"user"); 233 | remPref(c,"pass"); 234 | remPref(c,"host"); 235 | remPref(c,"realm"); 236 | unregister(c); 237 | } 238 | 239 | private static String code(String host, String realm) { 240 | try { 241 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 242 | String str = host + ":" + realm; 243 | md5.reset(); 244 | md5.update(str.getBytes()); 245 | byte[] result = md5.digest(); 246 | StringBuffer hexString = new StringBuffer(); 247 | for (int i=0; i hosts = new TreeSet(); 284 | for (i=0;i realms = new TreeSet(); 296 | for (i=0;i 0 && !codes[i].equals(c)) { 312 | newCodes += " " + codes[i]; 313 | } 314 | } 315 | Globals.prefs.put("localcopy-auth-list",newCodes); 316 | } 317 | 318 | private static void unregister(String c) { 319 | String cp = (Globals.prefs.hasKey("localcopy-auth-list"))? 320 | (Globals.prefs.get("localcopy-auth-list")):""; 321 | String[] codes = cp.split(" "); 322 | String newCodes = ""; 323 | int i; 324 | for (i=0;i 0 && !codes[i].equals(c)) { 326 | if (newCodes.length() > 0) 327 | newCodes += " "; 328 | newCodes += codes[i]; 329 | } 330 | } 331 | Globals.prefs.put("localcopy-auth-list",newCodes); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/LocalCopyPane.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import net.sf.jabref.gui.*; 28 | import net.sf.jabref.*; 29 | import net.sf.jabref.plugin.*; 30 | 31 | import java.util.*; 32 | import javax.swing.*; 33 | import javax.swing.event.*; 34 | import java.io.*; 35 | 36 | import java.awt.event.*; 37 | import java.awt.*; 38 | import java.awt.datatransfer.*; 39 | 40 | class CommandEprint implements ProgressDialog.Command { 41 | 42 | public boolean check(Component dialog, BasePanel panel, BibtexEntry[] bes) { 43 | return true; 44 | } 45 | 46 | public void run(Component dialog, BasePanel panel, BibtexEntry bes, Console console) { 47 | String id = bes.getField("eprint"); 48 | String key = bes.getField(BibtexFields.KEY_FIELD); 49 | if (id != null) { 50 | Download d = new Download(panel, dialog, console); 51 | d.downloadEprint(bes,id); 52 | } else { 53 | console.output("! entry " + key + " has no eprint field.\n",true); 54 | } 55 | } 56 | 57 | } 58 | 59 | class CommandDoiUrl implements ProgressDialog.Command { 60 | 61 | private enum Choice { UNDECIDED, DOI, URL }; 62 | private Choice userChoice; 63 | 64 | public CommandDoiUrl() { 65 | userChoice = Choice.UNDECIDED; 66 | } 67 | 68 | public boolean check(Component dialog, BasePanel panel, BibtexEntry[] bes) { 69 | return true; 70 | } 71 | 72 | public void run(Component dialog, BasePanel panel, BibtexEntry bes, Console console) { 73 | String doi = bes.getField("doi"); 74 | String url = bes.getField("url"); 75 | String key = bes.getField(BibtexFields.KEY_FIELD); 76 | 77 | /* 78 | * if no doi is set and url field contains doi information, re-format things 79 | */ 80 | if (url != null && url.toLowerCase().startsWith("doi:")) { 81 | if (doi == null) 82 | doi = url.substring(4); 83 | url = null; 84 | } 85 | 86 | /* 87 | * if doi is set, but full address is given, strip it away 88 | */ 89 | if (doi != null && doi.toLowerCase().startsWith("http://dx.doi.org/")) { 90 | doi = doi.substring(18); 91 | } 92 | 93 | /* 94 | * if doi is set, but doi prefix is given, strip it away 95 | */ 96 | if (doi != null && doi.toLowerCase().startsWith("doi:")) { 97 | doi = doi.substring(4); 98 | } 99 | 100 | /* 101 | * trim fields 102 | */ 103 | if (doi != null) 104 | doi = doi.trim(); 105 | if (url != null) 106 | url = url.trim(); 107 | 108 | Download d = new Download(panel, dialog, console); 109 | 110 | if (doi != null && url != null) { 111 | Choice thisChoice = userChoice; 112 | if (thisChoice == Choice.UNDECIDED) { 113 | Object[] options = {"Use DOI field", "Use URL field","Always use DOI field","Always use URL field"}; 114 | int n = JOptionPane.showOptionDialog(dialog,"The entry " + key + " has DOI and URL fields set.", 115 | "Select download source", 116 | JOptionPane.YES_NO_OPTION, 117 | JOptionPane.QUESTION_MESSAGE, 118 | null,options,options[1]); 119 | if (n == 0) { 120 | thisChoice = Choice.DOI; 121 | } else if (n == 1) { 122 | thisChoice = Choice.URL; 123 | } else if (n == 2) { 124 | thisChoice = Choice.DOI; 125 | userChoice = thisChoice; 126 | } else if (n == 3) { 127 | thisChoice = Choice.URL; 128 | userChoice = thisChoice; 129 | } 130 | } 131 | 132 | if (thisChoice == Choice.DOI) { 133 | d.downloadFromPage(bes,"http://dx.doi.org/" + doi); 134 | } else if (thisChoice == Choice.URL) { 135 | d.downloadFromPage(bes,url); 136 | } 137 | 138 | } else if (doi != null) { 139 | d.downloadFromPage(bes,"http://dx.doi.org/" + doi); 140 | } else if (url != null) { 141 | d.downloadFromPage(bes,url); 142 | } else { 143 | console.output("! entry " + key + " has no doi/url field.\n",true); 144 | } 145 | } 146 | } 147 | 148 | 149 | class LocalCopySidePaneComponent extends SidePaneComponent 150 | implements ActionListener { 151 | 152 | private GridBagLayout gbl = new GridBagLayout() ; 153 | private GridBagConstraints con = new GridBagConstraints() ; 154 | private JButton btnArxiv = new JButton(GUIGlobals.getImage("save")); 155 | private JButton btnDOIUrl = new JButton(GUIGlobals.getImage("save")); 156 | private JButton btnUpdate = new JButton(GUIGlobals.getImage("redo")); 157 | private JButton btnDelete = new JButton(GUIGlobals.getImage("delete")); 158 | 159 | private JButton btnSettings = new JButton(GUIGlobals.getImage("preferences")); 160 | private SidePaneManager manager; 161 | private JMenuItem menu; 162 | public JabRefFrame frame; 163 | 164 | public LocalCopySidePaneComponent(SidePaneManager manager,JabRefFrame frame,JMenuItem menu) { 165 | super(manager, GUIGlobals.getIconUrl("openUrl"), "Local copy"); 166 | this.manager = manager; 167 | this.menu = menu; 168 | this.frame = frame; 169 | 170 | int butSize = btnArxiv.getIcon().getIconHeight() + 5; 171 | Dimension butDim = new Dimension(butSize, butSize); 172 | 173 | btnArxiv.setPreferredSize(butDim); 174 | btnArxiv.setMinimumSize(butDim); 175 | btnArxiv.addActionListener(this); 176 | btnArxiv.setText("arXiv pdf"); 177 | btnArxiv.setToolTipText("Download pdf from arXiv preprint server."); 178 | 179 | btnDOIUrl.setPreferredSize(butDim); 180 | btnDOIUrl.setMinimumSize(butDim); 181 | btnDOIUrl.addActionListener(this); 182 | btnDOIUrl.setText("Journal pdf"); 183 | btnDOIUrl.setToolTipText("Download pdf from DOI/URL link."); 184 | 185 | btnUpdate.setPreferredSize(butDim); 186 | btnUpdate.setMinimumSize(butDim); 187 | btnUpdate.addActionListener(this); 188 | btnUpdate.setText("Update fields"); 189 | btnUpdate.setToolTipText("Update BibTeX fields from INSPIRE/SPIRES."); 190 | 191 | btnDelete.addActionListener(this); 192 | btnDelete.setToolTipText("Detach the local pdf from the BibTeX entry and delete the local pdf from the filesystem."); 193 | 194 | btnSettings.addActionListener(this); 195 | btnSettings.setToolTipText("Settings"); 196 | 197 | JPanel main = new JPanel(); 198 | main.setLayout(gbl); 199 | con.gridwidth = GridBagConstraints.REMAINDER; 200 | con.fill = GridBagConstraints.BOTH; 201 | con.weightx = 1; 202 | 203 | gbl.setConstraints(btnArxiv,con); 204 | main.add(btnArxiv); 205 | 206 | gbl.setConstraints(btnDOIUrl,con); 207 | main.add(btnDOIUrl); 208 | 209 | JPanel split = new JPanel(); 210 | split.setLayout(new BoxLayout(split, BoxLayout.LINE_AXIS)); 211 | btnUpdate.setMaximumSize(new Dimension(Short.MAX_VALUE,Short.MAX_VALUE)); 212 | split.add(btnUpdate); 213 | split.add(btnDelete); 214 | split.add(btnSettings); 215 | gbl.setConstraints(split,con); 216 | main.add(split); 217 | 218 | main.setBorder(BorderFactory.createEmptyBorder(1,1,1,1)); 219 | setContent(main); 220 | setName("localcopy"); 221 | } 222 | 223 | public void setActiveBasePanel(BasePanel panel) { 224 | super.setActiveBasePanel(panel); 225 | if (panel == null) { 226 | boolean status = Globals.prefs.getBoolean("localcopyShow"); 227 | manager.hide("localcopy"); 228 | Globals.prefs.putBoolean("localcopyShow",status); 229 | menu.setEnabled(false); 230 | } else { 231 | if (Globals.prefs.getBoolean("localcopyShow")) { 232 | manager.show("localcopy"); 233 | } 234 | menu.setEnabled(true); 235 | } 236 | } 237 | 238 | private void debugFields(BibtexEntry b) { 239 | Set keys = b.getAllFields(); 240 | Iterator iter = keys.iterator(); 241 | while (iter.hasNext()) { 242 | String key = (String)iter.next(); 243 | System.err.println("Field " + key + " = " + b.getField(key)); 244 | } 245 | } 246 | 247 | private static String getClipboardContents() { 248 | String result = ""; 249 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 250 | Transferable contents = clipboard.getContents(null); 251 | boolean hasTransferableText = (contents != null) && contents.isDataFlavorSupported(DataFlavor.stringFlavor); 252 | if (hasTransferableText) { 253 | try { 254 | result = (String)contents.getTransferData(DataFlavor.stringFlavor); 255 | } 256 | catch (UnsupportedFlavorException ex){ 257 | } 258 | catch (IOException ex) { 259 | } 260 | } 261 | return result; 262 | } 263 | 264 | public void actionPerformed(ActionEvent e) { 265 | if (e.getSource() == btnSettings) { 266 | JPopupMenu popup = new JPopupMenu(); 267 | JMenuItem menuItem; 268 | JMenu menu; 269 | 270 | final Icon iconPolicy = new ImageIcon(GUIGlobals.getIconUrl("integrityCheck")); 271 | final Icon iconDelete = new ImageIcon(GUIGlobals.getIconUrl("remove")); 272 | final Icon iconRealm = new ImageIcon(GUIGlobals.getIconUrl("toggleGroups")); 273 | final Icon iconKey = new ImageIcon(GUIGlobals.getIconUrl("makeKey")); 274 | final Icon iconHelp = new ImageIcon(GUIGlobals.getIconUrl("help")); 275 | final Icon iconWww = new ImageIcon(GUIGlobals.getIconUrl("www")); 276 | final Icon iconInvalidate = new ImageIcon(GUIGlobals.getIconUrl("markEntries")); 277 | final Icon iconRestore = new ImageIcon(GUIGlobals.getIconUrl("undo")); 278 | final Icon iconImport = new ImageIcon(GUIGlobals.getIconUrl("dbImport")); 279 | final Icon iconExport = new ImageIcon(GUIGlobals.getIconUrl("dbExport")); 280 | final Icon iconSaveAs = new ImageIcon(GUIGlobals.getIconUrl("saveAs")); 281 | 282 | // Authentication menu 283 | String[] hosts = IDManager.savedHosts(); 284 | if (hosts.length > 0) { 285 | menu = new JMenu("Authentication"); 286 | menu.setIcon(iconKey); 287 | 288 | int i,j; 289 | for (i=0;i 0) { 354 | 355 | int i,j; 356 | 357 | menuItem = new JMenuItem("Export to clipboard",iconExport); 358 | menuItem.addActionListener(new ActionListener() { 359 | public void actionPerformed(ActionEvent evt) { 360 | StringSelection ss = new StringSelection(PolicyManager.export()); 361 | Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); 362 | } 363 | }); 364 | menu.add(menuItem); 365 | 366 | final Component frm = frame; 367 | menuItem = new JMenuItem("Delete all",iconDelete); 368 | menuItem.addActionListener(new ActionListener() { 369 | public void actionPerformed(ActionEvent evt) { 370 | if (JOptionPane.showOptionDialog(frm, 371 | "Do you really want to delete all policies?", 372 | "Warning",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,null,null,null) 373 | ==JOptionPane.YES_OPTION) { 374 | String[] hosts = PolicyManager.savedHosts(); 375 | for (int i=0;i 0)) { 471 | if (e.getSource() == btnArxiv) { 472 | ProgressDialog.createAndShow(this,new CommandEprint(),bes); 473 | } else if (e.getSource() == btnDOIUrl) { 474 | ProgressDialog.createAndShow(this,new CommandDoiUrl(),bes); 475 | } else if (e.getSource() == btnUpdate) { 476 | ProgressDialog.createAndShow(this,new CommandUpdate(),bes); 477 | } else if (e.getSource() == btnDelete) { 478 | FileListItem[] fli = FileListDialog.createAndShow(bes); 479 | if (fli!=null) { 480 | int i; 481 | for (i=fli.length-1;i>=0;i--) { // go through reverse list, so that removeEntry picks correct one also for multiple pdfs/entry 482 | 483 | MetaData metaData = panel.metaData(); 484 | if (metaData!=null) { 485 | ArrayList< String > dirs = new ArrayList(Arrays.asList(metaData.getFileDirectory("file"))); 486 | dirs.addAll(Arrays.asList(metaData.getFileDirectory("pdf"))); 487 | dirs.add("."); 488 | File file = Util.expandFilename(fli[i].fn, (String[])dirs.toArray(new String[0])); 489 | 490 | if ((file == null) || !file.exists()) { 491 | frame.showMessage("File " + fli[i].fn + " not found!"); 492 | } else { 493 | try { 494 | String link = file.getCanonicalPath(); 495 | if (!file.delete()) 496 | frame.showMessage("File " + link + " could not be deleted from filesystem!"); 497 | else { 498 | fli[i].m.removeEntry(fli[i].id); 499 | fli[i].b.setField("file",fli[i].m.getStringRepresentation()); 500 | panel.markBaseChanged(); 501 | } 502 | 503 | } catch (IOException ex) { 504 | frame.showMessage("Error deleting file " + fli[i].fn + ":\n" + ex.getLocalizedMessage()); 505 | } 506 | } 507 | } else 508 | frame.showMessage("No metaData available."); 509 | } 510 | } 511 | } 512 | } else { 513 | frame.output("Nothing selected."); 514 | } 515 | } 516 | } 517 | } 518 | 519 | public void componentOpening() { 520 | Globals.prefs.putBoolean("localcopyShow",true); 521 | } 522 | 523 | public void componentClosing() { 524 | Globals.prefs.putBoolean("localcopyShow",false); 525 | } 526 | } 527 | 528 | 529 | 530 | 531 | 532 | public class LocalCopyPane implements SidePanePlugin, ActionListener { 533 | 534 | protected SidePaneManager manager; 535 | private JMenuItem toggleMenu; 536 | private JabRefFrame frame; 537 | private LocalCopySidePaneComponent c = null; 538 | 539 | public void init(JabRefFrame frame, SidePaneManager manager) { 540 | this.manager = manager; 541 | this.frame = frame; 542 | 543 | toggleMenu = new JMenuItem("Toggle local copy panel",new ImageIcon(GUIGlobals.getIconUrl("openUrl"))); 544 | toggleMenu.setMnemonic(KeyEvent.VK_L); 545 | toggleMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.ALT_MASK)); 546 | 547 | toggleMenu.addActionListener(this); 548 | 549 | Globals.prefs.defaults.put("localcopyShow",true); 550 | 551 | /* 552 | * Check new version 553 | */ 554 | Globals.prefs.putDefaultValue("localcopy-param-ver",0.0); 555 | double pv = Globals.prefs.getDouble("localcopy-param-ver"); 556 | if (pv < 1.0) { 557 | pv = 1.0; 558 | PolicyManager.importFrom(LocalCopyVer.DEFPOLICIES); 559 | System.err.println("LocalCopy: updated to version " + pv); 560 | } else if (pv < 1.5) { 561 | pv = 1.5; 562 | PolicyManager.importFrom(LocalCopyVer.NEWPARAM15); 563 | System.err.println("LocalCopy: updated to version " + pv); 564 | } else if (pv < 2.2) { 565 | pv = 2.2; 566 | PolicyManager.importFrom(LocalCopyVer.NEWPARAM22); 567 | System.err.println("LocalCopy: updated to version " + pv); 568 | } else if (pv < 2.3) { 569 | pv = 2.3; 570 | PolicyManager.importFrom(LocalCopyVer.NEWPARAM23); 571 | System.err.println("LocalCopy: updated to version " + pv); 572 | } 573 | 574 | Globals.prefs.putDouble("localcopy-param-ver",pv); 575 | } 576 | 577 | public SidePaneComponent getSidePaneComponent() { 578 | c = new LocalCopySidePaneComponent(manager,frame,toggleMenu); 579 | return c; 580 | } 581 | 582 | public JMenuItem getMenuItem() { 583 | if (Globals.prefs.getBoolean("localcopyShow")) { 584 | manager.show("localcopy"); 585 | } 586 | if (c != null) 587 | c.setActiveBasePanel(frame.basePanel()); 588 | return toggleMenu; 589 | } 590 | 591 | public String getShortcutKey() { 592 | return "alt L"; 593 | } 594 | 595 | public void actionPerformed(ActionEvent e) { 596 | if (e.getSource() == toggleMenu) { 597 | manager.toggle("localcopy"); 598 | } 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /src/LocalCopyVer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | public class LocalCopyVer { 28 | static String VERSION = "2.4"; 29 | static String DEFPOLICIES = 30 | "www.jstor.org:Text within pdf file:stable%2Fpdfplus\n" + 31 | "www.jstor.org:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 32 | "www3.interscience.wiley.com:Text within pdf file:fulltext\n" + 33 | "www3.interscience.wiley.com:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 34 | "arjournals.annualreviews.org:Index of pdf file:1\n" + 35 | "arjournals.annualreviews.org:On multiple pdf files:Select+n-th+pdf+file\n" + 36 | "www.sciencedirect.com:Text within pdf link caption:%26nbsp%3B\n" + 37 | "www.sciencedirect.com:On multiple pdf files:Select+pdf+link+whose+caption+contains+a+certain+text\n" + 38 | "www.iop.org:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 39 | "www.iop.org:Text within pdf file:article\n" + 40 | "scitation.aip.org:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 41 | "scitation.aip.org:Text within pdf file:getpdf\n" + 42 | "www.springerlink.com:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 43 | "www.springerlink.com:Text within pdf file:fulltext\n" + 44 | "www.informaworld.com:On multiple pdf files:Select+pdf+file+that+contains+a+certain+text\n" + 45 | "www.informaworld.com:Text within pdf file:fulltext\n" + 46 | "www.informaworld.com:On login form and pdf available:Don%27t+log+in+and+process+pdf+list\n" + 47 | "www.iop.org:On login form and pdf available:Don%27t+log+in+and+process+pdf+list\n"; 48 | 49 | static String NEWPARAM15 = "www.sciencedirect.com:Text within pdf link caption:%26nbsp%3B\n" + 50 | "www.sciencedirect.com:On multiple pdf files:Select+pdf+link+whose+caption+contains+a+certain+text\n"; 51 | 52 | static String NEWPARAM22 = "www.sciencedirect.com:Text within pdf link title:Download\n" + 53 | "www.sciencedirect.com:On multiple pdf files:Select+pdf+link+whose+title+contains+a+certain+text\n"; 54 | 55 | static String NEWPARAM23 = "www.sciencedirect.com:Text within pdf link class:icon_pdf\n" + 56 | "www.sciencedirect.com:On multiple pdf files:Select+pdf+link+whose+class+contains+a+certain+text\n"; 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/PolicyItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import java.lang.*; 28 | 29 | public class PolicyItem { 30 | private String sel; 31 | private int val; 32 | public PolicyItem(String sel) { 33 | this.sel = sel; 34 | try { 35 | this.val = Integer.parseInt(sel); 36 | } catch (NumberFormatException e) { 37 | this.val = 0; 38 | } 39 | } 40 | public PolicyItem(int val) { 41 | this.val = val; 42 | this.sel = (new Integer(val)).toString(); 43 | } 44 | public String getSel() { 45 | return sel; 46 | } 47 | public int getVal() { 48 | return val; 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/PolicyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.*; 30 | import java.security.*; 31 | import java.util.TreeSet; 32 | import net.sf.jabref.*; 33 | import java.net.URLDecoder; 34 | import java.net.URLEncoder; 35 | import java.io.*; 36 | 37 | class MPolicy { 38 | public PolicyItem sel; 39 | public boolean remember, canceled; 40 | } 41 | 42 | class PolicyDialog extends JPanel 43 | implements ActionListener, WindowListener { 44 | 45 | private JButton okButton, cancelButton; 46 | private JComboBox cb = null; 47 | private JTextField tf = null; 48 | private JCheckBox saveP; 49 | private JDialog d; 50 | private String[] policies; 51 | 52 | public MPolicy pol = new MPolicy(); 53 | 54 | public PolicyDialog(JDialog d, String host, String desc, String[] policies, String policy) { 55 | super(new BorderLayout()); 56 | 57 | this.d = d; 58 | this.policies = policies; 59 | 60 | Globals.prefs.putDefaultValue("localcopy-policy-remember",true); 61 | pol.remember = Globals.prefs.getBoolean("localcopy-policy-remember"); 62 | pol.canceled = false; 63 | 64 | JLabel labelP = new JLabel(""); 65 | 66 | okButton = new JButton("Ok"); 67 | okButton.addActionListener(this); 68 | 69 | cancelButton = new JButton("Cancel"); 70 | cancelButton.addActionListener(this); 71 | 72 | JTextArea labelInfo = new JTextArea(4,40); 73 | labelInfo.setEditable(false); 74 | labelInfo.setBackground(Color.white); 75 | labelInfo.setText(desc); 76 | 77 | saveP = new JCheckBox("Remember policy for " + host); 78 | saveP.setSelected(pol.remember); 79 | 80 | JPanel mainPane = new JPanel(); 81 | mainPane.setLayout(new BoxLayout(mainPane,BoxLayout.LINE_AXIS)); 82 | JPanel leftPane = new JPanel(new GridLayout(3,1,10,10)); 83 | JPanel rightPane = new JPanel(new GridLayout(3,1,10,10)); 84 | 85 | mainPane.add(leftPane); 86 | mainPane.add(rightPane); 87 | 88 | JPanel buttonPane = new JPanel(new GridLayout(1,2,10,10)); 89 | buttonPane.add(okButton); 90 | buttonPane.add(cancelButton); 91 | 92 | leftPane.add(labelP); 93 | leftPane.add(new JLabel("")); 94 | leftPane.add(new JLabel("")); 95 | 96 | if (policies.length > 1) { 97 | 98 | if (policy == null) 99 | policy = policies[0]; 100 | 101 | cb = new JComboBox(); 102 | int i; 103 | cb.setEditable(false); 104 | for (i=0;i maxLength) { 151 | JOptionPane.showMessageDialog(this, 152 | "The entered text is too long by " + 153 | (s.length()-maxLength) + " character(s)!", 154 | "Error",JOptionPane.ERROR_MESSAGE); 155 | return; 156 | } 157 | pol.sel = new PolicyItem(s); 158 | } else if (policies[0].equals("#")) { 159 | int val = 0; 160 | try { 161 | val = Integer.parseInt(s); 162 | } catch (NumberFormatException e) { 163 | JOptionPane.showMessageDialog(this, 164 | "The entered value could not be interpreted as a number!", 165 | "Error",JOptionPane.ERROR_MESSAGE); 166 | return; 167 | } 168 | int min = Integer.parseInt(policies[3]); 169 | int max = Integer.parseInt(policies[4]); 170 | if (val < min) { 171 | JOptionPane.showMessageDialog(this, 172 | "The entered value is too small! Minimum: " + min, 173 | "Error",JOptionPane.ERROR_MESSAGE); 174 | return; 175 | } 176 | if (val > max) { 177 | JOptionPane.showMessageDialog(this, 178 | "The entered value is too large! Maximum: " + max, 179 | "Error",JOptionPane.ERROR_MESSAGE); 180 | return; 181 | } 182 | pol.sel = new PolicyItem(val); 183 | } 184 | } 185 | pol.remember = saveP.isSelected(); 186 | pol.canceled = false; 187 | Globals.prefs.putBoolean("localcopy-policy-remember",pol.remember); 188 | d.dispose(); 189 | } else if (evt.getSource() == cancelButton) { 190 | pol.canceled = true; 191 | d.dispose(); 192 | } 193 | } 194 | 195 | public void windowClosing(WindowEvent e) { 196 | d.dispose(); 197 | pol.canceled = true; 198 | } 199 | 200 | public void windowClosed(WindowEvent e) { 201 | } 202 | 203 | public void windowOpened(WindowEvent e) { 204 | } 205 | 206 | public void windowIconified(WindowEvent e) { 207 | } 208 | 209 | public void windowDeiconified(WindowEvent e) { 210 | } 211 | 212 | public void windowActivated(WindowEvent e) { 213 | } 214 | 215 | public void windowDeactivated(WindowEvent e) { 216 | } 217 | 218 | static MPolicy createAndShow(String host, String realm, String desc, String[] policies, String policy) { 219 | JDialog d = new JDialog((Frame)null,realm,true); 220 | PolicyDialog p = new PolicyDialog(d,host,desc,policies,policy); 221 | d.setResizable(false); 222 | d.getRootPane().setDefaultButton(p.okButton); 223 | p.setOpaque(true); 224 | d.setContentPane(p); 225 | d.addWindowListener(p); 226 | d.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 227 | d.pack(); 228 | d.setLocationRelativeTo(null); 229 | d.setVisible(true); 230 | return p.pol; 231 | } 232 | } 233 | 234 | 235 | public class PolicyManager { 236 | 237 | private static String getPref(String c,String n,String d) { 238 | if (Globals.prefs.hasKey("localcopy-policy-" + c + "-" + n)) { 239 | return new String(Base64.decode(Globals.prefs.get("localcopy-policy-" + c + "-" + n))); 240 | } 241 | return d; 242 | } 243 | 244 | private static void setPref(String c,String n,String v) { 245 | Globals.prefs.put("localcopy-policy-" + c + "-" + n,Base64.encode(v)); 246 | } 247 | 248 | private static void remPref(String c,String n) { 249 | if (Globals.prefs.hasKey("localcopy-policy-" + c + "-" + n)) { 250 | Globals.prefs.remove("localcopy-policy-" + c + "-" + n); 251 | } 252 | } 253 | 254 | private static boolean hasPref(String c,String n) { 255 | return Globals.prefs.hasKey("localcopy-policy-" + c + "-" + n); 256 | } 257 | 258 | public static PolicyItem getPolicy(String host, String realm, String desc, String sel) { 259 | String[] a = { sel }; 260 | return getPolicy(host,realm,desc,a); 261 | } 262 | 263 | public static PolicyItem getPolicy(String host, String realm, String desc, String[] sel) { 264 | String c = code(host,realm); 265 | 266 | PolicyItem it = getPolicy(host, realm); 267 | if (it != null) 268 | return it; 269 | 270 | MPolicy p = PolicyDialog.createAndShow(host,realm,desc,sel, 271 | getDefaultPolicy(host,realm)); 272 | if (p.canceled) 273 | return null; 274 | 275 | if (p.remember) 276 | setPolicy(host, realm, p.sel); 277 | return p.sel; 278 | } 279 | 280 | public static void remove(String host, String realm) { 281 | String c = code(host,realm); 282 | remPref(c,"sel"); 283 | remPref(c,"host"); 284 | remPref(c,"realm"); 285 | unregister(c); 286 | } 287 | 288 | private static String code(String host, String realm) { 289 | try { 290 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 291 | String str = host + ":" + realm; 292 | md5.reset(); 293 | md5.update(str.getBytes()); 294 | byte[] result = md5.digest(); 295 | StringBuffer hexString = new StringBuffer(); 296 | for (int i=0; i hosts = new TreeSet(); 368 | try { 369 | for (i=0;i hosts = new TreeSet(); 387 | for (i=0;i realms = new TreeSet(); 399 | for (i=0;i 0 && !codes[i].equals(c)) { 415 | newCodes += " " + codes[i]; 416 | } 417 | } 418 | Globals.prefs.put("localcopy-policy-list",newCodes); 419 | } 420 | 421 | private static void unregister(String c) { 422 | String cp = (Globals.prefs.hasKey("localcopy-policy-list"))? 423 | (Globals.prefs.get("localcopy-policy-list")):""; 424 | String[] codes = cp.split(" "); 425 | String newCodes = ""; 426 | int i; 427 | for (i=0;i 0 && !codes[i].equals(c)) { 429 | if (newCodes.length() > 0) 430 | newCodes += " "; 431 | newCodes += codes[i]; 432 | } 433 | } 434 | Globals.prefs.put("localcopy-policy-list",newCodes); 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /src/ProgressDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Christoph Lehner 3 | * 4 | * All programs in this directory and subdirectories are published under the GNU 5 | * General Public License as described below. 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * Further information about the GNU GPL is available at: 22 | * http://www.gnu.org/copyleft/gpl.ja.html 23 | * 24 | */ 25 | package localcopy; 26 | 27 | import net.sf.jabref.gui.*; 28 | import net.sf.jabref.*; 29 | 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.event.*; 33 | 34 | class ProgressDialog extends JPanel 35 | implements ActionListener, WindowListener { 36 | 37 | private JButton stopButton; 38 | private JTextArea taskOutput; 39 | private JProgressBar progressBar; 40 | private Task task; 41 | private JDialog d; 42 | private Command command; 43 | private LocalCopySidePaneComponent parent; 44 | private BibtexEntry[] bes; 45 | 46 | interface Command { 47 | boolean check(Component dialog, BasePanel panel, BibtexEntry[] bes); 48 | void run(Component dialog, BasePanel panel, BibtexEntry bes, Console io); 49 | }; 50 | 51 | class Task extends Thread implements Console { 52 | private boolean done = false, canceled = false, errors = false; 53 | private String keepConsoleString = null; 54 | 55 | public void cancel(boolean ignore) { 56 | done(); 57 | output("* please stand by while the current operation is closed down properly.\n", false); 58 | canceled = true; 59 | } 60 | 61 | public void keepConsole(String title) { 62 | keepConsoleString = title; 63 | } 64 | 65 | public void setDoneFlag() { 66 | done = true; 67 | } 68 | 69 | public boolean isCanceled() { 70 | return canceled; 71 | } 72 | 73 | public boolean isDone() { 74 | return done; 75 | } 76 | 77 | public void output(String msg, boolean berr) { 78 | if (berr) { 79 | errors = true; 80 | keepConsole("One or more errors occurred."); 81 | } 82 | taskOutput.insert(msg,0); 83 | } 84 | 85 | public void setProgressBarEnabled(boolean b) { 86 | progressBar.setEnabled(b); 87 | } 88 | 89 | public void setProgressBarIndeterminate(boolean b) { 90 | progressBar.setIndeterminate(b); 91 | } 92 | 93 | public void setProgressBarValue(int v) { 94 | progressBar.setValue(v); 95 | } 96 | 97 | public void setProgressBarMaximum(int v) { 98 | progressBar.setMaximum(v); 99 | } 100 | 101 | public void run() { 102 | 103 | String msg; 104 | int i; 105 | 106 | keepConsoleString = null; 107 | 108 | try { 109 | for (i=0;i