1/*
2 * $Id: queries.c,v 1.22 2009-10-13 22:55:37 didg Exp $
3 *
4 * Copyright (c) 1990,1994 Regents of The University of Michigan.
5 * All Rights Reserved.  See COPYRIGHT.
6 */
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif /* HAVE_CONFIG_H */
11
12#include <string.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <atalk/logger.h>
16#include <sys/param.h>
17#include <sys/time.h>
18#include <sys/types.h>
19#include <netatalk/at.h>
20#include <atalk/atp.h>
21
22#ifdef KRB
23#ifdef SOLARIS
24#include <kerberos/krb.h>
25#else /* SOLARIS */
26#include <krb.h>
27#endif /* SOLARIS */
28#endif /* KRB */
29
30#include "file.h"
31#include "comment.h"
32#include "printer.h"
33#include "ppd.h"
34#include "lp.h"
35#include "uam_auth.h"
36
37int cq_default( struct papfile *, struct papfile * );
38int cq_k4login( struct papfile *, struct papfile * );
39int cq_uameth( struct papfile *, struct papfile * );
40
41int gq_balance( struct papfile * );
42int gq_pagecost( struct papfile * );
43int gq_true( struct papfile * );
44int gq_rbispoolerid( struct papfile * );
45int gq_rbiuamlist( struct papfile * );
46
47int cq_query( struct papfile *, struct papfile * );
48void cq_font_answer( char *, char *, struct papfile * );
49int cq_font( struct papfile *, struct papfile * );
50int cq_feature( struct papfile *, struct papfile * );
51int cq_printer( struct papfile *, struct papfile * );
52int cq_rmjob( struct papfile *, struct papfile * );
53#ifndef HAVE_CUPS
54int cq_listq( struct papfile *, struct papfile * );
55int cq_rbilogin( struct papfile *, struct papfile * );
56#endif  /* HAVE_CUPS */
57
58
59
60int cq_default( struct papfile *in, struct papfile *out)
61{
62    char		*start, *stop, *p;
63    int			linelength, crlflength;
64    struct papd_comment	*comment = compeek();
65
66    for (;;) {
67	switch ( markline( in, &start, &linelength, &crlflength )) {
68	case 0 :
69	    return( 0 );
70
71	case -1 :
72	    return( CH_MORE );
73
74        case -2 :
75            return( CH_ERROR );
76	}
77
78	stop = start+linelength;
79
80	if ( comgetflags() == 0 ) {	/* started */
81	    if ( comment->c_end ) {
82		comsetflags( 1 );
83	    } else {
84		compop();
85		CONSUME( in, linelength + crlflength );
86		return( CH_DONE );
87	    }
88	} else {
89	    /* return default */
90	    if ( comcmp( start, start+linelength, comment->c_end, 0 ) == 0 ) {
91		for ( p = start; p < stop; p++ ) {
92		    if ( *p == ':' ) {
93			break;
94		    }
95		}
96		if (p < stop) {
97		    p++;
98		    while ( *p == ' ' ) {
99		        p++;
100                    }
101		}
102
103		append( out, p, stop - p + crlflength );
104		compop();
105		CONSUME( in, linelength + crlflength );
106		return( CH_DONE );
107	    }
108	}
109
110	CONSUME( in, linelength + crlflength );
111    }
112}
113
114#ifdef KRB
115char	*LoginOK = "LoginOK\n";
116char	*LoginFailed = "LoginFailed\n";
117
118#define h2b(x)	(isdigit((x))?(x)-'0':(isupper((x))?(x)-'A':(x)-'a')+10)
119
120int cq_k4login( struct papfile *in, struct papfile *out)
121{
122    char		*start, *p;
123    int			linelength, crlflength;
124    unsigned char	*t;
125    struct papd_comment	*comment = compeek();
126    KTEXT_ST		tkt;
127    AUTH_DAT		ad;
128    int			rc, i;
129
130    switch ( markline( in, &start, &linelength, &crlflength )) {
131    case 0 :
132	return( 0 );
133
134    case -1 :
135	return( CH_MORE );
136
137    case -2 :
138        return( CH_ERROR );
139    }
140
141    p = start + strlen( comment->c_begin );
142    while ( *p == ' ' ) {
143	p++;
144    }
145
146    bzero( &tkt, sizeof( tkt ));
147    stop = start+linelength;
148    /* FIXME */
149    for ( i = 0, t = tkt.dat; p < stop; p += 2, t++, i++ ) {
150	*t = ( h2b( (unsigned char)*p ) << 4 ) +
151		h2b( (unsigned char)*( p + 1 ));
152    }
153    tkt.length = i;
154
155    if (( rc = krb_rd_req( &tkt, "LaserWriter", printer->p_name,
156	    0, &ad, "" )) != RD_AP_OK ) {
157	LOG(log_error, logtype_papd, "cq_k4login: %s", krb_err_txt[ rc ] );
158	append( out, LoginFailed, strlen( LoginFailed ));
159	compop();
160	CONSUME( in, linelength + crlflength );
161	return( CH_DONE );
162    }
163    LOG(log_info, logtype_papd, "cq_k4login: %s.%s@%s", ad.pname, ad.pinst,
164	    ad.prealm );
165    lp_person( ad.pname );
166    lp_host( ad.prealm );
167
168    append( out, LoginOK, strlen( LoginOK ));
169    compop();
170    CONSUME( in, linelength + crlflength);
171    return( CH_DONE );
172}
173
174char	*uameth = "UMICHKerberosIV\n*\n";
175
176int cq_uameth( struct papfile *in, struct papfile *out)
177{
178    char		*start;
179    int			linelength, crlflength;
180    struct papd_comment	*c, *comment = compeek();
181
182    for (;;) {
183	switch ( markline( in, &start, &linelength, &crlflength )) {
184	case 0 :
185	    return( 0 );
186
187	case -1 :
188	    return( CH_MORE );
189
190        case -2 :
191            return( CH_ERROR );
192	}
193
194	if ( comgetflags() == 0 ) {	/* start */
195	    if (( printer->p_flags & P_KRB ) == 0 ) {	/* no kerberos */
196		if ( comswitch( queries, cq_default ) < 0 ) {
197		    LOG(log_error, logtype_papd, "cq_uameth: can't find default!" );
198		    exit( 1 );
199		}
200		return( CH_DONE );
201	    }
202	    comsetflags( 1 );
203	} else {
204	    if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) { /* end */
205		append( out, uameth, strlen( uameth ));
206		compop();
207		return( CH_DONE );
208	    }
209	}
210
211	CONSUME( in, linelength + crlflength );
212    }
213}
214#endif /* KRB */
215
216int gq_true( struct papfile *out)
217{
218    if ( printer->p_flags & P_SPOOLED ) {
219	append( out, "true\n", 5 );
220	return( 0 );
221    } else {
222	return( -1 );
223    }
224}
225
226int gq_pagecost( struct papfile *out)
227{
228    char		cost[ 60 ];
229
230    /* check for spooler? XXX */
231    if ( printer->p_pagecost_msg != NULL ) {
232	append( out, printer->p_pagecost_msg,
233		strlen( printer->p_pagecost_msg ));
234    } else if ( printer->p_flags & P_ACCOUNT ) {
235#ifdef ABS_PRINT
236	lp_pagecost();
237#endif /* ABS_PRINT */
238	sprintf( cost, "%d", printer->p_pagecost );
239	append( out, cost, strlen( cost ));
240    } else {
241	return( -1 );
242    }
243    append( out, "\n", 1 );
244    return( 0 );
245}
246
247#ifdef ABS_PRINT
248int gq_balance( struct papfile *out)
249{
250    char		balance[ 60 ];
251
252    if ( lp_pagecost() != 0 ) {
253	return( -1 );
254    }
255    sprintf( balance, "$%1.2f\n", printer->p_balance );
256    append( out, balance, strlen( balance ));
257    return( 0 );
258}
259#endif /* ABS_PRINT */
260
261
262/*
263 * Handler for RBISpoolerID
264 */
265
266static const char *spoolerid = "(PAPD Spooler) 1.0 (" VERSION ")\n";
267
268int gq_rbispoolerid( struct papfile *out)
269{
270    append( out, spoolerid, strlen( spoolerid ));
271    return(0);
272}
273
274
275
276/*
277 * Handler for RBIUAMListQuery
278 */
279
280static const char *nouams = "*\n";
281
282int gq_rbiuamlist( struct papfile *out)
283{
284    char uamnames[128] = "\0";
285
286    if (printer->p_flags & P_AUTH_PSSP) {
287	if (getuamnames(UAM_SERVER_PRINTAUTH, uamnames) < 0) {
288	    append(out, nouams, strlen(nouams));
289	    return(0);
290	} else {
291	    append(out, uamnames, strlen(uamnames));
292	    return(0);
293	}
294    } else {
295	append(out, nouams, strlen(nouams));
296	return(0);
297    }
298}
299
300
301struct genquery {
302    char	*gq_name;
303    int		(*gq_handler)();
304} genqueries[] = {
305    { "UMICHCostPerPage", gq_pagecost },
306#ifdef notdef
307    { "UMICHUserBalance", gq_balance },
308#endif /* notdef */
309    { "RBISpoolerID",	gq_rbispoolerid },
310    { "RBIUAMListQuery", gq_rbiuamlist },
311    { "ADOIsBinaryOK?", gq_true },
312    { "UMICHListQueue", gq_true },
313    { "UMICHDeleteJob", gq_true },
314    { NULL, NULL },
315};
316
317int cq_query( struct papfile *in, struct papfile *out)
318{
319    char		*start, *stop, *p, *q;
320    int			linelength, crlflength;
321    struct papd_comment	*comment = compeek();
322    struct genquery	*gq;
323
324
325    for (;;) {
326	switch ( markline( in, &start, &linelength, &crlflength )) {
327	case 0 :
328	    return( 0 );
329
330	case -1 :
331	    return( CH_MORE );
332
333        case -2 :
334            return( CH_ERROR );
335	}
336
337	stop = start+linelength;
338
339	if ( comgetflags() == 0 ) {	/* started */
340	    comsetflags( 1 );
341
342	    for ( p = start; p < stop; p++ ) {
343		if ( *p == ':' ) {
344		    break;
345		}
346	    }
347
348	    if (p < stop) for ( p++; p < stop; p++ ) {
349		if ( *p != ' ' && *p != '\t' ) {
350		    break;
351		}
352	    }
353
354	    for ( q = p; q < stop; q++ ) {
355		if ( *q == ' ' || *q == '\t' || *q == '\r' || *q == '\n' ) {
356		    break;
357		}
358	    }
359
360	    for ( gq = genqueries; gq->gq_name; gq++ ) {
361		if (( strlen( gq->gq_name ) == (size_t)(q - p) ) &&
362			( strncmp( gq->gq_name, p, q - p ) == 0 )) {
363		    break;
364		}
365	    }
366	    if ( gq->gq_name == NULL || gq->gq_handler == NULL ||
367		    (gq->gq_handler)( out ) < 0 ) {
368		if ( comswitch( queries, cq_default ) < 0 ) {
369		    LOG(log_error, logtype_papd, "cq_feature: can't find default!" );
370		    exit( 1 );
371		}
372		return( CH_DONE );
373	    }
374	} else {
375	    if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
376		compop();
377		CONSUME( in, linelength + crlflength );
378		return( CH_DONE );
379	    }
380	}
381
382	CONSUME( in, linelength + crlflength );
383    }
384}
385
386void cq_font_answer( char *start, char *stop, struct papfile *out)
387{
388    char		*p, *q, buf[ 256 ];
389    struct ppd_font	*pfo;
390
391    p = start;
392    while ( p < stop ) {
393        unsigned int count = 0;
394	while (( *p == ' ' || *p == '\t' ) && p < stop ) {
395	    p++;
396	}
397
398	q = buf;
399	while ( *p != ' ' && *p != '\t' &&
400		*p != '\n' && *p != '\r' && p < stop && count < sizeof(buf)) {
401	    *q++ = *p++;
402	    count++;
403	}
404
405	if ( q != buf ) {
406	    *q = '\0';
407
408	    append( out, "/", 1 );
409	    append( out, buf, strlen( buf ));
410	    append( out, ":", 1 );
411
412	    if (( pfo = ppd_font( buf )) == NULL ) {
413		append( out, "No\n", 3 );
414	    } else {
415		append( out, "Yes\n", 4 );
416	    }
417	}
418    }
419
420    return;
421}
422
423int cq_font(struct papfile *in, struct papfile *out)
424{
425    char		*start, *stop, *p;
426    int			linelength, crlflength;
427    struct papd_comment	*comment = compeek();
428
429    for (;;) {
430	switch ( markline( in, &start, &linelength, &crlflength )) {
431	case 0 :
432	    return( 0 );
433
434	case -1 :
435	    return( CH_MORE );
436
437        case -2 :
438            return( CH_ERROR );
439	}
440
441	stop = start + linelength;
442
443	if ( comgetflags() == 0 ) {
444	    comsetflags( 1 );
445
446	    for ( p = start; p < stop; p++ ) {
447		if ( *p == ':' ) {
448		    break;
449		}
450	    }
451	    if (p < stop)
452	        p++;
453
454	    cq_font_answer( p, stop, out );
455	} else {
456	    if ( comgetflags() == 1 &&
457		    comcmp( start, stop, comcont, 0 ) == 0 ) {
458		/* continuation */
459
460		for ( p = start; p < stop; p++ ) {
461		    if ( *p == ' ' ) {
462			break;
463		    }
464		}
465		if (p < stop)
466		    p++;
467
468		cq_font_answer( p, stop, out );
469	    } else {
470		comsetflags( 2 );
471		if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
472		    append( out, "*\n", 2 );
473		    compop();
474		    CONSUME( in, linelength + crlflength );
475		    return( CH_DONE );
476		}
477	    }
478	}
479
480        CONSUME( in, linelength + crlflength );
481    }
482}
483
484int cq_feature( struct papfile *in, struct papfile *out)
485{
486    char		*start, *stop, *p;
487    int			linelength, crlflength;
488    struct papd_comment	*comment = compeek();
489    struct ppd_feature	*pfe;
490
491    for (;;) {
492	switch ( markline( in, &start, &linelength, &crlflength )) {
493	case 0 :
494	    return( 0 );
495
496	case -1 :
497	    return( CH_MORE );
498
499        case -2 :
500            return( CH_ERROR );
501	}
502
503	stop = start + linelength;
504
505	if ( comgetflags() == 0 ) {
506	    comsetflags( 1 );
507
508	    /* parse for feature */
509	    for ( p = start; p < stop; p++ ) {
510		if ( *p == ':' ) {
511		    break;
512		}
513	    }
514	    if (p < stop)
515	        p++;
516	    while ( *p == ' ' ) {
517		p++;
518	    }
519
520	    if (( pfe = ppd_feature( p, stop - p )) == NULL ) {
521		if ( comswitch( queries, cq_default ) < 0 ) {
522		    LOG(log_error, logtype_papd, "cq_feature: can't find default!" );
523		    exit( 1 );
524		}
525		return( CH_DONE );
526	    }
527
528	    append( out, pfe->pd_value, strlen( pfe->pd_value ));
529	    append( out, "\r", 1 );
530	} else {
531	    if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
532		compop();
533		CONSUME( in, linelength + crlflength );
534		return( CH_DONE );
535	    }
536	}
537
538	CONSUME( in, linelength + crlflength );
539    }
540}
541
542static const char	*psver = "*PSVersion\n";
543static const char	*prod = "*Product\n";
544
545int cq_printer(struct papfile *in, struct papfile *out)
546{
547    char		*start, *p;
548    int			linelength, crlflength;
549    struct papd_comment	*comment = compeek();
550    struct ppd_feature	*pdpsver, *pdprod;
551
552    for (;;) {
553	switch ( markline( in, &start, &linelength, &crlflength )) {
554	case 0 :
555	    return( 0 );
556
557	case -1 :
558	    return( CH_MORE );
559
560        case -2 :
561            return( CH_ERROR );
562	}
563
564	if ( comgetflags() == 0 ) {
565	    comsetflags( 1 );
566
567	    if (( pdpsver = ppd_feature( psver, strlen( psver ))) == NULL ) {
568		if ( comswitch( queries, cq_default ) < 0 ) {
569		    LOG(log_error, logtype_papd, "cq_printer: can't find default!" );
570		    exit( 1 );
571		}
572		return( CH_DONE );
573	    }
574
575	    for ( p = pdpsver->pd_value; *p != '\0'; p++ ) {
576		if ( *p == ' ' ) {
577		    break;
578		}
579	    }
580	    if ( *p == '\0' ) {
581		LOG(log_error, logtype_papd, "cq_printer: can't parse PSVersion!" );
582		if ( comswitch( queries, cq_default ) < 0 ) {
583		    LOG(log_error, logtype_papd, "cq_printer: can't find default!" );
584		    exit( 1 );
585		}
586		return( CH_DONE );
587	    }
588
589	    if (( pdprod = ppd_feature( prod, strlen( prod ))) == NULL ) {
590		if ( comswitch( queries, cq_default ) < 0 ) {
591		    LOG(log_error, logtype_papd, "cq_printer: can't find default!" );
592		    exit( 1 );
593		}
594		return( CH_DONE );
595	    }
596
597	    /* revision */
598	    append( out, p + 1, strlen( p + 1 ));
599	    append( out, "\r", 1 );
600
601	    /* version */
602	    append( out, pdpsver->pd_value, p - pdpsver->pd_value );
603	    append( out, "\r", 1 );
604
605	    /* product */
606	    append( out, pdprod->pd_value, strlen( pdprod->pd_value ));
607	    append( out, "\r", 1 );
608	} else {
609	    if ( comcmp( start, start+linelength, comment->c_end, 0 ) == 0 ) {
610		compop();
611		CONSUME( in, linelength + crlflength );
612		return( CH_DONE );
613	    }
614	}
615
616	CONSUME( in, linelength + crlflength );
617    }
618}
619
620#ifndef HAVE_CUPS
621
622static const char	*rmjobfailed = "Failed\n";
623static const char	*rmjobok = "Ok\n";
624
625int cq_rmjob( struct papfile *in, struct papfile *out)
626{
627    char		*start, *stop, *p;
628    int			linelength, crlflength;
629    int			job;
630
631    switch ( markline( in, &start, &linelength, &crlflength )) {
632    case 0 :
633	return( 0 );
634
635    case -1 :
636	return( CH_MORE );
637
638    case -2 :
639        return( CH_ERROR );
640    }
641
642    stop = start + linelength;
643
644    for ( p = start; p < stop; p++ ) {
645	if ( *p == ' ' || *p == '\t' ) {
646	    break;
647	}
648    }
649    for ( ; p < stop; p++ ) {
650	if ( *p != ' ' && *p != '\t' ) {
651	    break;
652	}
653    }
654
655    *stop = '\0';
656    if ( p < stop && ( job = atoi( p )) > 0 ) {
657	lp_rmjob( job );
658	append( out, rmjobok, strlen( rmjobok ));
659    } else {
660	append( out, rmjobfailed, strlen( rmjobfailed ));
661    }
662
663    compop();
664    CONSUME( in, linelength + crlflength );
665    return( CH_DONE );
666}
667
668int cq_listq( struct papfile *in, struct papfile *out)
669{
670    char		*start;
671    int			linelength, crlflength;
672
673    switch ( markline( in, &start, &linelength, &crlflength )) {
674    case 0 :
675	return( 0 );
676
677    case -1 :
678	return( CH_MORE );
679
680    case -2 :
681        return( CH_ERROR );
682    }
683
684    if ( lp_queue( out )) {
685	LOG(log_error, logtype_papd, "cq_listq: lp_queue failed" );
686    }
687
688    compop();
689    CONSUME( in, linelength + crlflength );
690    return( CH_DONE );
691}
692#endif /* HAVE_CUPS */
693
694
695/*
696 * Handler for RBILogin
697 */
698
699static struct uam_obj *papd_uam = NULL;
700static const char *rbiloginok = "0\r";
701static const char *rbiloginbad = "-1\r";
702static const char *rbiloginerrstr = "%%[Error: SecurityError; \
703SecurityViolation: Unknown user, incorrect password or log on is \
704disabled ]%%\r%%[Flushing: rest of job (to end-of-file) will be \
705ignored ]%%\r";
706
707int cq_rbilogin( struct papfile *in, struct papfile *out)
708{
709    char        	*start, *stop, *p, *begin;
710    int			linelength, crlflength;
711    char        	username[UAM_USERNAMELEN + 1] = "\0";
712    struct papd_comment	*comment = compeek();
713    char		uamtype[20];
714
715    for (;;) {
716        switch ( markline( in, &start, &linelength, &crlflength )) {
717        case 0 :
718            return( 0 );
719
720        case -1 :
721            return( CH_MORE );
722
723        case -2 :
724            return( CH_ERROR );
725        }
726
727	stop = start + linelength;
728
729        if ( comgetflags() == 0 ) { /* first line */
730	    begin = start + strlen(comment->c_begin);
731	    p = begin;
732
733	    while (*p != ' ' && p < stop) {
734		p++;
735	    }
736
737	    memset(uamtype, 0, sizeof(uamtype));
738	    if ((size_t)(p -begin) <= sizeof(uamtype) -1) {
739	        strncpy(uamtype, begin, p - begin);
740            }
741
742	    if ( !*uamtype || (papd_uam = auth_uamfind(UAM_SERVER_PRINTAUTH,
743				uamtype, strlen(uamtype))) == NULL) {
744		LOG(log_info, logtype_papd, "Could not find uam: %s", uamtype);
745		append(out, rbiloginbad, strlen(rbiloginbad));
746		append(out, rbiloginerrstr, strlen(rbiloginerrstr));
747	    } else {
748                if ( (papd_uam->u.uam_printer(p,stop,username,out)) == 0 ) {
749                    lp_person( username );
750                   append(out, rbiloginok, strlen( rbiloginok ));
751                   LOG(log_info, logtype_papd, "RBILogin: Logged in '%s'", username);
752                } else {
753                    append(out, rbiloginbad, strlen( rbiloginbad));
754                    append(out, rbiloginerrstr, strlen(rbiloginerrstr));
755                }
756	    }
757            comsetflags( 1 );
758        } else {
759            if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
760                compop();
761                return( CH_DONE );
762            }
763        }
764
765        CONSUME( in, linelength + crlflength );
766    }
767}
768
769
770/*
771 * All queries start with %%?Begin and end with %%?End.  Note that the
772 * "Begin"/"End" general queries have to be last.
773 */
774struct papd_comment	queries[] = {
775#ifdef KRB
776    { "%%Login: UMICHKerberosIV", NULL,			cq_k4login,	0 },
777    { "%%?BeginUAMethodsQuery",	"%%?EndUAMethodsQuery:", cq_uameth,C_FULL },
778#endif /* KRB */
779#ifndef HAVE_CUPS
780    { "%UMICHListQueue",	NULL,			cq_listq,  C_FULL },
781    { "%UMICHDeleteJob",	NULL,			cq_rmjob,	0 },
782#endif /* HAVE_CUPS */
783    { "%%?BeginQuery: RBILogin ", "%%?EndQuery",	cq_rbilogin,	0 },
784    { "%%?BeginQuery",		"%%?EndQuery",		cq_query,	0 },
785    { "%%?BeginFeatureQuery",	"%%?EndFeatureQuery",	cq_feature,	0 },
786    { "%%?BeginFontQuery",	"%%?EndFontQuery",	cq_font,	0 },
787    { "%%?BeginPrinterQuery",	"%%?EndPrinterQuery",	cq_printer,C_FULL },
788    { "%%?Begin",		"%%?End",		cq_default,	0 },
789    { NULL,			NULL,			NULL,		0 },
790};
791