├── 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