2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2003 Net Integration Technologies, Inc.
5 * Implementation of the LPD protocol (RFC1179).
7 * We implement the bare minimum to make (most) application produce
8 * the expected output on paper. We break the protocol in many
9 * wonderful ways, in the name of being ultra-simple and safe.
11 * How ultra-simple? We print whatever data we get transferred and the
12 * "interesting" attributes of the last control file we see gets to be
13 * applied to all the data files we got!
15 * "We don't care." -- apenwarr, our fearless leader.
17 * $Id: lpd.cc,v 1.34 2002/07/31 23:08:04 pphaneuf Exp $
26 #define MAX_CONTROL_FILE_SIZE 8192
27 #define DATA_BUFFER_SIZE 32767
29 WvLPDConnection::WvLPDConnection(WvPrint
&_wvprint
,
32 bool _stupid_klpq_hack
):
33 WvStreamClone(_conn
), wvprint(_wvprint
),
34 log(_log
), state(INITIAL
), queue(0), data(0),
35 left(0), job(0), username("unknown"), filename("unknown"),
36 jobname("unknown"), hostname("unknown"), copies(1),
37 stupid_klpq_hack(_stupid_klpq_hack
), seen_job(false)
40 log(WvLog::Debug1
, "LPD connection from %s\n",
41 *static_cast<WvTCPConn
*>(cloned
)->src());
44 WvLPDConnection::~WvLPDConnection()
46 PrintJobList::Iter
i(jobs
);
48 /* FIXME: provide some way to match this message with others on the
50 log(WvLog::Debug1
, "client disconnected\n");
52 for (i
.rewind(); i
.next(); )
54 i().username
= username
;
55 i().jobname
= jobname
;
56 i().hostname
= hostname
;
58 i().spoolInfo
->filename
= filename
;
65 void WvLPDConnection::state_initial()
73 str
= trim_string(str
);
75 params
.split(&str
[1]);
77 /* Pick out the queue name from the parameters. */
80 log(WvLog::Warning
, "no queue specified, disconnecting\n");
85 queuename
= *params
.first();
86 if (queuename
== WVPRINT_MAGIC_PROBE
)
88 print(WVPRINT_MAGIC_PROBE
"\n");
89 wvprint
.advertise_queues(AdvertiseQueueCallback(this, &WvLPDConnection::callback_probe
));
94 queue
= wvprint
.get_queue(queuename
);
95 params
.unlink_first();
100 state
= PRINT_WAITING
;
103 /* FIXME: We always accept the job if the queue exists. Maybe we
112 log(WvLog::Warning
, "unknown printer \"%s\"\n", queuename
);
122 state
= QUEUE_STATE_LONG
;
128 log(WvLog::Warning
, "incorrect LPD command, disconnecting\n");
135 * We have to poke ourselves because we might not be readable
136 * anymore (we possibly read everything the client had to say)
142 void WvLPDConnection::state_print_waiting()
145 * This LPD command seems to be pretty useless. It's use seems to be
146 * to prod the print server so that it starts printing right now (if
147 * the printer was down and the server didn't notice it was back up
148 * yet for example). Hopefully, WvPrint will be smarter and won't
149 * need something like that.
155 void WvLPDConnection::state_receive_job()
162 str
= trim_string(str
);
164 log(WvLog::Debug
, "we're in state_receive_job (and got \"%s\")\n", str
);
167 params
.split(&str
[1]);
172 log(WvLog::Debug
, "got the unsupported \"Abort job\" subcommand\n");
178 left
= params
.first()->num();
179 /* FIXME: arbitrary limit here. */
180 if (left
> MAX_CONTROL_FILE_SIZE
)
190 state
= RECEIVE_CONTROL
;
194 left
= params
.first()->num();
195 job
= queue
->new_job();
198 /* Sorry, try again later. */
204 jobs
.append(job
, false);
206 data
= new WvFile(job
->get_file(), O_CREAT
|O_TRUNC
|O_WRONLY
, 0600);
209 log(WvLog::Error
, "could not open spool file (%s)\n", job
->get_file());
217 state
= RECEIVE_DATA
;
221 /* FIXME: this is not the correct error message. */
222 log(WvLog::Warning
, "incorrect LPD command, disconnecting\n");
230 log(WvLog::Debug
, "we're in state_receive_job\n");
234 void WvLPDConnection::state_receive_control()
236 /* We are assuming that "left" is limited to a sensible number. */
237 char controlraw
[left
+ 1];
240 WvStringList::Iter
i(lines
);
242 /* Just being defensive. */
244 rv
= read(controlraw
, left
+ 1);
245 log(WvLog::Debug
, "state_receive_control: read %s bytes (%s left)\n", rv
, left
- rv
+ 1);
249 log(WvLog::Debug
, "why were we called if there was nothing to read?\n");
255 if ((rv
- 1) == left
)
257 /* Check if the client is trying to mess with us. */
258 if (controlraw
[left
] != 0)
260 log(WvLog::Warning
, "control file transfer from the client went bad\n");
268 lines
.split(trim_string(controlraw
), "\r\n");
275 log(WvLog::Debug
, "class for banner page: %s\n", *i
+ 1);
278 log(WvLog::Debug
, "host name: %s\n", *i
+ 1);
282 log(WvLog::Debug
, "indenting count: %s\n", *i
+ 1);
285 log(WvLog::Debug
, "job name for banner page: %s\n", *i
+ 1);
289 log(WvLog::Debug
, "print banner page (user: %s)\n", *i
+ 1);
292 log(WvLog::Debug
, "mail when printed: %s\n", *i
+ 1);
295 log(WvLog::Debug
, "name of source file: %s\n", *i
+ 1);
299 log(WvLog::Debug
, "user identification: %s\n", *i
+ 1);
303 log(WvLog::Debug
, "symlink data: %s\n", *i
+ 1);
306 log(WvLog::Debug
, "title: %s\n", *i
+ 1);
309 log(WvLog::Debug
, "unlink data file: %s\n", *i
+ 1);
312 log(WvLog::Debug
, "width of output: %s\n", *i
+ 1);
315 log(WvLog::Debug
, "troff R font: %s\n", *i
+ 1);
318 log(WvLog::Debug
, "troff I font: %s\n", *i
+ 1);
321 log(WvLog::Debug
, "troff B font: %s\n", *i
+ 1);
324 log(WvLog::Debug
, "troff S font: %s\n", *i
+ 1);
327 log(WvLog::Debug
, "plot CIF file: %s\n", *i
+ 1);
330 log(WvLog::Debug
, "print DVI file: %s\n", *i
+ 1);
334 log(WvLog::Debug
, "print formatted file: %s\n", *i
+ 1);
338 log(WvLog::Debug
, "plot file: %s\n", *i
+ 1);
342 log(WvLog::Debug
, "Kerberos info: %s\n", *i
+ 1);
345 log(WvLog::Debug
, "print file leaving control chars: %s\n", *i
+ 1);
349 log(WvLog::Debug
, "print ditroff output file: %s\n", *i
+ 1);
353 log(WvLog::Debug
, "print PostScript file: %s\n", *i
+ 1);
357 log(WvLog::Debug
, "print file with 'pr' format: %s\n", *i
+ 1);
361 log(WvLog::Debug
, "print file with FORTRAN carriage control: %s\n", *i
+ 1);
365 log(WvLog::Debug
, "print troff output file: %s\n", *i
+ 1);
369 log(WvLog::Debug
, "print raster file: %s\n", *i
+ 1);
373 log(WvLog::Debug
, "reserved for future use with the Palladium print system\n");
376 log(WvLog::Debug
, "Unknown control file line: %s\n", *i
);
380 /* FIXME: should verify the content of the control file before
387 log(WvLog::Warning
, "could not read the control file to the end?\n");
394 void WvLPDConnection::state_receive_data()
396 char buf
[DATA_BUFFER_SIZE
];
399 rv
= read(buf
, left
> DATA_BUFFER_SIZE
? DATA_BUFFER_SIZE
: left
);
400 log(WvLog::Debug
, "state_receive_data: read %s bytes (%s left)\n", rv
, left
- rv
);
403 data
->write(buf
, rv
);
408 state
= RECEIVE_DATA_FIN
;
412 void WvLPDConnection::state_receive_data_fin()
423 log(WvLog::Warning
, "data file transfer from the client went bad\n");
425 queue
->cancel(job
->get_id());
438 void WvLPDConnection::state_queue_state()
442 print("unknown printer\n");
448 if (queue
->job_count())
450 /* FIXME: here, we should give *accurate* status. */
451 print("%s is ready and printing\n", queue
->name
);
452 print("Rank Owner Job Files Total Size\n");
455 print("no entries\n");
457 queue
->foreach(PrintJobInfoCallback(this, &WvLPDConnection::send_printjobinfo
));
459 /* The rest of the work, including the disconnection, is done in
460 * the callback. So we go to eternal sleep. */
464 void WvLPDConnection::state_queue_state_long()
466 /* FIXME: for the moment, this is the same as the short status. */
471 void WvLPDConnection::send_printjobinfo(const PrintJobInfo
* info
)
473 WvStringList::Iter
i(params
);
479 if (queue
->lpq_crap
&& !(stupid_klpq_hack
&& seen_job
))
480 print(queue
->lpq_crap
);
493 if (isdigit(*static_cast<const char*>(*i
)))
495 if (static_cast<unsigned int>(atoi(*i
)) == info
->id
)
500 if (strcmp(*i
, info
->username
) == 0)
505 if (params
.isempty())
510 switch(info
->position
)
525 position
= WvString("%s%s", info
->position
, "th");
529 print("%-6s %-10s %-4s %-37s %s bytes\n", position
,
530 info
->username
, info
->id
, info
->filename
, info
->size
);
534 void WvLPDConnection::state_remove_job()
537 WvStringList::Iter
i(params
);
541 if (params
.isempty())
543 log(WvLog::Error
, "no user name specified to the remove job command, disconnecting\n");
549 user
= *params
.first();
550 params
.unlink_first();
552 if (params
.isempty())
554 unsigned int printing
= queue
->get_active_id();
558 if (queue
->cancel(printing
, user
))
559 print("job %s canceled\n", printing
);
561 print("could not cancel job %s\n", printing
);
571 print("param: %s\n", *i
);
573 if (isdigit(*static_cast<const char*>(*i
)))
575 if (queue
->cancel(atoi(*i
), user
))
576 print("job %s canceled\n", atoi(*i
));
578 print("could not cancel job %s\n", atoi(*i
));
582 print("removal of jobs by user name not implemented yet!\n");
585 queue
->foreach(PrintJobInfoCallback(this, &WvLPDConnection::cancel_job
));
589 /* FIXME: this might be a bug. If the queue->foreach() is not executed
590 * all in one go, then we might still be talking to the other end at
591 * this point. But there is no obvious place to close the
597 void WvLPDConnection::execute()
599 WvStreamClone::execute();
607 state_print_waiting();
612 case RECEIVE_CONTROL
:
613 state_receive_control();
616 state_receive_data();
618 case RECEIVE_DATA_FIN
:
619 state_receive_data_fin();
624 case QUEUE_STATE_LONG
:
625 state_queue_state_long();
631 log(WvLog::Debug
, "WvLPDConnection::execute called with state CLOSE\n");
635 /* FIXME: here, we don't want to do anything, but if by
636 * accident we become readable or something, we'll just get
637 * called as fast as possible. */
640 log(WvLog::Critical
, "bug in WvLPDConnection: invalid state\n");
646 void WvLPDConnection::callback_probe(WvStringParm queue
,
647 WvStringParm printertype
,
648 WvStringParm description
)
650 if (!queue
&& !printertype
&& !description
)
652 flush_then_close(60000);
659 print(":%s", printertype
);
664 print(":%s\n", description
);
669 void WvLPDConnection::cancel_job(const PrintJobInfo
* info
)
671 if (info
&& (info
->username
== username
))
673 if (queue
->cancel(info
->id
, agentname
))
674 print("job %s canceled\n", info
->id
);
676 print("could not cancel job %s\n", info
->id
);