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
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
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
93
94
95
96 public synchronized void admin(String password) throws IOException,
97 ServerResponseException {
98
99 ostream.write("admin " + password + "\r\n");
100 ostream.flush();
101 log.debug("-> admin " + password);
102
103
104 String response = new String(istream.readLine());
105 log.debug(response);
106
107
108 StringTokenizer st = new StringTokenizer(response);
109 if (!st.nextToken().equals("230")) {
110
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
132
133
134
135 public synchronized void config(String parm, int value) throws IOException,
136 ServerResponseException {
137 config(parm, Integer.toString(value));
138 }
139
140
141
142
143
144
145 public void config(String parm, long value) throws IOException,
146 ServerResponseException {
147 config(parm, Long.toString(value));
148 }
149
150
151
152
153
154
155
156 public synchronized void config(String parm, Object value)
157 throws IOException, ServerResponseException {
158 config(parm, value.toString());
159 }
160
161
162
163
164
165
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
192
193
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
213
214
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
232
233
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
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
274
275
276
277 public synchronized long idle() throws IOException, ServerResponseException {
278
279 ostream.write("idle\r\n");
280 ostream.flush();
281 log.debug("-> idle");
282
283
284 String response = new String(istream.readLine());
285 log.debug(response);
286
287
288 StringTokenizer st = new StringTokenizer(response);
289 if (!st.nextToken().equals("213")) {
290
291 throw (new ServerResponseException(response));
292 }
293
294
295 Long l = new Long(st.nextToken());
296 return l.longValue();
297 }
298
299
300
301
302
303
304 public synchronized void idle(long timeout) throws IOException,
305 ServerResponseException {
306
307 ostream.write("idle " + timeout + "\r\n");
308 ostream.flush();
309 log.debug("-> idle " + timeout);
310
311
312 String response = new String(istream.readLine());
313 log.debug(response);
314
315
316 StringTokenizer st = new StringTokenizer(response);
317 if (!st.nextToken().equals("213")) {
318
319 throw (new ServerResponseException(response));
320 }
321 }
322
323
324
325
326
327
328 public synchronized void jdele(long jobid) throws IOException,
329 ServerResponseException {
330
331 ostream.write("jdele " + jobid + "\r\n");
332 ostream.flush();
333 log.debug("-> jdele " + jobid);
334
335
336 String response = new String(istream.readLine());
337 log.debug(response);
338
339
340 StringTokenizer st = new StringTokenizer(response);
341 if (!st.nextToken().equals("200")) {
342
343 throw (new ServerResponseException(response));
344 }
345 }
346
347
348
349
350
351
352 public synchronized void jintr(long jobid) throws IOException,
353 ServerResponseException {
354
355 ostream.write("jintr " + jobid + "\r\n");
356 ostream.flush();
357 log.debug("-> jintr " + jobid);
358
359
360 String response = new String(istream.readLine());
361 log.debug(response);
362
363
364 StringTokenizer st = new StringTokenizer(response);
365 if (!st.nextToken().equals("200")) {
366
367 throw (new ServerResponseException(response));
368 }
369 }
370
371
372
373
374
375
376 public synchronized void jkill(long jobid) throws IOException,
377 ServerResponseException {
378
379 ostream.write("jkill " + jobid + "\r\n");
380 ostream.flush();
381 log.debug("-> jkill " + jobid);
382
383
384 String response = new String(istream.readLine());
385 log.debug(response);
386
387
388 StringTokenizer st = new StringTokenizer(response);
389 if (!st.nextToken().equals("200")) {
390
391 throw (new ServerResponseException(response));
392 }
393 }
394
395
396
397
398
399
400 public synchronized void jnew() throws IOException, ServerResponseException {
401 jnew(true);
402 }
403
404
405
406
407
408
409 public synchronized void jnew(boolean inheritDefault) throws IOException,
410 ServerResponseException {
411 if (inheritDefault) {
412 job(0);
413 }
414
415 ostream.write("jnew\r\n");
416 ostream.flush();
417 log.debug("-> jnew");
418
419
420 String response = new String(istream.readLine());
421 log.debug(response);
422
423
424 StringTokenizer st = new StringTokenizer(response);
425 if (!st.nextToken().equals("200")) {
426
427 throw (new ServerResponseException(response));
428 }
429 }
430
431
432
433
434
435
436 public synchronized long job() throws IOException, ServerResponseException {
437
438 ostream.write("job\r\n");
439 ostream.flush();
440 log.debug("-> job");
441
442
443 String response = new String(istream.readLine());
444 log.debug(response);
445
446
447 StringTokenizer st = new StringTokenizer(response);
448 if (!st.nextToken().equals("200")) {
449
450 throw (new ServerResponseException(response));
451 }
452
453
454
455 st.nextToken();
456 st.nextToken();
457 st.nextToken();
458
459
460 try {
461 Long l = new Long(st.nextToken());
462 return l.longValue();
463 } catch (Exception e) {
464
465 return 0;
466 }
467 }
468
469
470
471
472
473
474 public synchronized void job(long val) throws IOException,
475 ServerResponseException {
476
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
483 String response = new String(istream.readLine());
484 log.debug(response);
485
486
487 StringTokenizer st = new StringTokenizer(response);
488 if (!st.nextToken().equals("200")) {
489
490 throw (new ServerResponseException(response));
491 }
492 }
493
494
495
496
497
498
499 public synchronized String jobfmt() throws IOException,
500 ServerResponseException {
501
502 ostream.write("jobfmt\r\n");
503 ostream.flush();
504 log.debug("-> jobfmt");
505
506
507 String response = new String(istream.readLine());
508 log.debug(response);
509
510
511 StringTokenizer st = new StringTokenizer(response);
512 String temp = new String(st.nextToken());
513 if (!temp.equals("200")) {
514
515 throw (new ServerResponseException(response));
516 }
517
518
519 return response.substring(temp.length());
520 }
521
522
523
524
525
526
527 public synchronized void jobfmt(String value) throws IOException,
528 ServerResponseException {
529
530 String command = new String("jobfmt \"" + value + "\"\r\n");
531 ostream.write(command);
532 ostream.flush();
533 log.debug("-> jobfmt " + value);
534
535
536 String response = new String(istream.readLine());
537 log.debug(response);
538
539
540 StringTokenizer st = new StringTokenizer(response);
541 if (!st.nextToken().equals("200")) {
542
543 throw (new ServerResponseException(response));
544 }
545 }
546
547
548
549
550
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
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
569 res += "\n" + response.substring(4);
570 }
571 return res;
572 }
573 throw (new ServerResponseException(response));
574 }
575
576
577
578
579
580
581 public synchronized void jparm(String parm, int value) throws IOException,
582 ServerResponseException {
583 jparm(parm, Integer.toString(value));
584 }
585
586
587
588
589
590
591 public synchronized void jparm(String parm, long value) throws IOException,
592 ServerResponseException {
593 jparm(parm, Long.toString(value));
594 }
595
596
597
598
599
600
601 public synchronized void jparm(String parm, Object value)
602 throws IOException, ServerResponseException {
603 jparm(parm, value.toString());
604 }
605
606
607
608
609
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
634
635
636
637 public synchronized void jrest() throws IOException,
638 ServerResponseException {
639
640 ostream.write("jrest\r\n");
641 ostream.flush();
642 log.debug("-> jrest");
643
644
645 String response = new String(istream.readLine());
646 log.debug(response);
647
648
649 StringTokenizer st = new StringTokenizer(response);
650 if (!st.nextToken().equals("200")) {
651
652 throw (new ServerResponseException(response));
653 }
654 }
655
656
657
658
659
660
661 public synchronized long jsubm() throws IOException,
662 ServerResponseException {
663
664 ostream.write("jsubm\r\n");
665 ostream.flush();
666 log.debug("-> jsubm");
667
668
669 String response = new String(istream.readLine());
670 log.debug(response);
671
672
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
684
685
686
687 public synchronized int jsubm(long jobid) throws IOException,
688 ServerResponseException {
689
690 ostream.write("jsubm " + jobid + "\r\n");
691 ostream.flush();
692 log.debug("-> jsubm " + jobid);
693
694
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
706
707
708 try {
709 st.nextToken();
710 String jobStr = st.nextToken();
711
712 jobID = Integer.parseInt(jobStr);
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
724
725
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
744
745
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
764
765
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
778 throw (new ServerResponseException(response));
779 }
780
781 return response.substring(3);
782 }
783
784
785
786
787
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
801 throw (new ServerResponseException(response));
802 }
803 }
804
805
806
807
808
809
810 public synchronized void open() throws UnknownHostException, IOException,
811 ServerResponseException {
812 open("localhost");
813 }
814
815
816
817
818
819
820 public synchronized void open(String host) throws UnknownHostException,
821 IOException, ServerResponseException {
822 open(host, DEFAULT_PORT);
823 }
824
825
826
827
828
829
830 public synchronized void open(String host, int p)
831 throws UnknownHostException, IOException, ServerResponseException {
832 connect(host, p);
833 log.debug("Connected to: " + getServerVersion());
834 }
835
836
837
838
839
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
852 throw (new ServerResponseException(response));
853 }
854
855 return response.substring(3);
856 }
857
858
859
860
861
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
880
881
882
883 public synchronized void site(String parm, int value) throws IOException,
884 ServerResponseException {
885 site(parm, Integer.toString(value));
886 }
887
888
889
890
891
892
893 public synchronized void site(String parm, long value) throws IOException,
894 ServerResponseException {
895 site(parm, Long.toString(value));
896 }
897
898
899
900
901
902
903 public synchronized void site(String parm, Object value)
904 throws IOException, ServerResponseException {
905 site(parm, value.toString());
906 }
907
908
909
910
911
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
937
938
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
959 return Long.parseLong(st.nextToken());
960 }
961
962
963
964
965
966
967 public synchronized String stot(InputStream data) throws IOException,
968 ServerResponseException {
969 String filename;
970 String response;
971 StringTokenizer st;
972
973
974 ostream.write("stot\r\n");
975 ostream.flush();
976
977 log.debug("-> stot");
978
979
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();
988 filename = new String(st.nextToken());
989
990
991
992
993 response = istream.readLine();
994 log.debug(response);
995
996 st = new StringTokenizer(response);
997 if (!st.nextToken().equals("226")) {
998
999 throw (new ServerResponseException(response));
1000 }
1001
1002 return filename;
1003 }
1004
1005
1006
1007
1008
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
1028
1029
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 }