1 /* $OpenBSD: lp_rmjob.c,v 1.1.1.1 2018/04/27 16:14:36 eric Exp $ */
2
3 /*
4 * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <limits.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "lp.h"
26
27 #include "log.h"
28
29 static int docheck(struct lp_printer *, const char *, struct lp_jobfilter *,
30 const char *, int, int);
31 static void doremove(int, struct lp_printer *, const char *);
32
33 int
lp_rmjob(int ofd,struct lp_printer * lp,const char * agent,struct lp_jobfilter * jf)34 lp_rmjob(int ofd, struct lp_printer *lp, const char *agent,
35 struct lp_jobfilter *jf)
36 {
37 struct lp_queue q;
38 char currjob[PATH_MAX];
39 pid_t currpid;
40 int active, i, killed = 0;
41
42 if ((lp_readqueue(lp, &q)) == -1) {
43 log_warnx("cannot read queue");
44 return 0;
45 }
46
47 if (q.count == 0) {
48 lp_clearqueue(&q);
49 return 0;
50 }
51
52 /*
53 * Find the current task being printed, and kill the printer process
54 * if the file is to be removed.
55 */
56 if (lp_getcurrtask(lp, &currpid, currjob, sizeof(currjob)) == -1)
57 log_warnx("cannot get current task");
58
59 if (currjob[0] && docheck(lp, agent, jf, currjob, 1, 0) == 1) {
60 if (kill(currpid, SIGINT) == -1)
61 log_warn("lpr: cannot kill printer process %d",
62 (int)currpid);
63 else
64 killed = 1;
65 }
66
67 for(i = 0; i < q.count; i++) {
68 active = !strcmp(q.cfname[i], currjob);
69 switch (docheck(lp, agent, jf, q.cfname[i], active, 0)) {
70 case 0:
71 break;
72 case 1:
73 doremove(ofd, lp, q.cfname[i]);
74 break;
75 case 2:
76 if (lp->lp_type == PRN_LPR)
77 dprintf(ofd, "%s: ", lpd_hostname);
78 dprintf(ofd, "%s: Permission denied\n", q.cfname[i]);
79 break;
80 }
81 }
82
83 lp_clearqueue(&q);
84
85 return killed;
86 }
87
88 /*
89 * Check if a file must be removed.
90 *
91 * Return:
92 * 0: no
93 * 1: yes
94 * 2: yes but user has no right to do so
95 */
96 static int
docheck(struct lp_printer * lp,const char * agent,struct lp_jobfilter * jf,const char * cfname,int current,int local)97 docheck(struct lp_printer *lp, const char *agent, struct lp_jobfilter *jf,
98 const char *cfname, int current, int local)
99 {
100 FILE *fp;
101 ssize_t len;
102 size_t linesz = 0;
103 char *line = NULL, *person = NULL;
104 int i, own = 0;
105
106 /* The "-all" agent means remove all jobs from the client host. */
107 if (!strcmp(agent, "-all") && !strcmp(LP_JOBHOST(cfname), jf->hostfrom))
108 return 1;
109
110 /*
111 * Consider the root user owns local files, and files sent from
112 * the same machine.
113 */
114 if (!strcmp(agent, "root"))
115 own = local || !strcmp(LP_JOBHOST(cfname), jf->hostfrom);
116
117 /* Check if the task person matches the agent. */
118 fp = lp_fopen(lp, cfname);
119 if (fp == NULL) {
120 log_warn("cannot open %s", cfname);
121 return 0;
122 }
123 while ((len = getline(&line, &linesz, fp)) != -1) {
124 if (line[len-1] == '\n')
125 line[len - 1] = '\0';
126 if (line[0] == 'P') {
127 person = line + 1;
128 if (!strcmp(person, agent) &&
129 !strcmp(LP_JOBHOST(cfname), jf->hostfrom))
130 own = 1;
131 break;
132 }
133 }
134 fclose(fp);
135
136 if (person == NULL) {
137 free(line);
138 return 0;
139 }
140
141 /* Remove the current task if the request list is empty. */
142 if (current && jf->nuser == 0 && jf->njob == 0)
143 goto remove;
144
145 /* Check for matching jobnum. */
146 for (i = 0; i < jf->njob; i++)
147 if (jf->jobs[i] == LP_JOBNUM(cfname))
148 goto remove;
149
150 /* Check if person is in user list. */
151 for (i = 0; i < jf->nuser; i++)
152 if (!strcmp(jf->users[i], person))
153 goto remove;
154
155 free(line);
156 return 0;
157
158 remove:
159 free(line);
160 return own ? 1 : 2;
161 }
162
163 static void
doremove(int ofd,struct lp_printer * lp,const char * cfname)164 doremove(int ofd, struct lp_printer *lp, const char *cfname)
165 {
166 FILE *fp;
167 ssize_t len;
168 size_t linesz = 0;
169 char *line = NULL;
170
171 fp = lp_fopen(lp, cfname);
172 if (fp == NULL) {
173 log_warn("cannot open %s", cfname);
174 return;
175 }
176
177 if (lp->lp_type == PRN_LPR)
178 dprintf(ofd, "%s: ", lpd_hostname);
179
180 /* First, remove the control file. */
181 if (lp_unlink(lp, cfname) == -1) {
182 log_warn("cannot unlink %s", cfname);
183 dprintf(ofd, "cannot dequeue %s\n", cfname);
184 }
185 else {
186 log_info("removed job %s", cfname);
187 dprintf(ofd, "%s dequeued\n", cfname);
188 }
189
190 /* Then unlink all data files. */
191 while ((len = getline(&line, &linesz, fp)) != -1) {
192 if (line[len-1] == '\n')
193 line[len - 1] = '\0';
194 if (line[0] != 'U')
195 continue;
196 if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
197 continue;
198 if (lp->lp_type == PRN_LPR)
199 dprintf(ofd, "%s: ", lpd_hostname);
200 if (lp_unlink(lp, line + 1) == -1) {
201 log_warn("cannot unlink %s", line + 1);
202 dprintf(ofd, "cannot dequeue %s\n", line + 1);
203 }
204 else
205 dprintf(ofd, "%s dequeued\n", line + 1);
206 }
207
208 fclose(fp);
209 }
210