1 /*
2  * "cancel" command for CUPS.
3  *
4  * Copyright © 2007-2018 by Apple Inc.
5  * Copyright © 1997-2006 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include <cups/cups-private.h>
16 
17 
18 /*
19  * Local functions...
20  */
21 
22 static void	usage(void) _CUPS_NORETURN;
23 
24 
25 /*
26  * 'main()' - Parse options and cancel jobs.
27  */
28 
29 int					/* O - Exit status */
main(int argc,char * argv[])30 main(int  argc,				/* I - Number of command-line arguments */
31      char *argv[])			/* I - Command-line arguments */
32 {
33   http_t	*http;			/* HTTP connection to server */
34   int		i;			/* Looping var */
35   int		job_id;			/* Job ID */
36   int		num_dests;		/* Number of destinations */
37   cups_dest_t	*dests;			/* Destinations */
38   char		*opt,			/* Option pointer */
39 		*dest,			/* Destination printer */
40 		*job,			/* Job ID pointer */
41 		*user;			/* Cancel jobs for a user */
42   int		purge;			/* Purge or cancel jobs? */
43   char		uri[1024];		/* Printer or job URI */
44   ipp_t		*request;		/* IPP request */
45   ipp_t		*response;		/* IPP response */
46   ipp_op_t	op;			/* Operation */
47 
48 
49   _cupsSetLocale(argv);
50 
51  /*
52   * Setup to cancel individual print jobs...
53   */
54 
55   op        = IPP_CANCEL_JOB;
56   purge     = 0;
57   dest      = NULL;
58   user      = NULL;
59   http      = NULL;
60   num_dests = 0;
61   dests     = NULL;
62 
63 
64  /*
65   * Process command-line arguments...
66   */
67 
68   for (i = 1; i < argc; i ++)
69   {
70     if (!strcmp(argv[i], "--help"))
71       usage();
72     else if (argv[i][0] == '-' && argv[i][1])
73     {
74       for (opt = argv[i] + 1; *opt; opt ++)
75       {
76 	switch (*opt)
77 	{
78 	  case 'E' : /* Encrypt */
79 #ifdef HAVE_SSL
80 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
81 
82 	      if (http)
83 		httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
84 #else
85 	      _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
86 #endif /* HAVE_SSL */
87 	      break;
88 
89 	  case 'U' : /* Username */
90 	      if (opt[1] != '\0')
91 	      {
92 		cupsSetUser(opt + 1);
93 		opt += strlen(opt) - 1;
94 	      }
95 	      else
96 	      {
97 		i ++;
98 		if (i >= argc)
99 		{
100 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
101 		  usage();
102 		}
103 
104 		cupsSetUser(argv[i]);
105 	      }
106 	      break;
107 
108 	  case 'a' : /* Cancel all jobs */
109 	      op = purge ? IPP_PURGE_JOBS : IPP_CANCEL_JOBS;
110 	      break;
111 
112 	  case 'h' : /* Connect to host */
113 	      if (http != NULL)
114 	      {
115 		httpClose(http);
116 		http = NULL;
117 	      }
118 
119 	      if (opt[1] != '\0')
120 	      {
121 		cupsSetServer(opt + 1);
122 		opt += strlen(opt) - 1;
123 	      }
124 	      else
125 	      {
126 		i ++;
127 
128 		if (i >= argc)
129 		{
130 		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
131 		  usage();
132 		}
133 		else
134 		  cupsSetServer(argv[i]);
135 	      }
136 	      break;
137 
138 	  case 'u' : /* Username */
139 	      op = IPP_CANCEL_MY_JOBS;
140 
141 	      if (opt[1] != '\0')
142 	      {
143 		user = opt + 1;
144 		opt += strlen(opt) - 1;
145 	      }
146 	      else
147 	      {
148 		i ++;
149 
150 		if (i >= argc)
151 		{
152 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-u\" option."), argv[0]);
153 		  usage();
154 		}
155 		else
156 		  user = argv[i];
157 	      }
158 	      break;
159 
160 	  case 'x' : /* Purge job(s) */
161 	      purge = 1;
162 
163 	      if (op == IPP_CANCEL_JOBS)
164 		op = IPP_PURGE_JOBS;
165 	      break;
166 
167 	  default :
168 	      _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
169 	      return (1);
170 	}
171       }
172     }
173     else
174     {
175      /*
176       * Cancel a job or printer...
177       */
178 
179       if (num_dests == 0)
180         num_dests = cupsGetDests(&dests);
181 
182       if (!strcmp(argv[i], "-"))
183       {
184        /*
185         * Delete the current job...
186 	*/
187 
188         dest   = "";
189 	job_id = 0;
190       }
191       else if (cupsGetDest(argv[i], NULL, num_dests, dests) != NULL)
192       {
193        /*
194         * Delete the current job on the named destination...
195 	*/
196 
197         dest   = argv[i];
198 	job_id = 0;
199       }
200       else if ((job = strrchr(argv[i], '-')) != NULL && isdigit(job[1] & 255))
201       {
202        /*
203         * Delete the specified job ID.
204 	*/
205 
206         dest   = NULL;
207 	op     = IPP_CANCEL_JOB;
208         job_id = atoi(job + 1);
209       }
210       else if (isdigit(argv[i][0] & 255))
211       {
212        /*
213         * Delete the specified job ID.
214 	*/
215 
216         dest   = NULL;
217 	op     = IPP_CANCEL_JOB;
218         job_id = atoi(argv[i]);
219       }
220       else
221       {
222        /*
223         * Bad printer name!
224 	*/
225 
226         _cupsLangPrintf(stderr,
227 	                _("%s: Error - unknown destination \"%s\"."),
228 			argv[0], argv[i]);
229 	return (1);
230       }
231 
232      /*
233       * For Solaris LP compatibility, ignore a destination name after
234       * cancelling a specific job ID...
235       */
236 
237       if (job_id && (i + 1) < argc &&
238           cupsGetDest(argv[i + 1], NULL, num_dests, dests) != NULL)
239         i ++;
240 
241      /*
242       * Open a connection to the server...
243       */
244 
245       if (http == NULL)
246 	if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
247 	                               cupsEncryption())) == NULL)
248 	{
249 	  _cupsLangPrintf(stderr,
250 	                  _("%s: Unable to connect to server."), argv[0]);
251 	  return (1);
252 	}
253 
254      /*
255       * Build an IPP request, which requires the following
256       * attributes:
257       *
258       *    attributes-charset
259       *    attributes-natural-language
260       *    printer-uri + job-id *or* job-uri
261       *    [requesting-user-name]
262       */
263 
264       request = ippNewRequest(op);
265 
266       if (dest)
267       {
268 	httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
269 	                 "localhost", 0, "/printers/%s", dest);
270 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
271 	             "printer-uri", NULL, uri);
272 	ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
273 	              job_id);
274       }
275       else
276       {
277         sprintf(uri, "ipp://localhost/jobs/%d", job_id);
278 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
279 	             uri);
280       }
281 
282       if (user)
283       {
284 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
285                      "requesting-user-name", NULL, user);
286 	ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
287 
288         if (op == IPP_CANCEL_JOBS)
289           op = IPP_CANCEL_MY_JOBS;
290       }
291       else
292 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
293                      "requesting-user-name", NULL, cupsUser());
294 
295       if (purge)
296 	ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", (char)purge);
297 
298      /*
299       * Do the request and get back a response...
300       */
301 
302       if (op == IPP_CANCEL_JOBS && (!user || _cups_strcasecmp(user, cupsUser())))
303         response = cupsDoRequest(http, request, "/admin/");
304       else
305         response = cupsDoRequest(http, request, "/jobs/");
306 
307       if (response == NULL ||
308           response->request.status.status_code > IPP_OK_CONFLICT)
309       {
310 	_cupsLangPrintf(stderr, _("%s: %s failed: %s"), argv[0],
311 	        	op == IPP_PURGE_JOBS ? "purge-jobs" : "cancel-job",
312         		cupsLastErrorString());
313 
314 	if (response)
315 	  ippDelete(response);
316 
317 	return (1);
318       }
319 
320       ippDelete(response);
321     }
322   }
323 
324   if (num_dests == 0 && op != IPP_CANCEL_JOB)
325   {
326    /*
327     * Open a connection to the server...
328     */
329 
330     if (http == NULL)
331       if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
332 	                             cupsEncryption())) == NULL)
333       {
334 	_cupsLangPrintf(stderr, _("%s: Unable to contact server."), argv[0]);
335 	return (1);
336       }
337 
338    /*
339     * Build an IPP request, which requires the following
340     * attributes:
341     *
342     *    attributes-charset
343     *    attributes-natural-language
344     *    printer-uri + job-id *or* job-uri
345     *    [requesting-user-name]
346     */
347 
348     request = ippNewRequest(op);
349 
350     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
351 	         "printer-uri", NULL, "ipp://localhost/printers/");
352 
353     if (user)
354     {
355       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
356                    "requesting-user-name", NULL, user);
357       ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
358     }
359     else
360       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
361                    "requesting-user-name", NULL, cupsUser());
362 
363     ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", (char)purge);
364 
365    /*
366     * Do the request and get back a response...
367     */
368 
369     response = cupsDoRequest(http, request, "/admin/");
370 
371     if (response == NULL ||
372         response->request.status.status_code > IPP_OK_CONFLICT)
373     {
374       _cupsLangPrintf(stderr, _("%s: %s failed: %s"), argv[0],
375 		      op == IPP_PURGE_JOBS ? "purge-jobs" : "cancel-job",
376         	      cupsLastErrorString());
377 
378       if (response)
379 	ippDelete(response);
380 
381       return (1);
382     }
383 
384     ippDelete(response);
385   }
386 
387   return (0);
388 }
389 
390 
391 /*
392  * 'usage()' - Show program usage and exit.
393  */
394 
395 static void
usage(void)396 usage(void)
397 {
398   _cupsLangPuts(stdout, _("Usage: cancel [options] [id]\n"
399                           "       cancel [options] [destination]\n"
400                           "       cancel [options] [destination-id]"));
401   _cupsLangPuts(stdout, _("Options:"));
402   _cupsLangPuts(stdout, _("-a                      Cancel all jobs"));
403   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
404   _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
405   _cupsLangPuts(stdout, _("-u owner                Specify the owner to use for jobs"));
406   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
407   _cupsLangPuts(stdout, _("-x                      Purge jobs rather than just canceling"));
408 
409   exit(1);
410 }
411