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