View Javadoc

1   /********************************************************************************
2    * $Id: HylaFAXClientProtocol.java 153 2009-02-05 15:33:36Z sjardine $
3    * 
4    * Copyright 1999, 2000 Joe Phillips <jaiger@innovationsw.com>
5    * Copyright 2001 Innovation Software Group, LLC - http://www.innovationsw.com
6    * Copyright 2005-2008 Steven Jardine <steve@mjnservices.com>
7    * 
8    * All rights reserved. This program and the accompanying materials are made
9    * available under the terms of the GNU Lesser Public License v2.1 which 
10   * accompanies this distribution, and is available at
11   * 	http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
12   *
13   * For more information on the HylaFAX Fax Server please see
14   * 	HylaFAX  - http://www.hylafax.org or 
15   * 	Hylafax+ - http://hylafax.sourceforge.net
16   * 
17   * Contributors:
18   * 	Joe Phillips - Initial API and implementation
19   * 	Steven Jardine
20   * 	Jonas Wolz  
21   ******************************************************************************/
22  package gnu.hylafax;
23  
24  import gnu.inet.ftp.FtpClientProtocol;
25  import gnu.inet.ftp.ServerResponseException;
26  
27  import java.io.FileNotFoundException;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.InetAddress;
31  import java.net.UnknownHostException;
32  import java.util.NoSuchElementException;
33  import java.util.StringTokenizer;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  /***
39   * This is the core implementation of the HylaFAX client protocol.
40   * 
41   * The purpose of this class is to implement the HylaFAX client protocol as
42   * simply and straight-forward as possible.
43   * 
44   * Method names are not my choosing for the most part. They have been largely
45   * pulled straight from the protocol and HylaFAX man pages. I expect that
46   * convenience classes and methods, with more developer friendly names will be
47   * built on top of this raw protocol implementation as time passes.
48   * 
49   * Most developers should use the higher-level Client to perform some actions
50   * rather than this class directly.
51   * 
52   * @version $Revision: 153 $
53   * @author Joe Phillips <jaiger@net-foundry.com>
54   * @author Steven Jardine <steve@mjnservices.com>
55   * @author Jonas Wolz
56   */
57  public class HylaFAXClientProtocol extends FtpClientProtocol implements
58  	ClientProtocol {
59  
60      /***
61       * default HylaFAX server port. currently 4559
62       */
63      public static int DEFAULT_PORT = 4559;
64  
65      // public static stuff
66      private final static Log log = LogFactory.getLog(ClientProtocol.class);
67  
68      /***
69       * use the GMT timezone for date fields.
70       */
71      public static final String TZONE_GMT = "GMT";
72  
73      // public methods
74  
75      /***
76       * use the local timezone for date fields.
77       */
78      public static final String TZONE_LOCAL = "LOCAL";
79  
80      protected String hylafaxServerTimeZone = null;
81  
82      protected String version = null;
83  
84      /***
85       * default constructor. sets up the initial class state.
86       */
87      public HylaFAXClientProtocol() {
88  	super();
89      }
90  
91      /*
92       * (non-Javadoc)
93       * 
94       * @see gnu.hylafax.ClientProtocol#admin(java.lang.String)
95       */
96      public synchronized void admin(String password) throws IOException,
97  	    ServerResponseException {
98  	// send admin command to the server
99  	ostream.write("admin " + password + "\r\n");
100 	ostream.flush();
101 	log.debug("-> admin " + password);
102 
103 	// get reply string
104 	String response = new String(istream.readLine());
105 	log.debug(response);
106 
107 	// check response code
108 	StringTokenizer st = new StringTokenizer(response);
109 	if (!st.nextToken().equals("230")) {
110 	    // command failed
111 	    throw (new ServerResponseException(response));
112 	}
113     }
114 
115     public synchronized void answer(String modem) throws IOException,
116 	    ServerResponseException {
117 	ostream.write("answer " + modem + "\r\n");
118 	ostream.flush();
119 	log.debug("-> answer " + modem);
120 
121 	String response = new String(istream.readLine());
122 	log.debug(response);
123 
124 	StringTokenizer st = new StringTokenizer(response);
125 	if (!st.nextToken().equals("200")) {
126 	    throw (new ServerResponseException(response));
127 	}
128     }
129 
130     /*
131      * (non-Javadoc)
132      * 
133      * @see gnu.hylafax.ClientProtocol#config(java.lang.String, int)
134      */
135     public synchronized void config(String parm, int value) throws IOException,
136 	    ServerResponseException {
137 	config(parm, Integer.toString(value));
138     }
139 
140     /*
141      * (non-Javadoc)
142      * 
143      * @see gnu.hylafax.ClientProtocol#config(java.lang.String, long)
144      */
145     public void config(String parm, long value) throws IOException,
146 	    ServerResponseException {
147 	config(parm, Long.toString(value));
148     }
149 
150     /*
151      * (non-Javadoc)
152      * 
153      * @see gnu.hylafax.ClientProtocol#config(java.lang.String,
154      * java.lang.Object)
155      */
156     public synchronized void config(String parm, Object value)
157 	    throws IOException, ServerResponseException {
158 	config(parm, value.toString());
159     }
160 
161     /*
162      * (non-Javadoc)
163      * 
164      * @see gnu.hylafax.ClientProtocol#config(java.lang.String,
165      * java.lang.String)
166      */
167     public synchronized void config(String parm, String value)
168 	    throws IOException, ServerResponseException {
169 	String response;
170 	StringTokenizer st;
171 
172 	String cmd = "site config " + parm + " " + value + "\r\n";
173 
174 	ostream.write(cmd);
175 	ostream.flush();
176 
177 	log.debug("-> " + cmd);
178 
179 	response = istream.readLine();
180 	log.debug(response);
181 
182 	st = new StringTokenizer(response);
183 
184 	String return_code = st.nextToken();
185 	if ((!return_code.equals("213")) && (!return_code.equals("200"))) {
186 	    throw (new ServerResponseException(response));
187 	}
188     }
189 
190     /*
191      * (non-Javadoc)
192      * 
193      * @see gnu.hylafax.ClientProtocol#filefmt()
194      */
195     public synchronized String filefmt() throws IOException,
196 	    ServerResponseException {
197 	ostream.write("filefmt\r\n");
198 	ostream.flush();
199 	log.debug("-> filefmt");
200 
201 	String response = istream.readLine();
202 	log.debug(response);
203 
204 	if (!response.substring(0, 3).equals("200")) {
205 	    throw (new ServerResponseException(response));
206 	}
207 	return response.substring(3);
208 
209     }
210 
211     /*
212      * (non-Javadoc)
213      * 
214      * @see gnu.hylafax.ClientProtocol#filefmt(java.lang.String)
215      */
216     public synchronized void filefmt(String value) throws IOException,
217 	    ServerResponseException {
218 	ostream.write("filefmt \"" + value + "\"\r\n");
219 	ostream.flush();
220 	log.debug("-> filefmt \"" + value + "\"");
221 
222 	String response = istream.readLine();
223 	log.debug(response);
224 
225 	if (!response.substring(0, 3).equals("200")) {
226 	    throw (new ServerResponseException(response));
227 	}
228     }
229 
230     /*
231      * (non-Javadoc)
232      * 
233      * @see gnu.hylafax.ClientProtocol#tzone(java.lang.String)
234      */
235     public synchronized void form(String value) throws IOException,
236 	    ServerResponseException {
237 	ostream.write("form \"" + value + "\"\r\n");
238 	ostream.flush();
239 	log.debug("-> form " + value);
240 
241 	String response = istream.readLine();
242 	log.debug(response);
243 
244 	StringTokenizer st = new StringTokenizer(response);
245 	if (!st.nextToken().equals("200")) {
246 	    // problem
247 	    throw (new ServerResponseException(response));
248 	}
249     }
250 
251     /***
252      * @return the hylafax server version.
253      */
254     public String getServerVersion() {
255 	if (version == null) {
256 	    try {
257 		String tmp = getGreeting();
258 		if (tmp == null || tmp.equals(""))
259 		    version = null;
260 		else if (tmp.startsWith("220")) {
261 		    version = tmp.substring(tmp.indexOf("(") + 1, tmp
262 			    .lastIndexOf(")"));
263 		}
264 	    } catch (Exception e) {
265 		version = null;
266 		log.error("Cannot parse version from greeting", e);
267 	    }
268 	}
269 	return version;
270     }
271 
272     /*
273      * (non-Javadoc)
274      * 
275      * @see gnu.inet.ftp.FtpClientProtocol#idle()
276      */
277     public synchronized long idle() throws IOException, ServerResponseException {
278 	// send idle command to the server
279 	ostream.write("idle\r\n");
280 	ostream.flush();
281 	log.debug("-> idle");
282 
283 	// get response string
284 	String response = new String(istream.readLine());
285 	log.debug(response);
286 
287 	// check response code
288 	StringTokenizer st = new StringTokenizer(response);
289 	if (!st.nextToken().equals("213")) {
290 	    // command failed for some reason
291 	    throw (new ServerResponseException(response));
292 	}
293 
294 	// get the data to return
295 	Long l = new Long(st.nextToken());
296 	return l.longValue();
297     }
298 
299     /*
300      * (non-Javadoc)
301      * 
302      * @see gnu.inet.ftp.FtpClientProtocol#idle(long)
303      */
304     public synchronized void idle(long timeout) throws IOException,
305 	    ServerResponseException {
306 	// send idle command to the server
307 	ostream.write("idle " + timeout + "\r\n");
308 	ostream.flush();
309 	log.debug("-> idle " + timeout);
310 
311 	// get reply
312 	String response = new String(istream.readLine());
313 	log.debug(response);
314 
315 	// check result code
316 	StringTokenizer st = new StringTokenizer(response);
317 	if (!st.nextToken().equals("213")) {
318 	    // command failed
319 	    throw (new ServerResponseException(response));
320 	}
321     }
322 
323     /*
324      * (non-Javadoc)
325      * 
326      * @see gnu.hylafax.ClientProtocol#jdele(long)
327      */
328     public synchronized void jdele(long jobid) throws IOException,
329 	    ServerResponseException {
330 	// send command to server
331 	ostream.write("jdele " + jobid + "\r\n");
332 	ostream.flush();
333 	log.debug("-> jdele " + jobid);
334 
335 	// get server reply
336 	String response = new String(istream.readLine());
337 	log.debug(response);
338 
339 	// check result value
340 	StringTokenizer st = new StringTokenizer(response);
341 	if (!st.nextToken().equals("200")) {
342 	    // command failed
343 	    throw (new ServerResponseException(response));
344 	}
345     }
346 
347     /*
348      * (non-Javadoc)
349      * 
350      * @see gnu.hylafax.ClientProtocol#jintr(long)
351      */
352     public synchronized void jintr(long jobid) throws IOException,
353 	    ServerResponseException {
354 	// send command to server
355 	ostream.write("jintr " + jobid + "\r\n");
356 	ostream.flush();
357 	log.debug("-> jintr " + jobid);
358 
359 	// get reply string
360 	String response = new String(istream.readLine());
361 	log.debug(response);
362 
363 	// check result code
364 	StringTokenizer st = new StringTokenizer(response);
365 	if (!st.nextToken().equals("200")) {
366 	    // command failed
367 	    throw (new ServerResponseException(response));
368 	}
369     }
370 
371     /*
372      * (non-Javadoc)
373      * 
374      * @see gnu.hylafax.ClientProtocol#jkill(long)
375      */
376     public synchronized void jkill(long jobid) throws IOException,
377 	    ServerResponseException {
378 	// send command
379 	ostream.write("jkill " + jobid + "\r\n");
380 	ostream.flush();
381 	log.debug("-> jkill " + jobid);
382 
383 	// get reply
384 	String response = new String(istream.readLine());
385 	log.debug(response);
386 
387 	// check result code
388 	StringTokenizer st = new StringTokenizer(response);
389 	if (!st.nextToken().equals("200")) {
390 	    // job failed
391 	    throw (new ServerResponseException(response));
392 	}
393     }
394 
395     /*
396      * (non-Javadoc)
397      * 
398      * @see gnu.hylafax.ClientProtocol#jnew()
399      */
400     public synchronized void jnew() throws IOException, ServerResponseException {
401 	jnew(true);
402     }
403 
404     /*
405      * (non-Javadoc)
406      * 
407      * @see gnu.hylafax.ClientProtocol#jnew(boolean)
408      */
409     public synchronized void jnew(boolean inheritDefault) throws IOException,
410 	    ServerResponseException {
411 	if (inheritDefault) {
412 	    job(0);
413 	}
414 	// send command string
415 	ostream.write("jnew\r\n"); // no options
416 	ostream.flush();
417 	log.debug("-> jnew");
418 
419 	// get results
420 	String response = new String(istream.readLine());
421 	log.debug(response);
422 
423 	// check result code
424 	StringTokenizer st = new StringTokenizer(response);
425 	if (!st.nextToken().equals("200")) {
426 	    // command failed
427 	    throw (new ServerResponseException(response));
428 	}
429     }
430 
431     /*
432      * (non-Javadoc)
433      * 
434      * @see gnu.hylafax.ClientProtocol#job()
435      */
436     public synchronized long job() throws IOException, ServerResponseException {
437 	// send job command to server
438 	ostream.write("job\r\n");
439 	ostream.flush();
440 	log.debug("-> job");
441 
442 	// get reply
443 	String response = new String(istream.readLine());
444 	log.debug(response);
445 
446 	// check result code
447 	StringTokenizer st = new StringTokenizer(response);
448 	if (!st.nextToken().equals("200")) {
449 	    // command failed
450 	    throw (new ServerResponseException(response));
451 	}
452 
453 	// command succeeded
454 	// response should contain current job id
455 	st.nextToken(); // skip "Current"
456 	st.nextToken(); // skip "job:"
457 	st.nextToken(); // skip ...
458 
459 	// now, next token contains the job id or does not exist at all
460 	try {
461 	    Long l = new Long(st.nextToken());
462 	    return l.longValue();
463 	} catch (Exception e) {
464 	    // default job selected
465 	    return 0;
466 	}
467     }
468 
469     /*
470      * (non-Javadoc)
471      * 
472      * @see gnu.hylafax.ClientProtocol#job(long)
473      */
474     public synchronized void job(long val) throws IOException,
475 	    ServerResponseException {
476 	// send job command to the server
477 	String value = val <= 0 ? "default" : String.valueOf(val);
478 	ostream.write("job " + value + "\r\n");
479 	ostream.flush();
480 	log.debug("-> job " + value);
481 
482 	// get server reply
483 	String response = new String(istream.readLine());
484 	log.debug(response);
485 
486 	// check result code
487 	StringTokenizer st = new StringTokenizer(response);
488 	if (!st.nextToken().equals("200")) {
489 	    // command failed
490 	    throw (new ServerResponseException(response));
491 	}
492     }
493 
494     /*
495      * (non-Javadoc)
496      * 
497      * @see gnu.hylafax.ClientProtocol#jobfmt()
498      */
499     public synchronized String jobfmt() throws IOException,
500 	    ServerResponseException {
501 	// send command to server
502 	ostream.write("jobfmt\r\n");
503 	ostream.flush();
504 	log.debug("-> jobfmt");
505 
506 	// get server reply
507 	String response = new String(istream.readLine());
508 	log.debug(response);
509 
510 	// check result code
511 	StringTokenizer st = new StringTokenizer(response);
512 	String temp = new String(st.nextToken());
513 	if (!temp.equals("200")) {
514 	    // command failed
515 	    throw (new ServerResponseException(response));
516 	}
517 
518 	// get format string from response
519 	return response.substring(temp.length());
520     }
521 
522     /*
523      * (non-Javadoc)
524      * 
525      * @see gnu.hylafax.ClientProtocol#jobfmt(java.lang.String)
526      */
527     public synchronized void jobfmt(String value) throws IOException,
528 	    ServerResponseException {
529 	// send command
530 	String command = new String("jobfmt \"" + value + "\"\r\n");
531 	ostream.write(command);
532 	ostream.flush();
533 	log.debug("-> jobfmt " + value);
534 
535 	// get server reply
536 	String response = new String(istream.readLine());
537 	log.debug(response);
538 
539 	// check result code
540 	StringTokenizer st = new StringTokenizer(response);
541 	if (!st.nextToken().equals("200")) {
542 	    // command failed - why?
543 	    throw (new ServerResponseException(response));
544 	}
545     }
546 
547     /*
548      * (non-Javadoc)
549      * 
550      * @see gnu.hylafax.ClientProtocol#jparm(java.lang.String)
551      */
552     public synchronized String jparm(String parm) throws IOException,
553 	    ServerResponseException {
554 	String response;
555 
556 	if (log.isDebugEnabled())
557 	    log.debug("jparam " + parm);
558 	ostream.write("jparm " + parm + "\r\n");
559 	ostream.flush();
560 	response = istream.readLine();
561 	if (response.startsWith("213")) {
562 	    // FIXME check response.length()
563 	    String res = response.substring(4);
564 	    while (response.charAt(3) == '-') {
565 		response = istream.readLine();
566 		if (!response.startsWith("213"))
567 		    throw (new ServerResponseException(response));
568 		// FIXME check response.length()
569 		res += "\n" + response.substring(4);
570 	    }
571 	    return res;
572 	}
573 	throw (new ServerResponseException(response));
574     }
575 
576     /*
577      * (non-Javadoc)
578      * 
579      * @see gnu.hylafax.ClientProtocol#jparm(java.lang.String, int)
580      */
581     public synchronized void jparm(String parm, int value) throws IOException,
582 	    ServerResponseException {
583 	jparm(parm, Integer.toString(value));
584     }
585 
586     /*
587      * (non-Javadoc)
588      * 
589      * @see gnu.hylafax.ClientProtocol#jparm(java.lang.String, long)
590      */
591     public synchronized void jparm(String parm, long value) throws IOException,
592 	    ServerResponseException {
593 	jparm(parm, Long.toString(value));
594     }
595 
596     /*
597      * (non-Javadoc)
598      * 
599      * @see gnu.hylafax.ClientProtocol#jparm(java.lang.String, java.lang.Object)
600      */
601     public synchronized void jparm(String parm, Object value)
602 	    throws IOException, ServerResponseException {
603 	jparm(parm, value.toString());
604     }
605 
606     /*
607      * (non-Javadoc)
608      * 
609      * @see gnu.hylafax.ClientProtocol#jparm(java.lang.String, java.lang.String)
610      */
611     public synchronized void jparm(String parm, String value)
612 	    throws IOException, ServerResponseException {
613 	String response;
614 	StringTokenizer st;
615 
616 	ostream.write("jparm " + parm + " " + value + "\r\n");
617 	ostream.flush();
618 
619 	log.debug("-> jparm " + parm + " " + value);
620 
621 	response = istream.readLine();
622 	log.debug(response);
623 
624 	st = new StringTokenizer(response);
625 
626 	String return_code = st.nextToken();
627 	if ((!return_code.equals("213")) && (!return_code.equals("200"))) {
628 	    throw (new ServerResponseException(response));
629 	}
630     }
631 
632     /*
633      * (non-Javadoc)
634      * 
635      * @see gnu.hylafax.ClientProtocol#jrest()
636      */
637     public synchronized void jrest() throws IOException,
638 	    ServerResponseException {
639 	// send command
640 	ostream.write("jrest\r\n");
641 	ostream.flush();
642 	log.debug("-> jrest");
643 
644 	// get result
645 	String response = new String(istream.readLine());
646 	log.debug(response);
647 
648 	// check result code
649 	StringTokenizer st = new StringTokenizer(response);
650 	if (!st.nextToken().equals("200")) {
651 	    // command failed
652 	    throw (new ServerResponseException(response));
653 	}
654     }
655 
656     /*
657      * (non-Javadoc)
658      * 
659      * @see gnu.hylafax.ClientProtocol#jsubm()
660      */
661     public synchronized long jsubm() throws IOException,
662 	    ServerResponseException {
663 	// send command
664 	ostream.write("jsubm\r\n");
665 	ostream.flush();
666 	log.debug("-> jsubm");
667 
668 	// get results
669 	String response = new String(istream.readLine());
670 	log.debug(response);
671 
672 	// check response
673 	StringTokenizer st = new StringTokenizer(response);
674 	if (!st.nextToken().equals("200")) {
675 	    throw (new ServerResponseException(response));
676 	}
677 
678 	st.nextToken();
679 	return (Long.parseLong(st.nextToken()));
680     }
681 
682     /*
683      * (non-Javadoc)
684      * 
685      * @see gnu.hylafax.ClientProtocol#jsubm(long)
686      */
687     public synchronized int jsubm(long jobid) throws IOException,
688 	    ServerResponseException {
689 	// send command
690 	ostream.write("jsubm " + jobid + "\r\n");
691 	ostream.flush();
692 	log.debug("-> jsubm " + jobid);
693 
694 	// get result
695 	String response = new String(istream.readLine());
696 	log.debug(response);
697 
698 	StringTokenizer st = new StringTokenizer(response);
699 	int jobID = 0;
700 
701 	if (!st.nextToken().equals("200")) {
702 	    throw (new ServerResponseException(response));
703 	}
704 
705 	// third token is the stringified job id. return this.
706 	// catch those messed up responses.
707 
708 	try {
709 	    st.nextToken(); // waste this.
710 	    String jobStr = st.nextToken();
711 	    // only this matters. jobStr should be an int.
712 	    jobID = Integer.parseInt(jobStr); // return that job id.
713 	} catch (NumberFormatException nfe) {
714 	    throw new ServerResponseException("Bad number format for job id");
715 	} catch (NoSuchElementException nsee) {
716 	    new ServerResponseException("Mangled server response to job submit");
717 	}
718 
719 	return jobID;
720     }
721 
722     /*
723      * (non-Javadoc)
724      * 
725      * @see gnu.hylafax.ClientProtocol#jsusp(long)
726      */
727     public synchronized void jsusp(long jobid) throws IOException,
728 	    ServerResponseException {
729 	ostream.write("jsusp " + jobid + "\r\n");
730 	ostream.flush();
731 	log.debug("-> jsusp " + jobid);
732 
733 	String response = new String(istream.readLine());
734 	log.debug(response);
735 
736 	StringTokenizer st = new StringTokenizer(response);
737 	if (!st.nextToken().equals("200")) {
738 	    throw (new ServerResponseException(response));
739 	}
740     }
741 
742     /*
743      * (non-Javadoc)
744      * 
745      * @see gnu.hylafax.ClientProtocol#jwait(long)
746      */
747     public synchronized void jwait(long jobid) throws IOException,
748 	    ServerResponseException {
749 	ostream.write("jwait " + jobid + "\r\n");
750 	ostream.flush();
751 	log.debug("-> jwait " + jobid);
752 
753 	String response = readResponse(istream);
754 	log.debug(response);
755 
756 	StringTokenizer st = new StringTokenizer(response, " -");
757 	if (!st.nextToken().equals("216")) {
758 	    throw (new ServerResponseException(response));
759 	}
760     }
761 
762     /*
763      * (non-Javadoc)
764      * 
765      * @see gnu.hylafax.ClientProtocol#mdmfmt()
766      */
767     public synchronized String mdmfmt() throws IOException,
768 	    ServerResponseException {
769 	ostream.write("mdmfmt\r\n");
770 	ostream.flush();
771 	log.debug("-> mdmfmt");
772 
773 	String response = istream.readLine();
774 	log.debug(response);
775 
776 	if (!response.substring(0, 3).equals("200")) {
777 	    // error
778 	    throw (new ServerResponseException(response));
779 	}
780 
781 	return response.substring(3);
782     }
783 
784     /*
785      * (non-Javadoc)
786      * 
787      * @see gnu.hylafax.ClientProtocol#mdmfmt(java.lang.String)
788      */
789     public synchronized void mdmfmt(String value) throws IOException,
790 	    ServerResponseException {
791 	ostream.write("mdmfmt \"" + value + "\"\r\n");
792 	ostream.flush();
793 	log.debug("-> mdmfmt " + value);
794 
795 	String response = istream.readLine();
796 	log.debug(response);
797 
798 	StringTokenizer st = new StringTokenizer(response);
799 	if (!st.nextToken().equals("200")) {
800 	    // problem
801 	    throw (new ServerResponseException(response));
802 	}
803     }
804 
805     /*
806      * (non-Javadoc)
807      * 
808      * @see gnu.inet.ftp.FtpClientProtocol#open()
809      */
810     public synchronized void open() throws UnknownHostException, IOException,
811 	    ServerResponseException {
812 	open("localhost");
813     }
814 
815     /*
816      * (non-Javadoc)
817      * 
818      * @see gnu.inet.ftp.FtpClientProtocol#open(java.lang.String)
819      */
820     public synchronized void open(String host) throws UnknownHostException,
821 	    IOException, ServerResponseException {
822 	open(host, DEFAULT_PORT); // connect to default port
823     }
824 
825     /*
826      * (non-Javadoc)
827      * 
828      * @see gnu.inet.ftp.FtpClientProtocol#open(java.lang.String, int)
829      */
830     public synchronized void open(String host, int p)
831 	    throws UnknownHostException, IOException, ServerResponseException {
832 	connect(host, p); // connect to default port
833 	log.debug("Connected to: " + getServerVersion());
834     }
835 
836     /*
837      * (non-Javadoc)
838      * 
839      * @see gnu.hylafax.ClientProtocol#rcvfmt()
840      */
841     public synchronized String rcvfmt() throws IOException,
842 	    ServerResponseException {
843 	ostream.write("rcvfmt\r\n");
844 	ostream.flush();
845 	log.debug("-> rcvfmt");
846 
847 	String response = istream.readLine();
848 	log.debug(response);
849 
850 	if (!response.substring(0, 3).equals("200")) {
851 	    // error setting rcvfmt
852 	    throw (new ServerResponseException(response));
853 	}
854 
855 	return response.substring(3);
856     }
857 
858     /*
859      * (non-Javadoc)
860      * 
861      * @see gnu.hylafax.ClientProtocol#rcvfmt(java.lang.String)
862      */
863     public synchronized void rcvfmt(String value) throws IOException,
864 	    ServerResponseException {
865 	ostream.write("rcvfmt \"" + value + "\"\r\n");
866 	ostream.flush();
867 	log.debug("-> rcvfmt \"" + value + "\"\n");
868 
869 	String response = istream.readLine();
870 	log.debug(response);
871 
872 	StringTokenizer st = new StringTokenizer(response);
873 	if (!st.nextToken().equals("200")) {
874 	    throw (new ServerResponseException(response));
875 	}
876     }
877 
878     /*
879      * (non-Javadoc)
880      * 
881      * @see gnu.hylafax.ClientProtocol#site(java.lang.String, int)
882      */
883     public synchronized void site(String parm, int value) throws IOException,
884 	    ServerResponseException {
885 	site(parm, Integer.toString(value));
886     }
887 
888     /*
889      * (non-Javadoc)
890      * 
891      * @see gnu.hylafax.ClientProtocol#site(java.lang.String, long)
892      */
893     public synchronized void site(String parm, long value) throws IOException,
894 	    ServerResponseException {
895 	site(parm, Long.toString(value));
896     }
897 
898     /*
899      * (non-Javadoc)
900      * 
901      * @see gnu.hylafax.ClientProtocol#site(java.lang.String, java.lang.Object)
902      */
903     public synchronized void site(String parm, Object value)
904 	    throws IOException, ServerResponseException {
905 	site(parm, value.toString());
906     }
907 
908     /*
909      * (non-Javadoc)
910      * 
911      * @see gnu.hylafax.ClientProtocol#site(java.lang.String, java.lang.String)
912      */
913     public synchronized void site(String parm, String value)
914 	    throws IOException, ServerResponseException {
915 	String response;
916 	StringTokenizer st;
917 
918 	ostream.write("site " + parm + " " + value + "\r\n");
919 	ostream.flush();
920 
921 	log.debug("-> site " + parm + " " + value);
922 
923 	response = istream.readLine();
924 	log.debug(response);
925 
926 	st = new StringTokenizer(response);
927 
928 	String return_code = st.nextToken();
929 	if ((!return_code.equals("213")) && (!return_code.equals("200"))
930 		&& (!return_code.equals("150"))) {
931 	    throw (new ServerResponseException(response));
932 	}
933     }
934 
935     /*
936      * (non-Javadoc)
937      * 
938      * @see gnu.inet.ftp.FtpClientProtocol#size(java.lang.String)
939      */
940     public synchronized long size(String pathname) throws IOException,
941 	    FileNotFoundException, ServerResponseException {
942 	ostream.write("size " + pathname + "\r\n");
943 	ostream.flush();
944 	log.debug("-> size " + pathname);
945 
946 	String response = istream.readLine();
947 	log.debug(response);
948 
949 	StringTokenizer st = new StringTokenizer(response);
950 	String return_code = st.nextToken();
951 	if (!return_code.equals("213")) {
952 	    if (return_code.equals("550")) {
953 		throw (new FileNotFoundException(response));
954 	    }
955 	    throw (new ServerResponseException(response));
956 	}
957 
958 	// get file size from response
959 	return Long.parseLong(st.nextToken());
960     }
961 
962     /*
963      * (non-Javadoc)
964      * 
965      * @see gnu.inet.ftp.FtpClientProtocol#stot(java.io.InputStream)
966      */
967     public synchronized String stot(InputStream data) throws IOException,
968 	    ServerResponseException {
969 	String filename;
970 	String response;
971 	StringTokenizer st;
972 
973 	// send stot command to server
974 	ostream.write("stot\r\n");
975 	ostream.flush();
976 
977 	log.debug("-> stot");
978 
979 	// get results
980 	response = istream.readLine();
981 	log.debug(response);
982 
983 	st = new StringTokenizer(response);
984 	if (!st.nextToken().equals("150")) {
985 	    throw (new ServerResponseException(response));
986 	}
987 	st.nextToken(); // ignore 'FILE:' string
988 	filename = new String(st.nextToken()); // get filename value
989 
990 	// transfering ...
991 
992 	// next line tells us transfer completed
993 	response = istream.readLine();
994 	log.debug(response);
995 
996 	st = new StringTokenizer(response);
997 	if (!st.nextToken().equals("226")) {
998 	    // some sort of error
999 	    throw (new ServerResponseException(response));
1000 	}
1001 
1002 	return filename;
1003     }
1004 
1005     /*
1006      * (non-Javadoc)
1007      * 
1008      * @see gnu.hylafax.ClientProtocol#tzone(java.lang.String)
1009      */
1010     public synchronized void tzone(String value) throws IOException,
1011 	    ServerResponseException {
1012 	hylafaxServerTimeZone = value;
1013 	ostream.write("tzone " + value + "\r\n");
1014 	ostream.flush();
1015 	log.debug("-> tzone " + value);
1016 
1017 	String response = new String(istream.readLine());
1018 	log.debug(response);
1019 
1020 	StringTokenizer st = new StringTokenizer(response);
1021 	if (!st.nextToken().equals("200")) {
1022 	    throw (new ServerResponseException(response));
1023 	}
1024     }
1025 
1026     /*
1027      * (non-Javadoc)
1028      * 
1029      * @see gnu.hylafax.ClientProtocol#vrfy(java.lang.String)
1030      */
1031     public synchronized InetAddress vrfy(String dialstring) throws IOException,
1032 	    ServerResponseException {
1033 	ostream.write("vrfy " + dialstring + "\r\n");
1034 	ostream.flush();
1035 	log.debug("-> vrfy " + dialstring);
1036 
1037 	String response = istream.readLine();
1038 	log.debug(response);
1039 
1040 	StringTokenizer st = new StringTokenizer(response);
1041 	if (!st.nextToken().equals("200")) {
1042 	    throw (new ServerResponseException(response));
1043 	}
1044 	return InetAddress.getByName(st.nextToken());
1045     }
1046 
1047 }