1 |
dpavlin |
237 |
/* |
2 |
|
|
openisis - an open implementation of the CDS/ISIS database |
3 |
|
|
Version 0.8.x (patchlevel see file Version) |
4 |
|
|
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
|
|
6 |
|
|
This library is free software; you can redistribute it and/or |
7 |
|
|
modify it under the terms of the GNU Lesser General Public |
8 |
|
|
License as published by the Free Software Foundation; either |
9 |
|
|
version 2.1 of the License, or (at your option) any later version. |
10 |
|
|
|
11 |
|
|
This library is distributed in the hope that it will be useful, |
12 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
|
|
Lesser General Public License for more details. |
15 |
|
|
|
16 |
|
|
You should have received a copy of the GNU Lesser General Public |
17 |
|
|
License along with this library; if not, write to the Free Software |
18 |
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
|
|
20 |
|
|
see README for more information |
21 |
|
|
EOH */ |
22 |
|
|
|
23 |
|
|
package org.openisis; |
24 |
|
|
|
25 |
|
|
import java.io.*; |
26 |
|
|
import java.util.*; |
27 |
|
|
|
28 |
|
|
import javax.activation.*; |
29 |
|
|
import javax.mail.*; |
30 |
|
|
import javax.mail.internet.*; |
31 |
|
|
|
32 |
|
|
|
33 |
|
|
/** |
34 |
|
|
This class contains a collection of javax.mail based E-Mail utilities. |
35 |
|
|
|
36 |
|
|
<p> |
37 |
|
|
$Id: Mail.java,v 1.4 2003/04/08 00:20:53 kripke Exp $ |
38 |
|
|
@version $Revision: 1.4 $ |
39 |
|
|
@author $Author: kripke $ |
40 |
|
|
*/ |
41 |
|
|
public abstract class Mail { |
42 |
|
|
|
43 |
|
|
public static final String EMPTY = ""; |
44 |
|
|
|
45 |
|
|
public static final String MESSAGEID = "message-id"; |
46 |
|
|
public static final String DATE = "date"; |
47 |
|
|
public static final String TO = "to"; |
48 |
|
|
public static final String CC = "cc"; |
49 |
|
|
public static final String BCC = "bcc"; |
50 |
|
|
public static final String FROM = "from"; |
51 |
|
|
public static final String SENDER = "sender"; |
52 |
|
|
public static final String REPLYTO = "reply-to"; |
53 |
|
|
public static final String SUBJECT = "subject"; |
54 |
|
|
public static final String REFERENCES = "references"; |
55 |
|
|
public static final String INREPLYTO = "in-reply-to"; |
56 |
|
|
public static final String KEYWORDS = "keywords"; |
57 |
|
|
public static final String COMMENTS = "comments"; |
58 |
|
|
public static final String ENCRYPTED = "encrypted"; |
59 |
|
|
public static final String PRECEDENCE = "precedence"; // non-standard |
60 |
|
|
public static final String XPRIORITY = "x-priority"; // non-standard |
61 |
|
|
public static final String RETURNPATH = "return-path"; |
62 |
|
|
public static final String DLVRDTO = "delivered-to"; // qmail |
63 |
|
|
public static final String RECEIVED = "received"; |
64 |
|
|
public static final String XMAILER = "x-mailer"; |
65 |
|
|
public static final String CTYPE = "content-type"; |
66 |
|
|
public static final String CTRANSENC = "content-transfer-encoding"; |
67 |
|
|
|
68 |
|
|
public static final String TEXT_PLAIN = "text/plain"; |
69 |
|
|
public static final String TEXT_ISO = "text/plain; charset=\"iso-8859-1\""; |
70 |
|
|
public static final String TEXT_HTML = "text/html"; |
71 |
|
|
public static final String MESS_MIME = "message/rfc822"; |
72 |
|
|
public static final String MULT_MIXED = "multipart/mixed"; |
73 |
|
|
public static final String MULT_ALT = "multipart/alternative"; |
74 |
|
|
public static final String MULT_APPLE = "multipart/appledouble"; |
75 |
|
|
public static final String MULT_SIGN = "multipart/signed"; |
76 |
|
|
public static final String MULT_ANY = "multipart/*"; |
77 |
|
|
public static final String APPL_WORD = "application/msword"; |
78 |
|
|
public static final String APPL_PDF = "application/pdf"; |
79 |
|
|
public static final String APPL_RTF = "application/rtf"; |
80 |
|
|
public static final String APPL_BYTES = "application/octet-stream"; |
81 |
|
|
public static final String APPL_APPLE = "application/applefile"; |
82 |
|
|
public static final String APPL_ANY = "application/*"; |
83 |
|
|
public static final String IMAG_GIF = "image/gif"; |
84 |
|
|
public static final String IMAG_JPEG = "image/jpeg"; |
85 |
|
|
public static final String IMAG_ANY = "image/*"; |
86 |
|
|
|
87 |
|
|
public static final String SMTP_HOST = "mail.smtp.host"; |
88 |
|
|
|
89 |
|
|
public static final Session SES; |
90 |
|
|
static { |
91 |
|
|
if ( null == System.getProperty( SMTP_HOST ) ) |
92 |
|
|
System.setProperty( SMTP_HOST, "localhost" ); |
93 |
|
|
SES = Session.getDefaultInstance( System.getProperties(), null ); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
|
97 |
|
|
public static interface Source { |
98 |
|
|
Message[] get () throws Exception; |
99 |
|
|
void done ( boolean expunge ) throws Exception; |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
public static interface Sink { |
103 |
|
|
void put ( Message m ) throws Exception; |
104 |
|
|
void put ( Message[] m ) throws Exception; |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
|
108 |
|
|
// ////////////////////////////////////////////////// |
109 |
|
|
// utilities |
110 |
|
|
// |
111 |
|
|
|
112 |
|
|
public static void del ( Message m ) |
113 |
|
|
throws Exception |
114 |
|
|
{ |
115 |
|
|
m.setFlag( Flags.Flag.DELETED, true ); |
116 |
|
|
} // del |
117 |
|
|
|
118 |
|
|
public static void del ( Message[] m ) |
119 |
|
|
throws Exception |
120 |
|
|
{ |
121 |
|
|
for ( int i=0; i<m.length; i++ ) |
122 |
|
|
if ( null != m[i] ) |
123 |
|
|
m[i].setFlag( Flags.Flag.DELETED, true ); |
124 |
|
|
} // del |
125 |
|
|
|
126 |
|
|
|
127 |
|
|
public static void put ( Sink s, Message[] m ) |
128 |
|
|
throws Exception |
129 |
|
|
{ |
130 |
|
|
for ( int i=0; i<m.length; i++ ) |
131 |
|
|
if ( null != m[i] ) |
132 |
|
|
s.put( m[i] ); |
133 |
|
|
} // put |
134 |
|
|
|
135 |
|
|
public static int move ( Sink si, Source so ) |
136 |
|
|
throws Exception |
137 |
|
|
{ |
138 |
|
|
Message[] m; |
139 |
|
|
int copied = 0; |
140 |
|
|
while ( null != (m = so.get()) && 0 < m.length ) { |
141 |
|
|
si.put( m ); |
142 |
|
|
del( m ); |
143 |
|
|
so.done( true ); |
144 |
|
|
copied += m.length; |
145 |
|
|
} |
146 |
|
|
return copied; |
147 |
|
|
} // move |
148 |
|
|
|
149 |
|
|
public static String head ( Part p, String name, String def ) |
150 |
|
|
throws Exception |
151 |
|
|
{ |
152 |
|
|
String[] hh = p.getHeader( name ); |
153 |
|
|
return null == hh || 0 == hh.length ? def : hh[0]; |
154 |
|
|
} // head |
155 |
|
|
|
156 |
|
|
public static String head ( Part p, String name ) |
157 |
|
|
throws Exception |
158 |
|
|
{ |
159 |
|
|
return head( p, name, null ); |
160 |
|
|
} // head |
161 |
|
|
|
162 |
|
|
// join lines, commonly used for content type |
163 |
|
|
public static String oneline ( String ct ) |
164 |
|
|
{ |
165 |
|
|
return null == ct ? null : ct.replace( '\n', '\t' ).replace( '\r', ' ' ); |
166 |
|
|
} // oneline |
167 |
|
|
|
168 |
|
|
public static Message message ( String[] headers, String body ) |
169 |
|
|
throws Exception |
170 |
|
|
{ |
171 |
|
|
MimeMessage mm = new MimeMessage( SES ); |
172 |
|
|
for ( int i=0; i<headers.length; i+=2 ) |
173 |
|
|
mm.addHeader( headers[i], headers[i+1] ); |
174 |
|
|
mm.addHeader( CTYPE, TEXT_ISO ); |
175 |
|
|
mm.setContent( body, TEXT_ISO ); |
176 |
|
|
if ( null == mm.getSentDate() ) |
177 |
|
|
mm.setSentDate( new Date() ); |
178 |
|
|
return mm; |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
// ////////////////////////////////////////////////// |
182 |
|
|
// implementations |
183 |
|
|
// |
184 |
|
|
|
185 |
|
|
public static class Pop implements Source { |
186 |
|
|
static final FetchProfile FPEMPTY = new FetchProfile(); |
187 |
|
|
Store _store; |
188 |
|
|
Folder _f; |
189 |
|
|
String _h; |
190 |
|
|
int _o; |
191 |
|
|
String _u; |
192 |
|
|
String _p; |
193 |
|
|
int _max; |
194 |
|
|
|
195 |
|
|
public Pop ( String host, int port, String user, String pass, int max ) |
196 |
|
|
throws Exception |
197 |
|
|
{ |
198 |
|
|
_store = SES.getStore( "pop3" ); |
199 |
|
|
_h = host; |
200 |
|
|
_o = port; |
201 |
|
|
_u = user; |
202 |
|
|
_p = pass; |
203 |
|
|
_max = max; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
public Pop ( String host, String user, String pass, int max ) |
207 |
|
|
throws Exception |
208 |
|
|
{ |
209 |
|
|
this( host, 110, user, pass, max ); |
210 |
|
|
} |
211 |
|
|
|
212 |
|
|
public Pop ( String host, String user, String pass ) |
213 |
|
|
throws Exception |
214 |
|
|
{ |
215 |
|
|
this( host, user, pass, 0 ); |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
public Message[] get () |
219 |
|
|
throws Exception |
220 |
|
|
{ |
221 |
|
|
_store.connect( _h, _o, _u, _p ); |
222 |
|
|
_f = _store.getFolder( "INBOX" ); // the magic default folder |
223 |
|
|
_f.open( Folder.READ_WRITE ); |
224 |
|
|
Message[] m = _f.getMessages(); |
225 |
|
|
System.err.println( "pop "+_u+"@"+_h+":"+_o+" new " |
226 |
|
|
+ (null==m ? -1 : m.length) ); |
227 |
|
|
if ( null == m ) |
228 |
|
|
return null; |
229 |
|
|
if ( 0 != _max && _max < m.length ) { |
230 |
|
|
Message[] mm = new Message[_max]; |
231 |
|
|
System.arraycopy( m, 0, mm, 0, _max ); |
232 |
|
|
m = mm; |
233 |
|
|
} |
234 |
|
|
_f.fetch( m, FPEMPTY ); |
235 |
|
|
System.err.println( "pop "+_u+"@"+_h+":"+_o+" got "+m.length ); |
236 |
|
|
return m; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
public void done ( boolean expunge ) |
240 |
|
|
throws Exception |
241 |
|
|
{ |
242 |
|
|
if ( null != _f ) { |
243 |
|
|
_f.close( expunge ); |
244 |
|
|
_f = null; |
245 |
|
|
} |
246 |
|
|
_store.close(); |
247 |
|
|
} |
248 |
|
|
} // class Pop |
249 |
|
|
|
250 |
|
|
|
251 |
|
|
public static class Dir implements Source, Sink { // the Maildir |
252 |
|
|
final String _t; |
253 |
|
|
final String _n; |
254 |
|
|
final String _c; |
255 |
|
|
final File _ndir; |
256 |
|
|
int _lim; |
257 |
|
|
|
258 |
|
|
Msg[] got; |
259 |
|
|
|
260 |
|
|
class Msg extends MimeMessage { |
261 |
|
|
final File _f; |
262 |
|
|
Msg ( File f ) |
263 |
|
|
throws Exception |
264 |
|
|
{ |
265 |
|
|
super( SES, new FileInputStream( f ) ); |
266 |
|
|
_f = f; |
267 |
|
|
System.err.println( "file "+f.getName() |
268 |
|
|
+": "+head( this, MESSAGEID, "<?>" ) ); |
269 |
|
|
} |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
public Dir ( String base ) |
273 |
|
|
throws Exception |
274 |
|
|
{ |
275 |
|
|
_t = base + File.separator + "tmp"; |
276 |
|
|
_n = base + File.separator + "new"; |
277 |
|
|
_c = base + File.separator + "cur"; |
278 |
|
|
_ndir = new File(_n); |
279 |
|
|
File t = new File(_t); |
280 |
|
|
File c = new File(_c); |
281 |
|
|
if ( ! t.isDirectory() && ! t.mkdirs() ) |
282 |
|
|
throw new FileNotFoundException( _t ); |
283 |
|
|
if ( ! _ndir.isDirectory() && ! _ndir.mkdirs() ) |
284 |
|
|
throw new FileNotFoundException( _n ); |
285 |
|
|
if ( ! c.isDirectory() && ! c.mkdirs() ) |
286 |
|
|
throw new FileNotFoundException( _c ); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
public String toString () { |
290 |
|
|
return "Dir "+_ndir.getParentFile().getAbsolutePath(); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
|
294 |
|
|
public Message[] get () throws Exception |
295 |
|
|
{ |
296 |
|
|
File[] l = _ndir.listFiles(); |
297 |
|
|
System.err.println( _ndir+": "+l.length ); |
298 |
|
|
int i = l.length; |
299 |
|
|
if ( 0 != _lim && i > _lim ) |
300 |
|
|
i = _lim; |
301 |
|
|
got = new Msg[i]; |
302 |
|
|
while ( 0 < i-- ) { |
303 |
|
|
File f = new File( _t+File.separator+l[i].getName() ); |
304 |
|
|
l[i].renameTo( f ); |
305 |
|
|
got[i] = new Msg( f ); |
306 |
|
|
} |
307 |
|
|
return got; |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
public void done ( boolean expunge ) throws Exception |
311 |
|
|
{ |
312 |
|
|
if ( expunge && null != got ) |
313 |
|
|
for ( int i=0; i<got.length; i++ ) |
314 |
|
|
if ( got[i].isSet( Flags.Flag.DELETED ) ) |
315 |
|
|
got[i]._f.delete(); |
316 |
|
|
got = null; |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
|
320 |
|
|
public void put ( Message m ) |
321 |
|
|
throws Exception |
322 |
|
|
{ |
323 |
|
|
File f; |
324 |
|
|
String s = File.separator+System.currentTimeMillis(); |
325 |
|
|
while ( ! (f = new File( _t + s )).createNewFile() ) |
326 |
|
|
s += (int)(10*Math.random()) % 10; |
327 |
|
|
OutputStream o = new FileOutputStream(f); |
328 |
|
|
m.writeTo( o ); |
329 |
|
|
o.close(); |
330 |
|
|
while ( ! f.renameTo(new File( _n + s )) ) |
331 |
|
|
s += (int)(10*Math.random()) % 10; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
public void put ( Message[] m ) |
335 |
|
|
throws Exception |
336 |
|
|
{ |
337 |
|
|
Mail.put( this, m ); |
338 |
|
|
} |
339 |
|
|
} // class Dir |
340 |
|
|
|
341 |
|
|
|
342 |
|
|
public static class Arc implements Sink { // the archive |
343 |
|
|
static final String OK = "ok"; |
344 |
|
|
static final Map OKHDR = new HashMap(); |
345 |
|
|
static { |
346 |
|
|
OKHDR.put( MESSAGEID, OK ); |
347 |
|
|
OKHDR.put( DATE, OK ); |
348 |
|
|
OKHDR.put( FROM, OK ); |
349 |
|
|
OKHDR.put( REPLYTO, OK ); |
350 |
|
|
OKHDR.put( SUBJECT, OK ); |
351 |
|
|
OKHDR.put( REFERENCES, OK ); |
352 |
|
|
OKHDR.put( INREPLYTO, OK ); |
353 |
|
|
OKHDR.put( KEYWORDS, OK ); |
354 |
|
|
OKHDR.put( COMMENTS, OK ); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
final String _u; |
358 |
|
|
final String _b; |
359 |
|
|
final Sink _fwd; |
360 |
|
|
|
361 |
|
|
final int _maxlines = 60; |
362 |
|
|
final int _minlines = 15; |
363 |
|
|
final boolean _zip = true; |
364 |
|
|
|
365 |
|
|
|
366 |
|
|
/** path is /m/d/h/xxx. |
367 |
|
|
january is Calendar.JANUARY, which is 0 |
368 |
|
|
*/ |
369 |
|
|
public static String path ( int mon, int day, int hour, int min, int sec ) |
370 |
|
|
{ |
371 |
|
|
char mo = (char)('a'+mon); |
372 |
|
|
char d = (char)(day + (day<10 ? '0' : 'a'-10)); |
373 |
|
|
char h = (char)('a'+hour); |
374 |
|
|
int hs = 60*min + sec; |
375 |
|
|
sec = hs - 100*(min = hs / 100); |
376 |
|
|
char m = (char)(min + (min<10 ? '0' : 'a'-10)); |
377 |
|
|
return File.separator+mo + File.separator+d +File.separator+h |
378 |
|
|
+ File.separator+m+(char)('0'+sec/10)+(char)('0'+sec%10); |
379 |
|
|
} // path |
380 |
|
|
|
381 |
|
|
public static String path ( Calendar c ) |
382 |
|
|
{ |
383 |
|
|
return path( c.get( Calendar.MONTH ), c.get( Calendar.DATE ), |
384 |
|
|
c.get( Calendar.HOUR_OF_DAY ), c.get( Calendar.MINUTE ), |
385 |
|
|
c.get( Calendar.SECOND ) ); |
386 |
|
|
} // path |
387 |
|
|
|
388 |
|
|
public static String path ( Date d ) |
389 |
|
|
{ |
390 |
|
|
Calendar c = new GregorianCalendar(); |
391 |
|
|
c.setTime( d ); |
392 |
|
|
return path( c ); |
393 |
|
|
} // path |
394 |
|
|
|
395 |
|
|
public static String path () |
396 |
|
|
{ |
397 |
|
|
return path( new GregorianCalendar() ); |
398 |
|
|
} // path |
399 |
|
|
|
400 |
|
|
|
401 |
|
|
public Arc ( String base, String url, Sink fwd ) |
402 |
|
|
throws Exception |
403 |
|
|
{ |
404 |
|
|
_b = base; |
405 |
|
|
_u = url; |
406 |
|
|
_fwd = fwd; |
407 |
|
|
File b = new File(_b); |
408 |
|
|
if ( ! b.isDirectory() && ! b.mkdirs() ) |
409 |
|
|
throw new FileNotFoundException( _b ); |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
public String toString () { |
413 |
|
|
return "Arc "+_b+" "+_u; |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
public void put ( Message m ) |
417 |
|
|
throws Exception |
418 |
|
|
{ |
419 |
|
|
String mid = head( m, MESSAGEID, "<?>" ); |
420 |
|
|
System.err.println( _b+" += "+mid ); |
421 |
|
|
Date d = m.getSentDate(); |
422 |
|
|
if ( null == d ) |
423 |
|
|
d = m.getReceivedDate(); |
424 |
|
|
if ( null == d ) { |
425 |
|
|
System.err.println( "warning: no date found in "+mid ); |
426 |
|
|
d = new Date(); |
427 |
|
|
} |
428 |
|
|
String base = path( d ); |
429 |
|
|
String path = base; |
430 |
|
|
File f = new File( _b + path ); |
431 |
|
|
File dir = f.getParentFile(); |
432 |
|
|
if ( ! dir.isDirectory() && ! dir.mkdirs() ) |
433 |
|
|
throw new FileNotFoundException( dir.getAbsolutePath() ); |
434 |
|
|
for ( int i=0; ! f.createNewFile(); i++ ) |
435 |
|
|
f = new File( _b + (path = base + Integer.toString( i, 36 )) ); |
436 |
|
|
String name = f.getName(); |
437 |
|
|
// got unique path path |
438 |
|
|
System.out.println( "new message "+mid+" date "+d+" -> "+path ); |
439 |
|
|
MimeMessage mm = new MimeMessage( SES ); |
440 |
|
|
Enumeration e = m.getAllHeaders(); |
441 |
|
|
while ( e.hasMoreElements() ) { |
442 |
|
|
Header h = (Header)e.nextElement(); |
443 |
|
|
String n = h.getName().toLowerCase(); |
444 |
|
|
if ( OK == OKHDR.get(n) ) |
445 |
|
|
mm.addHeader( n, h.getValue() ); |
446 |
|
|
} |
447 |
|
|
mm.addHeader( CTYPE, TEXT_ISO ); |
448 |
|
|
mm.addHeader( CTRANSENC, "8bit" ); |
449 |
|
|
StringBuffer b = new StringBuffer( 4096 ); |
450 |
|
|
Stack s = new Stack(); |
451 |
|
|
s.push( m ); |
452 |
|
|
int i = 0; |
453 |
|
|
int totlines = 0; |
454 |
|
|
List binparts = new ArrayList( 32 ); |
455 |
|
|
while ( ! s.isEmpty() ) { |
456 |
|
|
Part p = (Part)s.pop(); |
457 |
|
|
String ct = oneline( p.getContentType() ); |
458 |
|
|
// ct = null == ct ? TEXT_PLAIN : ct.toLowerCase(); |
459 |
|
|
|
460 |
|
|
if ( p.isMimeType( MULT_ANY ) ) { |
461 |
|
|
Object content = p.getContent(); |
462 |
|
|
if ( ! (content instanceof Multipart) ) { |
463 |
|
|
// ignore |
464 |
|
|
System.err.println( "multipart type "+ct |
465 |
|
|
+" has class "+content.getClass().getName() ); |
466 |
|
|
mm.addHeader( COMMENTS, "dropped bad multipart "+ct ); |
467 |
|
|
} else { |
468 |
|
|
Multipart mu = (Multipart)content; |
469 |
|
|
int cnt = mu.getCount(); |
470 |
|
|
if ( cnt > 8 ) { |
471 |
|
|
System.err.println( "multipart with too many parts: "+cnt ); |
472 |
|
|
cnt = 8; |
473 |
|
|
} |
474 |
|
|
Part[] pu = new Part[cnt]; |
475 |
|
|
for ( int j=0; j<cnt; j++ ) |
476 |
|
|
pu[j] = mu.getBodyPart( j ); |
477 |
|
|
if ( p.isMimeType( MULT_ALT ) |
478 |
|
|
|| p.isMimeType( MULT_SIGN ) |
479 |
|
|
) { // alternatives suggested |
480 |
|
|
int select = -1; |
481 |
|
|
for ( int j=0; j<cnt; j++ ) // see wether we can decide |
482 |
|
|
if ( pu[j].isMimeType( TEXT_PLAIN ) ) { |
483 |
|
|
select = j; |
484 |
|
|
break; |
485 |
|
|
} |
486 |
|
|
if ( 0 <= select ) { |
487 |
|
|
for ( int j=0; j<cnt; j++ ) |
488 |
|
|
if ( select != j ) |
489 |
|
|
mm.addHeader( COMMENTS, "dropped alternative "+ |
490 |
|
|
oneline( pu[j].getContentType() ) + " for "+i ); |
491 |
|
|
if ( 0 != select ) |
492 |
|
|
pu[0] = pu[select]; |
493 |
|
|
cnt = 1; |
494 |
|
|
} |
495 |
|
|
} else if ( p.isMimeType( MULT_APPLE ) // apple ... |
496 |
|
|
&& 2 == cnt // ... double |
497 |
|
|
) { // kill ressource fork |
498 |
|
|
int j = 0; |
499 |
|
|
if ( pu[0].isMimeType( APPL_APPLE ) |
500 |
|
|
|| pu[j=1].isMimeType( APPL_APPLE ) |
501 |
|
|
) { |
502 |
|
|
if ( 0 == j ) |
503 |
|
|
pu[0] = pu[1]; |
504 |
|
|
mm.addHeader( COMMENTS, "dropped appledouble" ); |
505 |
|
|
cnt = 1; |
506 |
|
|
} |
507 |
|
|
} |
508 |
|
|
for ( int j=cnt; j-- > 0; ) // push parts backwards |
509 |
|
|
s.push( pu[j] ); |
510 |
|
|
} |
511 |
|
|
continue; |
512 |
|
|
} // multi |
513 |
|
|
|
514 |
|
|
i++; // about to create part i |
515 |
|
|
String part = path+'-'+i; |
516 |
|
|
|
517 |
|
|
// if ( ! p.isMimeType( TEXT_PLAIN ) ) { |
518 |
|
|
/* |
519 |
|
|
it's so awfully unreliable ... many RTFs go as APPL_WORD ... |
520 |
|
|
if ( p.isMimeType( TEXT_HTML ) ) |
521 |
|
|
ext = ".html"; |
522 |
|
|
else if ( p.isMimeType( APPL_WORD ) ) |
523 |
|
|
ext = ".doc"; |
524 |
|
|
else |
525 |
|
|
ext = ""; |
526 |
|
|
*/ |
527 |
|
|
OutputStream o = new FileOutputStream( _b+part ); |
528 |
|
|
p.getDataHandler().writeTo( o ); |
529 |
|
|
o.close(); |
530 |
|
|
// can we convert it ? |
531 |
|
|
String[] args = new String[] { "any2txt", "-v", name+'-'+i }; |
532 |
|
|
Process fix = Runtime.getRuntime().exec( args, null, dir ); |
533 |
|
|
BufferedReader err = new BufferedReader( new InputStreamReader( |
534 |
|
|
fix.getErrorStream() ) ); |
535 |
|
|
String line; |
536 |
|
|
while ( null != (line = err.readLine()) ) |
537 |
|
|
System.err.println( "any2txt:\t"+line ); |
538 |
|
|
err.close(); |
539 |
|
|
fix.waitFor(); |
540 |
|
|
int ret = fix.exitValue(); |
541 |
|
|
String ext = EMPTY; |
542 |
|
|
boolean havetext = 0 != (ret & 1) || 0 == ret; |
543 |
|
|
int type = ret & ~1; |
544 |
|
|
switch ( type ) { |
545 |
|
|
case 0: ext = ".txt"; break; |
546 |
|
|
case 2: ext = ".html"; break; |
547 |
|
|
case 4: ext = ".pdf"; break; |
548 |
|
|
case 6: ext = ".rtf"; break; |
549 |
|
|
case 8: ext = ".doc"; break; |
550 |
|
|
case 16: // mail -- reread and start over |
551 |
|
|
try { |
552 |
|
|
s.push( new MimeMessage( SES, new FileInputStream( _b+part ) ) ); |
553 |
|
|
// TODO: delete |
554 |
|
|
continue; |
555 |
|
|
} catch (Exception ee) { |
556 |
|
|
System.err.println( "could not reload mail "+mid+"["+i+"] "+ct |
557 |
|
|
+" from "+part ); |
558 |
|
|
ee.printStackTrace(); |
559 |
|
|
} |
560 |
|
|
ext = ".eml"; |
561 |
|
|
break; |
562 |
|
|
case 32: ext = ".gif"; break; |
563 |
|
|
case 34: ext = ".jpg"; break; |
564 |
|
|
case 36: ext = ".bmp"; break; |
565 |
|
|
case 38: ext = ".png"; break; |
566 |
|
|
case 40: ext = ".tiff"; break; |
567 |
|
|
} |
568 |
|
|
System.err.println( mid+"["+i+"] "+ct+" is "+ext+" ("+ret+")" ); |
569 |
|
|
|
570 |
|
|
if ( EMPTY != ext && 1 != ret ) // rename |
571 |
|
|
(new File(_b+part)).renameTo( new File(_b+part+ext) ); |
572 |
|
|
|
573 |
|
|
if ( 0 != type ) { |
574 |
|
|
binparts.add( name+'-'+i+ext ); |
575 |
|
|
// mm.addHeader( COMMENTS, "part "+i+" "+ct+" is "+part ); |
576 |
|
|
if ( ! havetext ) { |
577 |
|
|
if ( ! _zip ) { |
578 |
|
|
if ( 0 < totlines ) |
579 |
|
|
b.append( "\r\n---\r\n\r\n" ); |
580 |
|
|
b.append( "\r\nget part "+i+" at "+_u+part+ext+"\r\n" ); |
581 |
|
|
} |
582 |
|
|
totlines++; |
583 |
|
|
continue; |
584 |
|
|
} |
585 |
|
|
} |
586 |
|
|
// } |
587 |
|
|
|
588 |
|
|
// now _b+part.txt contains plaintext |
589 |
|
|
File plain = new File(_b+part+".txt"); |
590 |
|
|
BufferedReader br = new BufferedReader( |
591 |
|
|
new InputStreamReader( new FileInputStream( plain ) ) ); |
592 |
|
|
int lines = 0; |
593 |
|
|
int max = _maxlines - totlines; |
594 |
|
|
if ( max < _minlines ) |
595 |
|
|
max = _minlines; |
596 |
|
|
// String line; |
597 |
|
|
if ( 0 < totlines ) |
598 |
|
|
b.append( "\r\n---\r\n\r\n" ); |
599 |
|
|
while ( null != (line = br.readLine()) ) { |
600 |
|
|
b.append( line ).append( "\r\n" ); |
601 |
|
|
if ( max == ++lines ) { |
602 |
|
|
mm.addHeader( COMMENTS, "plaintext "+i+" continued at "+part ); |
603 |
|
|
b.append( "... read more: "+_u+part+".txt (" |
604 |
|
|
+(1 + plain.length()/1024)+" KB)\r\n" ); |
605 |
|
|
break; |
606 |
|
|
} |
607 |
|
|
} |
608 |
|
|
if ( 0 != type && !_zip ) |
609 |
|
|
b.append( "\r\nget original part "+i+" at "+_u+part+ext+"\r\n" ); |
610 |
|
|
totlines += lines; |
611 |
|
|
br.close(); |
612 |
|
|
} |
613 |
|
|
int nparts = binparts.size(); |
614 |
|
|
if ( _zip && 0 != nparts ) { |
615 |
|
|
String[] args = new String[3+nparts]; |
616 |
|
|
int a=0; |
617 |
|
|
args[a++] = "zip"; |
618 |
|
|
args[a++] = "-m"; // move |
619 |
|
|
args[a++] = name+".zip"; |
620 |
|
|
for ( int p = 0; p<nparts; p++ ) |
621 |
|
|
args[a++] = (String)binparts.get(p); |
622 |
|
|
Process zip = Runtime.getRuntime().exec( |
623 |
|
|
args, null, dir ); |
624 |
|
|
BufferedReader out = new BufferedReader( new InputStreamReader( |
625 |
|
|
zip.getInputStream() ) ); |
626 |
|
|
String line; |
627 |
|
|
while ( null != (line = out.readLine()) ) |
628 |
|
|
b.append( line+"\r\n" ); |
629 |
|
|
// System.err.println( "zip:\t"+line ); |
630 |
|
|
out.close(); |
631 |
|
|
zip.waitFor(); |
632 |
|
|
int ret = zip.exitValue(); |
633 |
|
|
File zipped = new File(_b+path+".zip"); |
634 |
|
|
if ( 0 != ret ) |
635 |
|
|
System.err.println( "zip exited "+ret ); |
636 |
|
|
else if ( ! zipped.exists() ) |
637 |
|
|
System.err.println( "no zipfile "+zipped ); |
638 |
|
|
else |
639 |
|
|
b.append( "\r\nget original parts at "+_u+path+".zip (" |
640 |
|
|
+(1 + zipped.length()/1024)+" KB)\r\n" ); |
641 |
|
|
} |
642 |
|
|
mm.setContent( b.toString(), TEXT_ISO ); |
643 |
|
|
OutputStream o = new FileOutputStream(f); |
644 |
|
|
mm.writeTo( o ); |
645 |
|
|
o.close(); |
646 |
|
|
f.renameTo( new File( f.getPath()+".mail" ) ); |
647 |
|
|
if ( null != _fwd ) |
648 |
|
|
_fwd.put( mm ); |
649 |
|
|
} |
650 |
|
|
|
651 |
|
|
public void put ( Message[] m ) |
652 |
|
|
throws Exception |
653 |
|
|
{ |
654 |
|
|
Mail.put( this, m ); |
655 |
|
|
} |
656 |
|
|
} // class Arc |
657 |
|
|
|
658 |
|
|
|
659 |
|
|
public static class Smtp implements Sink { |
660 |
|
|
|
661 |
|
|
Transport _t; |
662 |
|
|
Address[] _a; |
663 |
|
|
boolean _conn; |
664 |
|
|
|
665 |
|
|
public Smtp ( Session s, Address[] a ) throws Exception { |
666 |
|
|
_t = s.getTransport( s.getProvider("smtp") ); |
667 |
|
|
_a = a; |
668 |
|
|
} |
669 |
|
|
|
670 |
|
|
public Smtp ( Address[] a ) throws Exception { |
671 |
|
|
this( SES, a ); |
672 |
|
|
} |
673 |
|
|
|
674 |
|
|
public Smtp ( String a ) throws Exception { |
675 |
|
|
this( SES, new Address[] { new InternetAddress(a) } ); |
676 |
|
|
} |
677 |
|
|
|
678 |
|
|
/** connect. return true, iff we weren't already connected. */ |
679 |
|
|
public boolean connect () throws Exception { |
680 |
|
|
if ( _conn ) |
681 |
|
|
return false; |
682 |
|
|
_t.connect(); |
683 |
|
|
return _conn = true; |
684 |
|
|
} |
685 |
|
|
|
686 |
|
|
public void close () throws Exception { |
687 |
|
|
if ( _conn ) { |
688 |
|
|
_t.close(); |
689 |
|
|
_conn = false; |
690 |
|
|
} |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
public void put ( Message m ) |
694 |
|
|
throws Exception |
695 |
|
|
{ |
696 |
|
|
boolean nconn = connect(); |
697 |
|
|
_t.sendMessage( m, null != _a ? _a : m.getAllRecipients() ); |
698 |
|
|
if ( nconn ) close(); |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
public void put ( Message[] m ) |
702 |
|
|
throws Exception |
703 |
|
|
{ |
704 |
|
|
boolean nconn = connect(); |
705 |
|
|
Mail.put( this, m ); |
706 |
|
|
if ( nconn ) close(); |
707 |
|
|
} |
708 |
|
|
} // class Smtp |
709 |
|
|
|
710 |
|
|
|
711 |
|
|
public static class Cmd implements Sink { |
712 |
|
|
|
713 |
|
|
String _cmd; |
714 |
|
|
|
715 |
|
|
public Cmd ( String cmd ) { |
716 |
|
|
_cmd = cmd; |
717 |
|
|
} |
718 |
|
|
|
719 |
|
|
public void put ( Message m ) |
720 |
|
|
throws Exception |
721 |
|
|
{ |
722 |
|
|
Process run = Runtime.getRuntime().exec( _cmd ); |
723 |
|
|
OutputStream o = run.getOutputStream(); |
724 |
|
|
m.writeTo( o ); |
725 |
|
|
o.close(); |
726 |
|
|
BufferedReader err = new BufferedReader( new InputStreamReader( |
727 |
|
|
run.getErrorStream() ) ); |
728 |
|
|
String line; |
729 |
|
|
while ( null != (line = err.readLine()) ) |
730 |
|
|
System.err.println( _cmd+":\t"+line ); |
731 |
|
|
err.close(); |
732 |
|
|
run.waitFor(); |
733 |
|
|
int ret = run.exitValue(); |
734 |
|
|
if ( 0 != ret ) |
735 |
|
|
System.err.println( "Cmd '"+_cmd+"' exited "+ret ); |
736 |
|
|
} |
737 |
|
|
|
738 |
|
|
public void put ( Message[] m ) |
739 |
|
|
throws Exception |
740 |
|
|
{ |
741 |
|
|
Mail.put( this, m ); |
742 |
|
|
} |
743 |
|
|
} // class Cmd |
744 |
|
|
|
745 |
|
|
|
746 |
|
|
public static void main ( String[] args ) |
747 |
|
|
throws Exception |
748 |
|
|
{ |
749 |
|
|
String host = "localhost"; |
750 |
|
|
int port = 110; |
751 |
|
|
String user = "test"; |
752 |
|
|
String pass = "test"; |
753 |
|
|
String sodir = "/tmp/test"; |
754 |
|
|
String sidir = "/tmp/test"; |
755 |
|
|
String url = "http://openisis.org/archive"; |
756 |
|
|
int a = 0; |
757 |
|
|
Source so = null; |
758 |
|
|
Sink si = null; |
759 |
|
|
Sink fwd = null; |
760 |
|
|
char source = 'd'; |
761 |
|
|
char sink = 'd'; |
762 |
|
|
char task = 0; |
763 |
|
|
int n = 0; |
764 |
|
|
|
765 |
|
|
while ( a<args.length ) { |
766 |
|
|
String arg = args[a++]; |
767 |
|
|
if ( "-h".equals( arg ) ) |
768 |
|
|
host = args[a++]; |
769 |
|
|
else if ( "-o".equals( arg ) ) |
770 |
|
|
port = Integer.parseInt( args[a++] ); |
771 |
|
|
else if ( "-u".equals( arg ) ) |
772 |
|
|
user = args[a++]; |
773 |
|
|
else if ( "-p".equals( arg ) ) |
774 |
|
|
pass = args[a++]; |
775 |
|
|
else if ( "-pop".equals( arg ) ) |
776 |
|
|
source = 'p'; |
777 |
|
|
else if ( "-url".equals( arg ) ) // arc |
778 |
|
|
url = args[a++]; |
779 |
|
|
else if ( "-fwd".equals( arg ) ) // fwd by smtp |
780 |
|
|
fwd = new Smtp( args[a++] ); |
781 |
|
|
else if ( "-fwdcmd".equals( arg ) ) // fwd by command |
782 |
|
|
fwd = new Cmd( args[a++] ); |
783 |
|
|
else if ( "-dir".equals( arg ) ) { |
784 |
|
|
source = 'd'; |
785 |
|
|
sodir = args[a++]; |
786 |
|
|
} else if ( "-todir".equals( arg ) ) { |
787 |
|
|
sink = 'd'; |
788 |
|
|
sidir = args[a++]; |
789 |
|
|
} else if ( "-toarc".equals( arg ) ) { |
790 |
|
|
sink = 'a'; |
791 |
|
|
sidir = args[a++]; |
792 |
|
|
} else if ( "-move".equals( arg ) ) |
793 |
|
|
task = 'v'; |
794 |
|
|
else |
795 |
|
|
throw new IllegalArgumentException( arg ); |
796 |
|
|
} |
797 |
|
|
|
798 |
|
|
switch ( source ) { |
799 |
|
|
case 'p': |
800 |
|
|
so = new Pop( host, port, user, pass, 8 ); |
801 |
|
|
break; |
802 |
|
|
default: // 'd' |
803 |
|
|
so = new Dir( sodir ); |
804 |
|
|
} |
805 |
|
|
switch ( sink ) { |
806 |
|
|
case 'a': |
807 |
|
|
si = new Arc( sidir, url, fwd ); |
808 |
|
|
break; |
809 |
|
|
default: // 'd' |
810 |
|
|
si = new Dir( sidir ); |
811 |
|
|
} |
812 |
|
|
|
813 |
|
|
switch ( task ) { |
814 |
|
|
case 'v': // -move |
815 |
|
|
n = move( si, so ); |
816 |
|
|
System.err.println( "moved "+n+" messages from "+so+" to "+si ); |
817 |
|
|
break; |
818 |
|
|
} |
819 |
|
|
} |
820 |
|
|
} // Mail |