1 /* TN5250 - An implementation of the 5250 telnet protocol.
2  * Copyright (C) 1997-2008 Michael Madore
3  *
4  * This file is part of TN5250.
5  *
6  * TN5250 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1, or (at your option)
9  * any later version.
10  *
11  * TN5250 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this software; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307 USA
20  *
21  */
22 
23 #include "tn5250-private.h"
24 
25 #ifndef WIN32
26 
27 
28 static const struct response_code {
29    const char * code;
30    int retval;
31    const char * text;
32 } response_codes[] = {
33    { "I901", 1, "Virtual device has less function than source device." },
34    { "I902", 1, "Session successfully started." },
35    { "I906", 1, "Automatic sign-on requested, but not allowed.  A sign-on screen will follow." },
36    { "2702", 0, "Device description not found." },
37    { "2703", 0, "Controller description not found." },
38    { "2777", 0, "Damaged device description." },
39    { "8901", 0, "Device not varied on." },
40    { "8902", 0, "Device not available." },
41    { "8903", 0, "Device not valid for session." },
42    { "8906", 0, "Session initiation failed." },
43    { "8907", 0, "Session failure." },
44    { "8910", 0, "Controller not valid for session." },
45    { "8916", 0, "No matching device found." },
46    { "8917", 0, "Not authorized to object." },
47    { "8918", 0, "Job cancelled." },
48    { "8920", 0, "Object partially damaged." },  /* As opposed to fully damaged? */
49    { "8921", 0, "Communications error." },
50    { "8922", 0, "Negative response received." }, /* From what?!? */
51    { "8923", 0, "Start-up record built incorrectly." },
52    { "8925", 0, "Creation of device failed." },
53    { "8928", 0, "Change of device failed." },
54    { "8929", 0, "Vary on or vary off failed." },
55    { "8930", 0, "Message queue does not exist." },
56    { "8934", 0, "Start up for S/36 WSF received." },
57    { "8935", 0, "Session rejected." },
58    { "8936", 0, "Security failure on session attempt." },
59    { "8937", 0, "Automatic sign-on rejected." },
60    { "8940", 0, "Automatic configuration failed or not allowed." },
61    { "I904", 0, "Source system at incompatible release." },
62    { NULL, 0, NULL }
63 };
64 
65 static int tn5250_print_session_waitevent(Tn5250PrintSession * This);
66 
67 /****f* lib5250/tn5250_print_session_new
68  * NAME
69  *    tn5250_print_session_new
70  * SYNOPSIS
71  *    ret = tn5250_print_session_new ();
72  * INPUTS
73  *    None
74  * DESCRIPTION
75  *    DOCUMENT ME!!!
76  *****/
tn5250_print_session_new()77 Tn5250PrintSession *tn5250_print_session_new()
78 {
79    Tn5250PrintSession *This;
80 
81    This = tn5250_new(Tn5250PrintSession, 1);
82    if (This == NULL)
83 	   return NULL;
84 
85    This->rec = tn5250_record_new();
86    if (This->rec == NULL) {
87 	   free (This);
88 	   return NULL;
89    }
90 
91    This->stream = NULL;
92    This->printfile = NULL;
93    This->output_cmd = NULL;
94    This->conn_fd = -1;
95    This->map = NULL;
96    This->script_slot = NULL;
97 
98    return This;
99 }
100 
101 /****f* lib5250/tn5250_print_session_destroy
102  * NAME
103  *    tn5250_print_session_destroy
104  * SYNOPSIS
105  *    tn5250_print_session_destroy (This);
106  * INPUTS
107  *    Tn5250PrintSession * This       -
108  * DESCRIPTION
109  *    DOCUMENT ME!!!
110  *****/
tn5250_print_session_destroy(Tn5250PrintSession * This)111 void tn5250_print_session_destroy(Tn5250PrintSession * This)
112 {
113    if (This->stream != NULL)
114       tn5250_stream_destroy(This->stream);
115    if (This->rec != NULL)
116       tn5250_record_destroy(This->rec);
117    if (This->output_cmd != NULL)
118       free(This->output_cmd);
119    if (This->map != NULL)
120       tn5250_char_map_destroy (This->map);
121    free (This);
122 }
123 
124 /****f* lib5250/tn5250_print_session_set_fd
125  * NAME
126  *    tn5250_print_session_set_fd
127  * SYNOPSIS
128  *    tn5250_print_session_set_fd (This, fd);
129  * INPUTS
130  *    Tn5250PrintSession * This       -
131  *    SOCKET_TYPE          fd         -
132  * DESCRIPTION
133  *    DOCUMENT ME!!!
134  *****/
tn5250_print_session_set_fd(Tn5250PrintSession * This,SOCKET_TYPE fd)135 void tn5250_print_session_set_fd(Tn5250PrintSession * This, SOCKET_TYPE fd)
136 {
137    This->conn_fd = fd;
138 }
139 
140 /****f* lib5250/tn5250_print_session_set_stream
141  * NAME
142  *    tn5250_print_session_set_stream
143  * SYNOPSIS
144  *    tn5250_print_session_set_stream (This, newstream);
145  * INPUTS
146  *    Tn5250PrintSession * This       -
147  *    Tn5250Stream *       newstream  -
148  * DESCRIPTION
149  *    DOCUMENT ME!!!
150  *****/
tn5250_print_session_set_stream(Tn5250PrintSession * This,Tn5250Stream * newstream)151 void tn5250_print_session_set_stream(Tn5250PrintSession * This, Tn5250Stream * newstream)
152 {
153    if (This->stream != NULL)
154       tn5250_stream_destroy (This->stream);
155    This->stream = newstream;
156 }
157 
158 /****f* lib5250/tn5250_print_session_set_char_map
159  * NAME
160  *    tn5250_print_session_set_char_map
161  * SYNOPSIS
162  *    tn5250_print_session_set_char_map (This, map);
163  * INPUTS
164  *    Tn5250PrintSession * This       -
165  *    const char *         map        -
166  * DESCRIPTION
167  *    Sets the current translation map for this print session.  This is
168  *    used to translate response codes to something we can use.
169  *****/
tn5250_print_session_set_char_map(Tn5250PrintSession * This,const char * map)170 void tn5250_print_session_set_char_map (Tn5250PrintSession *This, const char *map)
171 {
172    if (This->map != NULL)
173       tn5250_char_map_destroy (This->map);
174    This->map = tn5250_char_map_new (map);
175 }
176 
177 /****f* lib5250/tn5250_print_session_set_output_command
178  * NAME
179  *    tn5250_print_session_set_output_command
180  * SYNOPSIS
181  *    tn5250_print_session_set_output_command (This, output_cmd);
182  * INPUTS
183  *    Tn5250PrintSession * This       -
184  *    const char *         output_cmd -
185  * DESCRIPTION
186  *    DOCUMENT ME!!!
187  *****/
tn5250_print_session_set_output_command(Tn5250PrintSession * This,const char * output_cmd)188 void tn5250_print_session_set_output_command(Tn5250PrintSession * This, const char *output_cmd)
189 {
190    if (This->output_cmd != NULL)
191       free(This->output_cmd);
192    This->output_cmd = (char *) malloc(strlen(output_cmd) + 1);
193    strcpy(This->output_cmd, output_cmd);
194 }
195 
196 /****f* lib5250/tn5250_print_session_get_response_code
197  * NAME
198  *    tn5250_print_session_get_response_code
199  * SYNOPSIS
200  *    rc = tn5250_print_session_get_response_code (This, code);
201  * INPUTS
202  *    Tn5250PrintSession * This       -
203  *    char *               code       -
204  * DESCRIPTION
205  *    Retrieves the response code from the startup response record.  The
206  *    function returns 1 for a successful startup, and 0 otherwise.  On return,
207  *    code contains the 5 character response code.
208  *****/
tn5250_print_session_get_response_code(Tn5250PrintSession * This,char * code)209 int tn5250_print_session_get_response_code(Tn5250PrintSession * This, char *code)
210 {
211 
212    /* Offset of first byte of data after record variable-length header. */
213    int o = 6 + tn5250_record_data(This->rec)[6];
214    int i;
215 
216    for (i = 0; i < 4; i++) {
217       if (This->map == NULL)
218 	 code[i] = tn5250_record_data(This->rec)[o+i+5];
219       else {
220 	 code[i] = tn5250_char_map_to_local (This->map,
221 	       tn5250_record_data(This->rec)[o+i+5]
222 	       );
223       }
224    }
225 
226    code[4] = '\0';
227    for (i = 0; i < sizeof (response_codes)/sizeof (struct response_code); i++) {
228       if (!strcmp (response_codes[i].code, code)) {
229          syslog(LOG_INFO, "%s : %s", response_codes[i].code, response_codes[i].text);
230 	 return response_codes[i].retval;
231       }
232    }
233    return 0;
234 }
235 
236 /****f* lib5250/tn5250_print_session_main_loop
237  * NAME
238  *    tn5250_print_session_main_loop
239  * SYNOPSIS
240  *    tn5250_print_session_main_loop (This);
241  * INPUTS
242  *    Tn5250PrintSession * This       -
243  * DESCRIPTION
244  *    This function continually loops, waiting for print jobs from the AS/400.
245  *    When it gets one, it sends it to the output command which was specified
246  *    on the command line.  If the host closes the socket, we exit.
247  *****/
tn5250_print_session_main_loop(Tn5250PrintSession * This)248 void tn5250_print_session_main_loop(Tn5250PrintSession * This)
249 {
250    int pcount;
251    int newjob;
252    char responsecode[5];
253    StreamHeader header;
254 
255    while (1) {
256       if (tn5250_print_session_waitevent(This)) {
257 	 if( tn5250_stream_handle_receive(This->stream) ) {
258 	    pcount = tn5250_stream_record_count(This->stream);
259 	    if (pcount > 0) {
260 	       if (This->rec != NULL)
261 	          tn5250_record_destroy(This->rec);
262 	       This->rec = tn5250_stream_get_record(This->stream);
263 	       if (!tn5250_print_session_get_response_code(This, responsecode))
264 	          exit (1);
265 	       break;
266 	    }
267 	 }
268 	 else {
269 	    syslog(LOG_INFO, "Socket closed by host.");
270 	    exit(-1);
271 	 }
272       }
273 
274    }
275 
276 
277    newjob = 1;
278    while (1) {
279       if (tn5250_print_session_waitevent(This)) {
280 	 if( tn5250_stream_handle_receive(This->stream) ) {
281 	    pcount = tn5250_stream_record_count(This->stream);
282 	    if (pcount > 0) {
283 	       if (newjob) {
284 	          char *output_cmd;
285 	          if ((output_cmd = This->output_cmd) == NULL)
286 		     output_cmd = "scs2ascii |lpr";
287 	          This->printfile = popen(output_cmd, "w");
288 	          TN5250_ASSERT(This->printfile != NULL);
289 	          newjob = 0;
290 	       }
291 	       if (This->rec != NULL)
292 	          tn5250_record_destroy(This->rec);
293 	       This->rec = tn5250_stream_get_record(This->stream);
294 
295 	       if(tn5250_record_opcode(This->rec)
296 		  == TN5250_RECORD_OPCODE_CLEAR)
297 		 {
298 		   syslog(LOG_INFO, "Clearing print buffers");
299 		   continue;
300 		 }
301 
302 	       header.h5250.flowtype = TN5250_RECORD_FLOW_CLIENTO;
303 	       header.h5250.flags    = TN5250_RECORD_H_NONE;
304 	       header.h5250.opcode   = TN5250_RECORD_OPCODE_PRINT_COMPLETE;
305 
306 	       tn5250_stream_send_packet(This->stream, 0,
307 					 header,
308 					 NULL);
309 
310 	       if (tn5250_record_length(This->rec) == 0x11) {
311 		 syslog(LOG_INFO, "Job Complete\n");
312 		 pclose(This->printfile);
313 		 newjob = 1;
314 	       } else {
315 		 while (!tn5250_record_is_chain_end(This->rec))
316 		   fprintf(This->printfile, "%c",
317 			   tn5250_record_get_byte(This->rec));
318 	       }
319 	    }
320 	 }
321 	 else {
322 	    syslog(LOG_INFO, "Socket closed by host");
323 	    exit(-1);
324 	 }
325       }
326    }
327 }
328 
329 /****f* lib5250/tn5250_print_session_waitevent
330  * NAME
331  *    tn5250_print_session_waitevent
332  * SYNOPSIS
333  *    ret = tn5250_print_session_waitevent (This);
334  * INPUTS
335  *    Tn5250PrintSession * This       -
336  * DESCRIPTION
337  *    Calls select() to wait for data to arrive on the socket fdr.
338  *    This is the socket being used by the print session to communicate
339  *    with the AS/400.  There is no timeout, so the function will wait forever
340  *    if no data arrives.
341  *****/
tn5250_print_session_waitevent(Tn5250PrintSession * This)342 static int tn5250_print_session_waitevent(Tn5250PrintSession * This)
343 {
344    fd_set fdr;
345    int result = 0;
346 
347    FD_ZERO(&fdr);
348    FD_SET(This->conn_fd, &fdr);
349    select(This->conn_fd + 1, &fdr, NULL, NULL, NULL);
350 
351    if (FD_ISSET(This->conn_fd, &fdr))
352       result = 1;
353 
354    return result;
355 }
356 
357 #endif /* ifndef WIN32 */
358 /* vi:set sts=3 sw=3: */
359 
360 
361 
362 
363 
364 
365 
366 
367