Settings.java
001 /*
002  * This class is part of DocWhatsUp, a bug profiling and -reporting lib.
003  
004  * Copyright (C)
005  
006  * This library is free software; you can redistribute it and/or modify it
007  * under the terms of the GNU General Public License as published by the
008  * Free Software Foundation; either version 2 of the License, or (at your
009  * option) any later version.
010  
011  * This program is distributed in the hope that it will be useful, but
012  * WITHOUT ANY WARRANTY; without even the implied warranty of
013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
014  * Public License for more details.
015  *  
016  * You should have received a copy of the GNU General Public License along
017  * with this program; if not, write to the Free Software Foundation, Inc.,
018  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019  
020  * EXTENSION:
021  * Linking DocWhatsUp statically or dynamically with other modules is making a
022  * combined work based on DocWhatsUp.  Thus, the terms and conditions of the 
023  * GNU General Public License cover the whole combination.
024  *
025  * As a special exception, the copyright holder of DocWhatsUp give you
026  * permission to link DocWhatsUp with independent modules that communicate with
027  * DocWhatsUp solely through the DocWhatsUp.java interface, regardless of the 
028  * license terms of these independent modules, and to copy and distribute the
029  * resulting combined work under terms of your choice, provided that
030  * every copy of the combined work is accompanied by a complete copy of
031  * the source code of DocWhatsUp (the version of DocWhatsUp used to produce the
032  * combined work), being distributed under the terms of the GNU General
033  * Public License plus this exception.  An independent module is a module
034  * which is not derived from or based on DocWhatsUp.
035 
036  * Note that people who make modified versions of DocWhatsUp are not obligated
037  * to grant this special exception for their modified versions; it is
038  * their choice whether to do so.  The GNU General Public License gives
039  * permission to release a modified version without this exception; this
040  * exception also makes it possible to release a modified version which
041  * carries forward this exception.
042  
043  * Author: Philipp Bartsch; codeshaker@gmx.net
044  */
045  
046 package org.shaker.dwu;
047 
048 import java.io.File;
049 import java.io.FileInputStream;
050 import java.io.FileOutputStream;
051 import java.io.IOException;
052 import java.io.InputStream;
053 import java.util.ArrayList;
054 import java.util.Properties;
055 import java.util.StringTokenizer;
056 
057 import javax.mail.internet.AddressException;
058 import javax.mail.internet.InternetAddress;
059 
060 /**
061  * This class manages all DocWhatsUp properties:
062  <ul>
063  <li>POP-Server (optional)</li>
064  <li>POP-Server port (default 110)</li>
065  <li>SMTP-Server</li>
066  <li>SMTP-Server port (default 25)</li>
067  <li>Mailaccount name (optional)</li>
068  <li>Mailaddress.</li>
069  </ul>
070  * The POP settings and the mailaccount name must be present for POP-Auth 
071  * SMTP-Servers (like the majority of them).
072  
073  @author <A HREF="mailto:codeshaker@gmx.net">
074  *            Philipp Bartsch (codeshaker@gmx.net)</A>
075  *            <A HREF="../../../../gpl.txt">GPL License</A>
076  */
077 final class   Settings
078       extends Properties {
079     
080     /**POP-Server key*/
081     static final byte POP         = 1;
082     /**POP-Server port key*/
083     static final byte POP_PORT    = 2;
084     /**SMTP-Server key*/
085     static final byte SMTP         = 3;
086     /**SMTP-Server port key*/
087     static final byte SMTP_PORT    = 4;
088     /**Mail account name key*/
089     static final byte USER         = 5;
090     /**User`s mail address - key*/
091     static final byte FROM         = 6;
092     /**Submission list key*/    
093     static final byte SUBMITTED    = 10;
094         
095     /**Settings file reference*/
096     private static final File                cfgFile  = new File(ToolBox.DWU_PATH+
097                                                                 "settings.dwu");
098     /**Custom mail configuration file reference*/
099     private static final File                cstmFile = new File(ToolBox.DWU_PATH+
100                                                                 "custom.mta");
101     /**The common settings (that also stores sigs of submitted BugProfiles)*/
102     private static final Properties        cfgProps = new Properties();
103     /**The array of cc addresses, specified by settings.dwu*/
104     private           final InternetAddress[] ccArray;         
105     /**The array of maintainer addresses, specified by settings.dwu*/
106     private           final InternetAddress[] mntArray;         
107     
108     /**Settings file inputstream*/
109     private FileInputStream         cfgInstream;        
110     /**Settings file outputstream*/
111     private FileOutputStream cfgOutstream;                                              
112     /**Mail settings file inputstream*/
113     private InputStream  propInstream;        
114     /**Mail settings file outputstream*/
115     private FileOutputStream propOutstream;                                              
116     
117     
118     /**
119      * Inits the Properties and loads, if present, older values.
120      */
121     Settings () {
122         ToolBox.checkUp();
123         try {
124             // load mail config
125             if (cstmFile.exists())
126                 load(propInstream = new FileInputStream(cstmFile));
127             else
128                 load(propInstream = ClassLoader
129                                     .getSystemClassLoader()
130                                     .getResourceAsStream("default.mta"));        
131             // load configuration            
132             if (cfgFile.exists()) {            
133                 cfgProps.load(cfgInstream = new FileInputStream(cfgFile));
134                 cfgInstream.close();
135             else
136                 cfgFile.createNewFile();    
137             propInstream.close();                    
138         catch (IOException ioe) {    
139             ToolBox.logException(ioe);                                                                  
140         finally {
141             if (getProperty("mail.host.port"== null)
142                 setProperty("mail.host.port",
143                             "25");
144             if (getProperty("mail.pop.port"== null)
145                 setProperty("mail.pop.port",
146                             "110");                        
147         }
148         ccArray  = resolveAddrs(cfgProps.getProperty("cc"));
149         mntArray = resolveAddrs(cfgProps.getProperty("maintain"));
150     }
151     
152     /**
153      * Returns true if the default or custom mail config files are present.
154      * If one of these file exists, there`s implicitly a working configuration.
155      @see ToolBox#DWU_PATH if you want to know, where the files are located
156      
157      @return boolean flag
158      */
159     static final boolean isConfigurated () {
160         return cstmFile.exists()
161                || ClassLoader
162                   .getSystemClassLoader()
163                   .getResourceAsStream("default.mta"!= null;
164     }
165         
166     /**
167      * Reverts current settings and loads saved state.
168      */
169     final void revertChanges () {
170         clear();
171         if (isConfigurated()) //is there a mta to revert to? 
172             try {
173                 if (!cstmFile.exists()) 
174                     propInstream = ClassLoader
175                                    .getSystemClassLoader()
176                                    .getResourceAsStream("default.mta");
177                 else
178                     propInstream = new FileInputStream(cstmFile);
179                 load(propInstream);
180                 propInstream.close();
181             catch (IOException ioe) {
182                 //hardly reachable
183                 ToolBox.logException(ioe);
184             }
185     }
186     
187     static final File getSettingsFile () {
188         return cfgFile;
189     }
190             
191     /**
192      * Tries to delete a somehow corrupted properties file.
193      */
194     static final void killPhysically () {
195         cstmFile.delete();
196     }
197     
198     /**
199      * Returns true, if the current settings could be successfully saved to 
200      * disk.
201      */
202     final void applyChanges () {
203         try {    
204             if (!cstmFile.exists()) {
205                 cstmFile.createNewFile();
206             }
207             // don`t save users password for security reasons
208             String passwd = getProperty("mail.pass");
209             remove("mail.pass");
210             // store settings
211             store(propOutstream = new FileOutputStream(cstmFile),
212                   "Donīt touch ;)");
213             propOutstream.close();    
214             if (passwd != null)
215                 super.setProperty("mail.pass",
216                                   passwd);
217         catch (IOException ioe) {
218             ToolBox.logException(ioe);
219         }
220     }
221     
222     /**
223      * Sets a mail configuration property. Used keys:
224      <ul>
225      <li>POP                 - The POP-Server</li>
226      <li>POP_Port          - The port of the POP-Server</li>
227      <li>SMTP                - THE SMTP-Server</li>
228      <li>SMTP_PORT        - The port of the SMTP-Server</li>
229      <li>FROM                - The senders mail address</li>
230      <li>USER                - The mailbox account name</li>
231      <li>FORMAT            - The standard send format of the mail body</li>
232      </ul>
233      
234      @param key        an int key representation of the current value
235      @param value     a string representation of the value
236      */
237     final void setProperty (final byte        key,
238                             final String     value) {
239         switch(key) {
240             case(POP):
241                 setProperty("mail.pop", value);
242                 remove("mail.pass");
243                 break;
244             case(POP_PORT)       :
245                 setProperty("mail.pop.port",
246                             (value.length() == 0
247                              "110"
248                              : value));
249                 remove("mail.pass");
250                 break;
251             case(SMTP):
252                 setProperty("mail.host", value);
253                 remove("mail.pass");
254                 break;
255             case(SMTP_PORT):
256                 setProperty("mail.host.port"
257                             (value.length() == 0
258                              "25"
259                              : value));
260                 remove("mail.pass");
261                 break;
262             case(FROM):
263                 setProperty("mail.from", value);
264                 break;
265             case(USER):
266                 setProperty("mail.user", value);
267                 remove("mail.pass");
268                 break;
269         }
270     }
271     
272     /**
273      * Returns users mail address.
274      
275      @return InternetAddress    the mail address
276      */
277     final InternetAddress getSender () {
278         try {
279             return new InternetAddress(getProperty("mail.from"));
280         catch (AddressException ae) {
281             return null;
282         catch (NullPointerException npe) {
283             return null;
284         }
285     }
286     
287     /**
288      * Returns true, if a password has been specified.
289      
290      @return true, if there`s a password
291      */
292     final boolean hasPassword () {        
293         return containsKey("mail.pass");
294     }
295     
296     /**
297      * Returns the specified password.
298      * see#hasPassword() that should be used to check presence
299      
300      @return the password
301      */
302     final String getPassword () {
303         return getProperty("mail.pass");
304     }
305     
306     /**
307      * Removes the given password to force the user to reenter it.
308      * This method doesn`t delete the password of the default mail configuration!
309      */
310     final void removePassword () {
311         if (cstmFile.exists() && containsKey("mail.pass"))
312             remove("mail.pass");
313     }
314         
315     /**
316      * Returns true, if a mail account has been specified.
317      *  
318      @return boolean flag
319      */
320     final boolean hasUser () {
321         return containsKey("mail.user");
322     }
323     
324     /**
325      * Returns mail account name.
326      
327      @return String the mail account name
328      */
329     final String getUser () {
330         return getProperty("mail.user");
331     }
332     
333     /**
334      * Returns true, if a pop-server has been specified.
335      
336      @return boolean flag
337      */
338     final boolean hasPOPServer () {
339         return containsKey("mail.pop")
340                && getProperty("mail.pop").length() 0;
341     }
342     
343     /**
344      * Return the POP-Server address. 
345      
346      @return String the POP-Server address
347      */
348     final String getPOPServer () {
349         return getProperty("mail.pop");
350     }
351     
352     /**
353      * Returns the POP-Server port.
354      
355      @return int the port
356      */
357     final int getPOPPort () {
358         return Integer.parseInt(getProperty("mail.pop.port"));
359     }
360     
361     /**
362      * Returns true, if a mailaccount has been specified.
363      
364      @return boolean flag
365      */
366     final boolean hasSMTPServer () {
367         return containsKey("mail.host");
368     }
369 
370     /**
371      * Returns the specified SMTP-Server address.
372      
373      @return String the address
374      */
375     final String getSMTPServer () {
376         return getProperty("mail.host");
377     }
378 
379     /**
380      * Returns the SMTP-Server port.
381      
382      @return int the port
383      */
384     final int getSMTPPort () {
385         return Integer.parseInt(getProperty("mail.host.port"));
386     }
387 
388     /**
389      * Returns the specified default send format or text, if nothing has been specified.
390      
391      @return    the default format from common.properties or an empty string
392      */
393     final String getDefaultSendFormat() {
394         return (cfgProps.containsKey("format")
395                 ? cfgProps.getProperty("format")
396                 "text");        
397     }
398     
399     /**
400      * Returns true, if "mailing" in settings.dwu has been set to "true". If you
401      * set "mailing" to "false", DWU won`t submit mails, doesn`t include the 
402      * "Send Error" tab into the dialogs and ignores the submitBugQueue() method.
403      *  
404      @return true, if "mailing" in settings.dwu is set to "true"
405      */
406     final boolean isMailingEnabled () {
407         return (cfgProps.containsKey("mailing")
408                 ? cfgProps.getProperty("mailing").equals("true")
409                 false);
410     }
411     
412     /**
413      * Returns true, if "dialogs" in settings.dwu has been set to "true". If you
414      * set "dialogs" to "false", DWU won`t create dialogs, even if you call the 
415      * appropiate methods.
416      *  
417      @return true, if "dialogs" in settings.dwu is set to "true"
418      */
419     final boolean isDialogEnabled () {
420         return (cfgProps.containsKey("dialogs")
421                 ? cfgProps.getProperty("dialogs").equals("true")
422                 false);
423     }
424     
425     /**
426      * Returns true, if at least one valid maintainers has been specified.
427      
428      @return true, if settings.dwu contains a valid "maintain" entry
429      */
430     final boolean hasMaintainer () {
431         return mntArray.length > 0;        
432     }
433      
434     /**
435      * Returns the address of the DWU project maintainer specified by 
436      * "maintain" in settings.dwu or null, if nothing is specified. If 
437      * this property is missing, DWU won`t send maintenance mails (about 
438      * DWU errors).
439      
440      @return the addresses of the maintainer or an empty array
441      */
442     final InternetAddress[] getMaintainers () {
443         return mntArray;        
444     }
445     
446     /**
447      * Returns the co recipients specified by "cc" in settings.dwu.
448      
449      @return the cc address array
450      */
451     final InternetAddress[] getCoRecipients () {
452         return ccArray; 
453     }
454     
455     /**
456      * Returns true, if the settings.dwu file specified further recipients.
457      
458      @return cc flag
459      */
460     final boolean hasCoRecipient () {
461         return ccArray.length > 0;
462     }
463     
464     /**
465      * Returns true, if a valid alternative recipient has been specified.
466      
467      @return true, if settings.dwu contains a valid "alt" entry
468      */
469     final boolean hasAlternativeRecipient () {
470         try {        
471             if ((containsKey("alt"&& cfgProps.getProperty("alt"!= null))
472                 new InternetAddress(cfgProps.getProperty("alt"));
473             return true;
474         catch (AddressException ae) {
475             return false;
476         }             
477     }
478     
479     /**
480      * Returns the alternative recipient specified by "alt" in settings.dwu 
481      * or null, if nothing is specified.
482      
483      @return the alternative recipient
484      */
485     final String getAlternativeRecipient () {
486         return cfgProps.getProperty("alt");
487     }
488     
489     /**
490      * Returns the number of transmitted report.
491      
492      @return int the amount
493      */
494     final int getTransferCount () {
495         
496         if (cfgProps.containsKey("submitted"))
497             return new StringTokenizer(cfgProps.getProperty("submitted")
498                                        ";").countTokens();
499         else
500             return 0;                                                   
501     }
502     
503     /**
504      * Registers a BugProfile as submitted.
505      
506      @param hashValue the hashvalue of the submitted profile
507      */
508     final void registerSubmission (final String hashValue) {
509         if (cfgProps.getProperty("submitted"== null)            
510             cfgProps.setProperty("submitted",
511                                   hashValue + ";");
512         else
513             cfgProps.setProperty("submitted",
514                                  cfgProps.getProperty("submitted")
515                                              .concat(hashValue + ";"));
516         try {        
517             cfgProps.store(cfgOutstream = new FileOutputStream(cfgFile),
518                             "Please have a look at the manual first!");
519             cfgOutstream.close();
520         catch (Exception e) {
521             ToolBox.logException(e);
522         }
523     }    
524     
525     /**
526      * Returns true if a BugProfile with the given hashValue has already been
527      * submitted before. This method searches for an entry of the given 
528      * hashValue in section "submitted" (which keeps track of submitted hashes).
529      
530      @param  hashValue    the current StackTrace hashvalue
531      @return boolean         true if already submitted  
532      */
533     final boolean alreadySubmitted (final String     hashValue) {        
534         return cfgProps.containsKey("submitted"
535                && cfgProps.getProperty("submitted").indexOf(hashValue> -1;
536         
537     }
538         
539     /**
540      * Returns all addresses of a comma separated string containing mail
541      * addresses.
542      *
543      @param  rawList  a comma separated string of addresses
544      @return             the resolved address array
545      */
546     final InternetAddress[] resolveAddrs (final String rawList) {
547         if (rawList == null || rawList.length() == 0)
548             return new InternetAddress[] {};        
549         
550         final ArrayList adds = new ArrayList();
551         final StringTokenizer tok = new StringTokenizer(rawList,
552                                                         ",");
553         try {
554             while (tok.hasMoreElements()) {
555                 adds.add(new InternetAddress(tok.nextElement()
556                                              .toString()
557                                              .trim()));
558             }
559             return (InternetAddress[]) adds.toArray(new InternetAddress[0]);
560         catch (AddressException ae) {
561             if (adds.size() 0)
562                 return (InternetAddress[]) adds.toArray(new InternetAddress[0]);
563             return new InternetAddress[] {};
564         }        
565     }
566 }