use IsisDB module instead of OpenIsis -- this will fix various problems in
[webpac] / openisis / org / openisis / Mail.java
1 /*
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