Java tutorial
/** * JHylaFax - A java client for HylaFAX. * * Copyright (C) 2005 by Steffen Pingel <steffenp@gmx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package net.sf.jhylafax.fax; import gnu.hylafax.Job; import gnu.inet.ftp.ServerResponseException; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.TimeZone; import net.sf.jhylafax.Settings; import net.sf.jhylafax.fax.FaxJob.JobType; import net.sf.jhylafax.fax.FaxJob.PageChopping; import net.sf.jhylafax.fax.Modem.Volume; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Provides static methods to handle the server communication. * * @author Steffen Pingel */ public class HylaFAXClientHelper extends Thread { /** * The format used for docq, contains all valid tokens except %p. * * <p>Note: %a, %c, %m adds a line break, therefore it needs to be last. */ public final static String FILEFMT = "__FILEFMT |%d |%f |%g |%i |%l |%o |%p |%q |%r |%s |%m "; /** * The format used for sendq and doneq, contains all valid tokens except %T, * %Z, %z. * * <p>Note: %s may contain line breaks, therefore it is last */ public final static String JOBFMT = "__JOBFMT |%a |%b |%c |%e |%f |%g |%h |%i |%j |%k |%l |%m |%n |%o |%p |%q |%r |%t |%u |%v |%w |%x |%y |%z" + " |%A |%B |%C |%D |%E |%F |%G |%H |%I |%J |%K |%L |%M |%N |%O |%P |%Q |%R |%S |%U |%V |%W |%X |%Y |%Z |%s "; private final static Log logger = LogFactory.getLog(HylaFAXClientHelper.class); /** * The format used for modem status, contains all valid tokens. */ public final static String MODEMFMT = "__MODEMFMT |%h |%l |%m |%n |%r |%s |%t |%v |%z "; /** * The format strings need an additional space to avoid empty tokens. */ protected final static String QUEUE_SEPARATOR = "|"; /** * The format used for recvq, contains all valid tokens except %m, %t. * * <p>%q has been removed as well since it causes some versions of HylaFAX * to segfault (#1496477). */ //public final static String RCVFMT = "__RCVFMT |%Y |%a |%b |%d |%e |%f |%h |%i |%j |%l |%n |%o |%p |%q |%r |%s |%w |%z "; public final static String RCVFMT = "__RCVFMT |%Y |%a |%b |%d |%e |%f |%h |%i |%j |%l |%n |%o |%p |%r |%s |%w |%z "; private static DateFormat fileDateFormat = new SimpleDateFormat("MMM dd HH:mm:ss yyyy", Locale.ENGLISH); public static void applyParameter(Job faxJob, FaxJob job) throws ServerResponseException, IOException { //faxJob.setChopThreshold(3); faxJob.setDialstring(job.getNumber()); if (job.getSender() != null && job.getSender().trim().length() > 0) { faxJob.setFromUser(job.getSender()); } faxJob.setKilltime("000259"); faxJob.setMaximumDials(job.getMaxDials()); faxJob.setMaximumTries(job.getMaxDials()); if (job.getNotifyAdress() != null && job.getNotifyAdress().trim().length() > 0) { faxJob.setNotifyAddress(job.getNotifyAdress()); } if (job.getNotify() != null) { faxJob.setNotifyType(job.getNotify()); } faxJob.setPageChop(Job.CHOP_DEFAULT); faxJob.setPageWidth(job.getPageWidth()); faxJob.setPageLength(job.getPageLength()); faxJob.setPriority(job.getPriority()); faxJob.setProperty("SENDTIME", calculateTime(job.getSendTime(), Settings.TIMEZONE.getValue())); faxJob.setVerticalResolution(job.getVerticalResolution()); } public static String calculateTime(Date sendTime, String timeZoneID) { if (sendTime == null) { return "NOW"; } else { long date = sendTime.getTime(); TimeZone tz = TimeZone.getTimeZone(timeZoneID); // tz.setStartRule(Calendar.MARCH, -1, Calendar.SUNDAY, 2*60*60*1000); // tz.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000); date -= tz.getRawOffset(); if (tz.inDaylightTime(sendTime)) { date -= 3600 * 1000; } return new SimpleDateFormat("yyyyMMddHHmm").format(new Date(date)); } } private static JobType getJobType(char c) { switch (c) { case 'P': return JobType.PAGER; default: // 'F' return JobType.FACSIMILE; } } private final static String getNotify(char notify) { switch (notify) { case 'D': return Job.NOTIFY_DONE; case 'Q': return Job.NOTIFY_REQUEUE; case 'A': return Job.NOTIFY_ALL; default: return Job.NOTIFY_NONE; } } private static PageChopping getPageChopping(char c) { switch (c) { case 'D': return PageChopping.DISABLED; case 'A': return PageChopping.ALL; case 'L': return PageChopping.LAST; default: // ' ' return PageChopping.DEFAULT; } } public final static FaxJob.State getState(char state) { switch (state) { case 'T': return FaxJob.State.SUSPENDED; case 'P': return FaxJob.State.PENDING; case 'S': return FaxJob.State.SLEEPING; case 'B': return FaxJob.State.BLOCKED; case 'W': return FaxJob.State.WAITING; case 'D': return FaxJob.State.DONE; case 'R': return FaxJob.State.RUNNING; case 'F': return FaxJob.State.FAILED; default: // '?' return FaxJob.State.UNDEFINED; } } private static Volume getVolume(char c) { // TODO add switch return Volume.OFF; } public final static void initializeFromSettings(FaxJob job) { job.setSender(Settings.FULLNAME.getValue()); job.setNotifyAdress(Settings.EMAIL.getValue()); job.setMaxDials(Settings.MAXDIALS.getValue()); job.setMaxTries(Settings.MAXTRIES.getValue()); job.setNotify(Settings.NOTIFICATION.getValue().getCommand()); job.setPageLength(Settings.PAPER.getValue().getHeight()); job.setPageWidth(Settings.PAPER.getValue().getWidth()); job.setPriority(Settings.PRIORITY.getValue()); job.setResolution(Settings.RESOLUTION.getValue().getLinesPerInch()); } public static boolean isPostscript(String filename) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename))); try { return in.readLine().startsWith("%!"); } finally { try { in.close(); } catch (IOException e) { } } } static int parseDuration(String s) { StringTokenizer t = new StringTokenizer(s, ":"); int duration = 0; while (t.hasMoreTokens()) { int n = Integer.parseInt(t.nextToken()); duration = duration * 60 + n; } return duration; } public final static Document parseFileFmt(String response) { StringTokenizer st = new StringTokenizer(response, QUEUE_SEPARATOR); StringTokenizer jf = new StringTokenizer(FILEFMT, QUEUE_SEPARATOR); Document file = new Document(); while (st.hasMoreElements() && jf.hasMoreElements()) { char c = jf.nextToken().charAt(1); String s = st.nextToken().trim(); if (s.length() > 0) { try { // parse switch (c) { case 'a': file.setLastAccessTime(fileDateFormat.parse(s)); break; case 'c': file.setCreationTime(fileDateFormat.parse(s)); break; case 'd': file.setDeviceNumber(Integer.parseInt(s, 8)); break; case 'f': file.setFilename(s); break; case 'g': file.setGroupID(Integer.parseInt(s)); break; case 'i': file.setInodeNumber(Long.parseLong(s)); break; case 'l': file.setLinkCount(Integer.parseInt(s)); break; case 'm': file.setLastModificationTime(fileDateFormat.parse(s)); break; case 'o': file.setOwner(s); break; case 'p': // Fax-style protection flags (no group bits) // 'q' is used instead break; case 'q': file.setPermissions(s); break; case 'r': file.setRootDeviceNumber(Integer.parseInt(s)); break; case 's': file.setFilesize(Long.parseLong(s)); break; case 'u': file.setOwnerID(Integer.parseInt(s)); break; } } catch (NumberFormatException e) { logger.info("Error parsing respone", e); } catch (ParseException e) { logger.info("Error parsing response", e); } } } return file; } public final static Object parseFmt(String response) { if (logger.isDebugEnabled()) logger.debug("Received: " + response); if (response.trim().length() == 0) { // work around a bug in HylaFax return null; } if (response.startsWith("__JOBFMT")) { return parseJobFmt(response); } else if (response.startsWith("__RCVFMT")) { return parseRcvFmt(response); } else if (response.startsWith("__FILEFMT")) { return parseFileFmt(response); } else if (response.startsWith("__MODEMFMT")) { return parseModemFmt(response); } else { logger.error("Invalid response: " + response); return null; } } public final static FaxJob parseJobFmt(String response) { StringTokenizer st = new StringTokenizer(response, QUEUE_SEPARATOR); StringTokenizer jf = new StringTokenizer(JOBFMT, QUEUE_SEPARATOR); FaxJob job = new FaxJob(); while (st.hasMoreElements() && jf.hasMoreElements()) { char c = jf.nextToken().charAt(1); String s = st.nextToken().trim(); if (s.length() > 0) { try { switch (c) { case 'a': job.setState(getState(s.charAt(0))); break; case 'b': job.setConsecutiveFailedTries(Integer.parseInt(s)); break; case 'c': job.setClientMachineName(s); break; case 'd': job.setDialsAttempted(Integer.parseInt(s)); break; case 'e': job.setNumber(s); break; case 'f': job.setConsecutiveFailedDials(Integer.parseInt(s)); break; case 'g': job.setGroupID(Integer.parseInt(s)); break; case 'h': job.setPageChopping(getPageChopping(s.charAt(0))); break; case 'i': job.setPriority((new Integer(s)).intValue()); break; case 'j': job.setID((new Integer(s)).intValue()); break; case 'k': job.setKillTime(s); break; case 'l': // FIXME 'any' job.setPageLength(Integer.parseInt(s)); break; case 'm': job.setAssignedModem(s); break; case 'n': job.setNotify(getNotify(s.charAt(0))); break; case 'o': job.setOwner(s); break; case 'p': job.setPagesTransmitted(Integer.parseInt(s)); break; case 'q': job.setRetryTime(parseDuration(s)); break; case 'r': job.setResolution((new Integer(s)).intValue()); break; case 's': job.setLastError(s); break; case 't': job.setTriesAttempted((new Integer(s)).intValue()); break; case 'u': job.setMaxTries((new Integer(s)).intValue()); break; case 'v': job.setClientDialString(s); break; case 'w': job.setPageWidth(Integer.parseInt(s)); break; case 'x': // FIXME 'x/y' job.setMaxDials((new Integer(s)).intValue()); break; case 'z': // the handling code never worked correctly, use // 'Y' instead //Date date = parseDate(s, true); //job.setSendTime(date); break; case 'A': job.setDestinationSubAddress(s); break; case 'B': job.setDestinationPassword(s); break; case 'C': job.setDestinationCompanyName(s); break; case 'D': { StringTokenizer t = new StringTokenizer(s, ":"); job.setDialsAttempted(Integer.parseInt(t.nextToken())); job.setMaxDials(Integer.parseInt(t.nextToken())); break; } case 'E': job.setDesiredSignallingRate(s); break; case 'F': job.setClientDialString(s); break; case 'G': job.setDesiredMinScanline(s); break; case 'H': job.setDesiredDataFormat(s); break; case 'I': job.setClientSchedulingPriority(s); break; case 'J': job.setClientJobTag(s); break; case 'K': job.setDesiredECM(s); break; case 'L': job.setDestinationLocation(s); break; case 'M': job.setNotifyAdress(s); break; case 'N': job.setUsePrivateTagLine("P".equals(s)); break; case 'P': { StringTokenizer t = new StringTokenizer(s, ":"); job.setPagesTransmitted(Integer.parseInt(t.nextToken())); job.setPageCount(Integer.parseInt(t.nextToken())); break; } case 'R': job.setReceiver(s); break; case 'S': job.setSender(s); break; case 'T': // Total # tries/maximum # tries // %t, %u are used instead break; case 'U': job.setChoppingThreshold(Double.parseDouble(s)); break; case 'V': job.setJobDoneOperation(s); break; case 'W': job.setCommunicationIdentifier(s); break; case 'X': job.setJobType(getJobType(s.charAt(0))); break; case 'Y': { Date date = new SimpleDateFormat("yyyy/MM/dd HH.mm.ss").parse(s); job.setSendTime(date); break; } case 'Z': { // should work, but for some reason calculates the // wrong time, so 'Y' is used instead Date date = new Date(Long.parseLong(s)); //job.setSendTime(date); break; } } } catch (ParseException e) { logger.info("Error parsing response", e); } catch (NumberFormatException e) { logger.info("Error parsing response", e); } catch (NoSuchElementException e) { logger.info("Error parsing response", e); } } } return job; } public final static Modem parseModemFmt(String response) { StringTokenizer st = new StringTokenizer(response, QUEUE_SEPARATOR); StringTokenizer jf = new StringTokenizer(MODEMFMT, QUEUE_SEPARATOR); Modem modem = new Modem(); while (st.hasMoreElements() && jf.hasMoreElements()) { char c = jf.nextToken().charAt(1); String s = st.nextToken().trim(); if (s.length() > 0) { try { switch (c) { case 'h': modem.setHostname(s); break; case 'l': modem.setLocalIdentifier(s); break; case 'm': modem.setCanonicalName(s); break; case 'n': modem.setFaxNumber(s); break; case 'r': modem.setMaxPagesPerCall(Integer.parseInt(s)); break; case 's': modem.setStatus(s); break; case 't': { StringTokenizer t = new StringTokenizer(s, ":"); modem.setServerTracing(Integer.parseInt(t.nextToken())); modem.setSessionTracing(Integer.parseInt(t.nextToken())); break; } case 'v': modem.setSpeakerVolume(getVolume(s.charAt(0))); break; case 'z': modem.setRunning("*".equals(s)); break; } } catch (NumberFormatException e) { logger.info("Error parsing respone", e); } catch (NoSuchElementException e) { logger.info("Error parsing response", e); } } } return modem; } public final static ReceivedFax parseRcvFmt(String response) { StringTokenizer st = new StringTokenizer(response, QUEUE_SEPARATOR); StringTokenizer jf = new StringTokenizer(RCVFMT, QUEUE_SEPARATOR); ReceivedFax fax = new ReceivedFax(); while (st.hasMoreElements() && jf.hasMoreElements()) { char c = jf.nextToken().charAt(1); String s = st.nextToken().trim(); if (s.length() > 0) { try { switch (c) { case 'Y': Date date = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(s); fax.setReceivedTime(date); break; case 'a': fax.setSubAddress(s); break; case 'b': fax.setSignallingRate(Integer.parseInt(s)); break; case 'd': fax.setDataFormat(s); break; case 'e': fax.setLastError(s); break; case 'f': fax.setFilename(s); break; case 'h': fax.setTimeSpent(parseDuration(s)); break; case 'i': fax.setCallerIDName(s); break; case 'j': fax.setCallerIDNumber(s); break; case 'l': // FIXME '4294967295' fax.setPageLength(Integer.parseInt(s)); break; case 'm': // Fax-style protection mode string // 'q' is used instead break; case 'n': fax.setFilesize(Long.parseLong(s)); break; case 'o': fax.setOwner(s); break; case 'p': fax.setPageCount(Integer.parseInt(s)); break; case 'q': fax.setProtectionMode(Integer.parseInt(s)); break; case 'r': fax.setResolution(Integer.parseInt(s)); break; case 's': fax.setSender(s); break; case 't': // the handling code never worked correctly // 'Y' is used instead //job.setSendTime(parseDate(s, false)); break; case 'w': fax.setPageWidth(Integer.parseInt(s)); break; case 'z': fax.setReceiving(s.equals("*")); break; } } catch (ParseException e) { logger.info("Error parsing response", e); } catch (NumberFormatException e) { logger.info("Error parsing response", e); } } } return fax; } public static void setJobProperties(Job faxJob, FaxJob job) throws ServerResponseException, IOException { /* job.setNumber(faxJob.getDialstring()); job.setChopThreshold(faxJob.getChopThreshold()); job.setDialstring(faxJob.getNumber()); job.setSender(faxJob.getSender()); job.setKilltime(faxJob.getKilltime()); job.setMaxDials(faxJob.getMaximumDials()); job.setMaxTries(faxJob.getMaximumTries()); job.setNotifyAddress(faxJob.getNotifyAddress()); job.setNotify(faxJob.getNotifyType()); job.setPageChop(faxJob.getPageChop()); job.setPaperWidth(faxJob.getPageWidth()); job.setPaperHeight(faxJob.getPageLength()); job.setPriority(faxJob.getPriority()); job.setSendTime(faxJob.getRetrytime()); job.setResolution(faxJob.getVerticalResolution()); job.setDocumentNames(faxJob.getDocumentName()); */ } }