1 /*
2 * cups - Python bindings for CUPS
3 * Copyright (C) 2002-2020 Red Hat, Inc
4 * Authors: Tim Waugh <twaugh@redhat.com>
5 * Zdenek Dohnal <zdohnal@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include "cupsconnection.h"
23 #include "cupsppd.h"
24 #include "cupsmodule.h"
25
26 #ifndef __SVR4
27 #include <paths.h>
28 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #ifndef _PATH_TMP
35 #define _PATH_TMP P_tmpdir
36 #endif
37
38 #define DICT_POS_TYPE Py_ssize_t
39
40 PyObject *HTTPError;
41 PyObject *IPPError;
42
43 static Connection **Connections = NULL;
44 static long unsigned int NumConnections = 0;
45
46 static void
set_http_error(http_status_t status)47 set_http_error (http_status_t status)
48 {
49 PyObject *v = Py_BuildValue ("i", status);
50 debugprintf("set_http_error: %d\n", (int) status);
51 if (v != NULL) {
52 PyErr_SetObject (HTTPError, v);
53 Py_DECREF (v);
54 }
55 }
56
57 void
set_ipp_error(ipp_status_t status,const char * message)58 set_ipp_error (ipp_status_t status, const char *message)
59 {
60 if (!message)
61 message = ippErrorString (status);
62
63 debugprintf("set_ipp_error: %d, %s\n", (int) status, message);
64 PyObject *v = Py_BuildValue ("(is)", status, message);
65 if (v != NULL) {
66 PyErr_SetObject (IPPError, v);
67 Py_DECREF (v);
68 }
69 }
70
71 static PyObject *
PyObj_from_UTF8(const char * utf8)72 PyObj_from_UTF8 (const char *utf8)
73 {
74 PyObject *val = PyUnicode_Decode (utf8, strlen (utf8), "utf-8", NULL);
75 if (!val) {
76 // CUPS 1.2 always gives us UTF-8. Before CUPS 1.2, the
77 // ppd-* strings come straight from the PPD with no
78 // transcoding, but the attributes-charset is still 'utf-8'
79 // so we've no way of knowing the real encoding.
80 // In that case, detect the error and force it to ASCII.
81 const char *orig = utf8;
82 char *ascii;
83 int i;
84 PyErr_Clear ();
85 ascii = malloc (1 + strlen (orig));
86 for (i = 0; orig[i]; i++)
87 ascii[i] = orig[i] & 0x7f;
88 ascii[i] = '\0';
89 val = PyUnicode_FromString (ascii);
90 free (ascii);
91 }
92
93 return val;
94 }
95
96 const char *
UTF8_from_PyObj(char ** const utf8,PyObject * obj)97 UTF8_from_PyObj (char **const utf8, PyObject *obj)
98 // converts PyUnicode or PyBytes to char *
99 {
100 if (PyUnicode_Check (obj)) {
101 const char *string;
102 PyObject *stringobj = PyUnicode_AsUTF8String (obj);
103 if (stringobj == NULL)
104 return NULL;
105
106 string = PyBytes_AsString (stringobj);
107 if (string == NULL) {
108 Py_DECREF (stringobj);
109 return NULL;
110 }
111
112 *utf8 = strdup (string);
113 Py_DECREF (stringobj);
114 return *utf8;
115 }
116 else if (PyBytes_Check (obj)) {
117 const char *ret;
118 PyObject *unicodeobj = PyUnicode_FromEncodedObject (obj, "utf-8", NULL);
119 if (unicodeobj == NULL)
120 return NULL;
121
122 ret = UTF8_from_PyObj (utf8, unicodeobj);
123 Py_DECREF (unicodeobj);
124 return ret;
125 }
126
127 PyErr_SetString (PyExc_TypeError, "unicode or bytes object required");
128 return NULL;
129 }
130
131 static void
construct_uri(char * buffer,size_t buflen,const char * base,const char * value)132 construct_uri (char *buffer, size_t buflen, const char *base, const char *value)
133 {
134 char *d = buffer;
135 const unsigned char *s = (const unsigned char *) value;
136 if (strlen (base) < buflen) {
137 strcpy (buffer, base);
138 d += strlen (base);
139 } else {
140 strncpy (buffer, base, buflen);
141 d += buflen;
142 }
143
144 while (*s && d < buffer + buflen) {
145 if (isalpha (*s) || isdigit (*s) || *s == '-')
146 *d++ = *s++;
147 else if (*s == ' ') {
148 *d++ = '+';
149 s++;
150 } else {
151 if (d + 2 < buffer + buflen) {
152 *d++ = '%';
153 *d++ = "0123456789ABCDEF"[((*s) & 0xf0) >> 4];
154 *d++ = "0123456789ABCDEF"[((*s) & 0x0f)];
155 s++;
156 } else
157 break;
158 }
159 }
160
161 if (d < buffer + buflen)
162 *d = '\0';
163 }
164
165 ////////////////
166 // Connection //
167 ////////////////
168
169 static PyObject *
Connection_new(PyTypeObject * type,PyObject * args,PyObject * kwds)170 Connection_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
171 {
172 Connection *self;
173 self = (Connection *) type->tp_alloc (type, 0);
174 if (self != NULL) {
175 self->http = NULL;
176 self->host = NULL;
177 self->tstate = NULL;
178 self->cb_password = NULL;
179 }
180
181 return (PyObject *) self;
182 }
183
184 static int
Connection_init(Connection * self,PyObject * args,PyObject * kwds)185 Connection_init (Connection *self, PyObject *args, PyObject *kwds)
186 {
187 const char *host = cupsServer ();
188 int port = ippPort ();
189 int encryption = (http_encryption_t) cupsEncryption ();
190 static char *kwlist[] = { "host", "port", "encryption", NULL };
191
192 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|sii", kwlist,
193 &host, &port, &encryption))
194 return -1;
195
196 debugprintf ("-> Connection_init(host=%s)\n", host);
197 self->host = strdup (host);
198 if (!self->host) {
199 debugprintf ("<- Connection_init() = -1\n");
200 return -1;
201 }
202
203 Connection_begin_allow_threads (self);
204 debugprintf ("httpConnect2(...)\n");
205 self->http = httpConnect2(host, port, NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
206 Connection_end_allow_threads (self);
207
208 if (!self->http) {
209 PyErr_SetString (PyExc_RuntimeError, "failed to connect to server");
210 debugprintf ("<- Connection_init() = -1\n");
211 return -1;
212 }
213
214 if (NumConnections == 0)
215 {
216 Connections = malloc (sizeof (Connection *));
217 if (Connections == NULL) {
218 PyErr_SetString (PyExc_RuntimeError, "insufficient memory");
219 debugprintf ("<- Connection_init() = -1\n");
220 return -1;
221 }
222 }
223 else
224 {
225 Connection **old_array = Connections;
226
227 if ((1 + NumConnections) >= UINT_MAX / sizeof (Connection *))
228 {
229 PyErr_SetString (PyExc_RuntimeError, "too many connections");
230 debugprintf ("<- Connection_init() == -1\n");
231 return -1;
232 }
233
234 Connections = realloc (Connections,
235 (1 + NumConnections) * sizeof (Connection *));
236 if (Connections == NULL) {
237 Connections = old_array;
238 PyErr_SetString (PyExc_RuntimeError, "insufficient memory");
239 debugprintf ("<- Connection_init() = -1\n");
240 return -1;
241 }
242 }
243
244 Connections[NumConnections++] = self;
245
246 debugprintf ("<- Connection_init() = 0\n");
247 return 0;
248 }
249
250 static void
Connection_dealloc(Connection * self)251 Connection_dealloc (Connection *self)
252 {
253 long unsigned int i, j;
254
255 for (j = 0; j < NumConnections; j++)
256 if (Connections[j] == self)
257 break;
258
259 if (j < NumConnections)
260 {
261 if (NumConnections > 1)
262 {
263 Connection **new_array = calloc (NumConnections - 1,
264 sizeof (Connection *));
265
266 if (new_array)
267 {
268 int k;
269 for (i = 0, k = 0; i < NumConnections; i++)
270 {
271 if (i == j)
272 continue;
273
274 new_array[k++] = Connections[i];
275 }
276
277 free (Connections);
278 Connections = new_array;
279 NumConnections--;
280 } else
281 /* Failed to allocate memory. Just clear out the reference. */
282 Connections[j] = NULL;
283 }
284 else
285 {
286 /* The only element is the one we no longer need. */
287 free (Connections);
288 Connections = NULL;
289 NumConnections = 0;
290 }
291 }
292
293 if (self->http) {
294 debugprintf ("httpClose()\n");
295 httpClose (self->http);
296 free (self->host);
297 free (self->cb_password);
298 }
299
300 ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
301 }
302
303 static PyObject *
Connection_repr(Connection * self)304 Connection_repr (Connection *self)
305 {
306 char buffer[256];
307 snprintf (buffer, 256, "<cups.Connection object for %s at %p>",
308 self->host, self);
309 return PyUnicode_FromString (buffer);
310 }
311
312 void
Connection_begin_allow_threads(void * connection)313 Connection_begin_allow_threads (void *connection)
314 {
315 Connection *self = (Connection *) connection;
316 debugprintf ("begin allow threads\n");
317
318 self->tstate = PyEval_SaveThread ();
319 }
320
321 void
Connection_end_allow_threads(void * connection)322 Connection_end_allow_threads (void *connection)
323 {
324 Connection *self = (Connection *) connection;
325 debugprintf ("end allow threads\n");
326 PyEval_RestoreThread (self->tstate);
327 self->tstate = NULL;
328 }
329
330 ////////////////
331 // Connection // METHODS
332 ////////////////
333
334 static const char *
password_callback(int newstyle,const char * prompt,http_t * http,const char * method,const char * resource,void * user_data)335 password_callback (int newstyle,
336 const char *prompt,
337 http_t *http,
338 const char *method,
339 const char *resource,
340 void *user_data)
341 {
342 struct TLS *tls = get_TLS ();
343 PyObject *cb_context = user_data;
344 Connection *self = NULL;
345 PyObject *args;
346 PyObject *result;
347 long unsigned int i;
348
349 debugprintf ("-> password_callback for http=%p, newstyle=%d\n",
350 http, newstyle);
351
352 for (i = 0; i < NumConnections; i++)
353 if (Connections[i]->http == http)
354 {
355 self = Connections[i];
356 break;
357 }
358
359 if (!self)
360 {
361 debugprintf ("cannot find self!\n");
362 return "";
363 }
364
365 Connection_end_allow_threads (self);
366 if (newstyle)
367 {
368 /* New-style callback. */
369 if (cb_context)
370 args = Py_BuildValue ("(sOssO)", prompt, self, method, resource,
371 cb_context);
372 else
373 args = Py_BuildValue ("(sOss)", prompt, self, method, resource);
374 } else
375 args = Py_BuildValue ("(s)", prompt);
376
377 result = PyEval_CallObject (tls->cups_password_callback, args);
378 Py_DECREF (args);
379 if (result == NULL)
380 {
381 debugprintf ("<- password_callback (exception)\n");
382 Connection_begin_allow_threads (self);
383 return NULL;
384 }
385
386 free (self->cb_password);
387 if (result == Py_None ||
388 !UTF8_from_PyObj (&self->cb_password, result))
389 self->cb_password = NULL;
390
391 Py_DECREF (result);
392 if (!self->cb_password || !*self->cb_password)
393 {
394 debugprintf ("<- password_callback (empty/null)\n");
395 Connection_begin_allow_threads (self);
396 return NULL;
397 }
398
399 Connection_begin_allow_threads (self);
400 debugprintf ("<- password_callback\n");
401 return self->cb_password;
402 }
403
404 const char *
password_callback_oldstyle(const char * prompt,http_t * http,const char * method,const char * resource,void * user_data)405 password_callback_oldstyle (const char *prompt,
406 http_t *http,
407 const char *method,
408 const char *resource,
409 void *user_data)
410 {
411 return password_callback (0, prompt, http, method, resource, user_data);
412 }
413
414 const char *
password_callback_newstyle(const char * prompt,http_t * http,const char * method,const char * resource,void * user_data)415 password_callback_newstyle (const char *prompt,
416 http_t *http,
417 const char *method,
418 const char *resource,
419 void *user_data)
420 {
421 return password_callback (1, prompt, http, method, resource, user_data);
422 }
423
424 static PyObject *
do_printer_request(Connection * self,PyObject * args,PyObject * kwds,ipp_op_t op)425 do_printer_request (Connection *self, PyObject *args, PyObject *kwds,
426 ipp_op_t op)
427 {
428 PyObject *nameobj;
429 PyObject *reasonobj = NULL;
430 char *name;
431 char uri[HTTP_MAX_URI];
432 ipp_t *request, *answer;
433
434 switch (op) {
435 case IPP_PAUSE_PRINTER:
436 case CUPS_REJECT_JOBS:
437 {
438 static char *kwlist[] = { "name", "reason", NULL };
439 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist,
440 &nameobj, &reasonobj))
441 return NULL;
442 break;
443 }
444
445 default:
446 if (!PyArg_ParseTuple (args, "O", &nameobj))
447 return NULL;
448 break;
449 }
450
451 if (UTF8_from_PyObj (&name, nameobj) == NULL)
452 return NULL;
453
454 debugprintf ("-> do_printer_request(op:%d, name:%s)\n", (int) op, name);
455
456 request = ippNewRequest (op);
457 construct_uri (uri, sizeof (uri), "ipp://localhost/printers/", name);
458 free (name);
459
460 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
461 "printer-uri", NULL, uri);
462
463 if (reasonobj) {
464 char *reason;
465 if (UTF8_from_PyObj (&reason, reasonobj) == NULL) {
466 ippDelete (request);
467 return NULL;
468 }
469
470 debugprintf ("reason: %s\n", reason);
471 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
472 "printer-state-message", NULL, reason);
473 free (reason);
474 }
475
476 debugprintf ("cupsDoRequest(\"/admin/\")\n");
477 Connection_begin_allow_threads (self);
478 answer = cupsDoRequest (self->http, request, "/admin/");
479 Connection_end_allow_threads (self);
480 if (PyErr_Occurred ()) {
481 if (answer)
482 ippDelete (answer);
483 debugprintf("<- do_printer_request (error)\n");
484 return NULL;
485 }
486
487 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
488 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
489 answer ? NULL : cupsLastErrorString ());
490 if (answer)
491 ippDelete (answer);
492 debugprintf("<- do_printer_request (error)\n");
493 return NULL;
494 }
495
496 ippDelete (answer);
497 debugprintf("<- do_printer_request (None)\n");
498 Py_RETURN_NONE;
499 }
500
501 static int
copy_dest(Dest * dst,cups_dest_t * src)502 copy_dest (Dest *dst, cups_dest_t *src)
503 {
504 int i;
505 dst->is_default = src->is_default;
506 dst->destname = strdup (src->name);
507 dst->instance = (src->instance ? strdup (src->instance) : NULL );
508 dst->num_options = src->num_options;
509 dst->name = malloc (src->num_options * sizeof (char *));
510 dst->value = malloc (src->num_options * sizeof (char *));
511 for (i = 0; i < src->num_options; i++) {
512 dst->name[i] = strdup (src->options[i].name);
513 dst->value[i] = strdup (src->options[i].value);
514 }
515 return 0;
516 }
517
518 static PyObject *
Connection_getDests(Connection * self)519 Connection_getDests (Connection *self)
520 {
521 cups_dest_t *dests;
522 int num_dests;
523 PyObject *pydests = PyDict_New ();
524 int i;
525
526 debugprintf ("-> Connection_getDests()\n");
527 debugprintf ("cupsGetDests2()\n");
528 Connection_begin_allow_threads (self);
529 num_dests = cupsGetDests2 (self->http, &dests);
530 Connection_end_allow_threads (self);
531
532 // Create a dict indexed by (name,instance)
533 for (i = 0; i <= num_dests; i++) {
534 PyObject *largs = Py_BuildValue ("()");
535 PyObject *lkwlist = Py_BuildValue ("{}");
536 Dest *destobj = (Dest *) PyType_GenericNew (&cups_DestType,
537 largs, lkwlist);
538 Py_DECREF (largs);
539 Py_DECREF (lkwlist);
540
541 cups_dest_t *dest;
542 PyObject *nameinstance;
543 if (i == num_dests)
544 {
545 // Add a (None,None) entry for the default printer.
546 dest = cupsGetDest (NULL, NULL, num_dests, dests);
547 if (dest == NULL) {
548 /* No default printer. */
549 Py_DECREF ((PyObject *) destobj);
550 break;
551 }
552
553 nameinstance = Py_BuildValue ("(ss)", NULL, NULL);
554 }
555 else
556 {
557 dest = dests + i;
558 nameinstance = Py_BuildValue ("(ss)", dest->name, dest->instance);
559 }
560
561 copy_dest (destobj, dest);
562
563 PyDict_SetItem (pydests, nameinstance, (PyObject *) destobj);
564 Py_DECREF ((PyObject *) destobj);
565 }
566
567 debugprintf ("cupsFreeDests()\n");
568 cupsFreeDests (num_dests, dests);
569 debugprintf ("<- Connection_getDests()\n");
570 return pydests;
571 }
572
573 int
cups_dest_cb(void * user_data,unsigned flags,cups_dest_t * dest)574 cups_dest_cb (void *user_data, unsigned flags, cups_dest_t *dest)
575 {
576 CallbackContext *context = user_data;
577 PyObject *largs = Py_BuildValue ("()");
578 PyObject *lkwlist = Py_BuildValue ("{}");
579 Dest *destobj;
580 PyObject *args;
581 PyObject *result;
582 int ret = 0;
583
584 debugprintf ("-> cups_dest_cb\n");
585 destobj = (Dest *) PyType_GenericNew (&cups_DestType,
586 largs, lkwlist);
587 Py_DECREF (largs);
588 Py_DECREF (lkwlist);
589 copy_dest (destobj, dest);
590 args = Py_BuildValue ("(OiO)",
591 context->user_data,
592 flags,
593 destobj);
594 Py_DECREF ((PyObject *) destobj);
595
596 result = PyEval_CallObject (context->cb, args);
597 Py_DECREF (args);
598 if (result == NULL) {
599 debugprintf ("<- cups_dest_cb (exception from cb func)\n");
600 ret = 0;
601 }
602
603
604 if (result && PyLong_Check (result)) {
605 ret = PyLong_AsLong (result);
606 debugprintf (" cups_dest_cb: cb func returned %d\n", ret);
607 }
608
609 debugprintf ("<- cups_dest_cb (%d)\n", ret);
610
611 return ret;
612 }
613
614 static PyObject *
PyObject_from_attr_value(ipp_attribute_t * attr,int i)615 PyObject_from_attr_value (ipp_attribute_t *attr, int i)
616 {
617 PyObject *val = NULL;
618 char unknown[100];
619 int lower, upper;
620 int xres, yres;
621 ipp_res_t units;
622
623 switch (ippGetValueTag(attr)) {
624 case IPP_TAG_NAME:
625 case IPP_TAG_TEXT:
626 case IPP_TAG_KEYWORD:
627 case IPP_TAG_URI:
628 case IPP_TAG_CHARSET:
629 case IPP_TAG_MIMETYPE:
630 case IPP_TAG_LANGUAGE:
631 val = PyObj_from_UTF8 (ippGetString (attr, i, NULL));
632 break;
633 case IPP_TAG_INTEGER:
634 case IPP_TAG_ENUM:
635 val = PyLong_FromLong (ippGetInteger (attr, i));
636 break;
637 case IPP_TAG_BOOLEAN:
638 val = PyBool_FromLong (ippGetBoolean (attr, i));
639 break;
640 case IPP_TAG_RANGE:
641 lower = ippGetRange (attr, i, &upper);
642 val = Py_BuildValue ("(ii)",
643 lower,
644 upper);
645 break;
646 case IPP_TAG_NOVALUE:
647 Py_RETURN_NONE;
648 break;
649
650 // TODO:
651 case IPP_TAG_DATE:
652 val = PyUnicode_FromString ("(IPP_TAG_DATE)");
653 break;
654 case IPP_TAG_RESOLUTION:
655 xres = ippGetResolution(attr, i, &yres, &units);
656 val = Py_BuildValue ("(iii)",
657 xres,
658 yres,
659 units);
660 break;
661 default:
662 snprintf (unknown, sizeof (unknown),
663 "(unknown IPP value tag 0x%x)", ippGetValueTag(attr));
664 val = PyUnicode_FromString (unknown);
665 break;
666 }
667
668 return val;
669 }
670
671 static PyObject *
PyList_from_attr_values(ipp_attribute_t * attr)672 PyList_from_attr_values (ipp_attribute_t *attr)
673 {
674 PyObject *list = PyList_New (0);
675 int i;
676 debugprintf ("-> PyList_from_attr_values()\n");
677 for (i = 0; i < ippGetCount (attr); i++) {
678 PyObject *val = PyObject_from_attr_value (attr, i);
679 if (val) {
680 PyList_Append (list, val);
681 Py_DECREF (val);
682 }
683 }
684
685 debugprintf ("<- PyList_from_attr_values()\n");
686 return list;
687 }
688
689 static PyObject *
Connection_getPrinters(Connection * self)690 Connection_getPrinters (Connection *self)
691 {
692 PyObject *result;
693 ipp_t *request = ippNewRequest(CUPS_GET_PRINTERS), *answer;
694 ipp_attribute_t *attr;
695 const char *attributes[] = {
696 "printer-name",
697 "printer-type",
698 "printer-location",
699 "printer-info",
700 "printer-make-and-model",
701 "printer-state",
702 "printer-state-message",
703 "printer-state-reasons",
704 "printer-uri-supported",
705 "device-uri",
706 "printer-is-shared",
707 };
708
709 debugprintf ("-> Connection_getPrinters()\n");
710
711 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
712 "requested-attributes",
713 sizeof (attributes) / sizeof (attributes[0]),
714 NULL, attributes);
715 debugprintf ("cupsDoRequest(\"/\")\n");
716 Connection_begin_allow_threads (self);
717 answer = cupsDoRequest (self->http, request, "/");
718 Connection_end_allow_threads (self);
719 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
720 if (answer && ippGetStatusCode (answer) == IPP_NOT_FOUND) {
721 // No printers.
722 debugprintf ("<- Connection_getPrinters() = {} (no printers)\n");
723 ippDelete (answer);
724 return PyDict_New ();
725 }
726
727 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
728 answer ? NULL : cupsLastErrorString ());
729 if (answer)
730 ippDelete (answer);
731 debugprintf ("<- Connection_getPrinters() (error)\n");
732 return NULL;
733 }
734
735 result = PyDict_New ();
736 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
737 PyObject *dict;
738 char *printer = NULL;
739
740 while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
741 attr = ippNextAttribute (answer);
742
743 if (!attr)
744 break;
745
746 dict = PyDict_New ();
747 for (; attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER;
748 attr = ippNextAttribute (answer)) {
749 PyObject *val = NULL;
750
751 debugprintf ("Attribute: %s\n", ippGetName (attr));
752 if (!strcmp (ippGetName (attr), "printer-name") &&
753 ippGetValueTag (attr) == IPP_TAG_NAME)
754 printer = (char *) ippGetString (attr, 0, NULL);
755 else if ((!strcmp (ippGetName (attr), "printer-type") ||
756 !strcmp (ippGetName (attr), "printer-state")) &&
757 ippGetValueTag (attr) == IPP_TAG_ENUM) {
758 int ptype = ippGetInteger (attr, 0);
759 val = PyLong_FromLong (ptype);
760 }
761 else if ((!strcmp (ippGetName (attr),
762 "printer-make-and-model") ||
763 !strcmp (ippGetName (attr), "printer-info") ||
764 !strcmp (ippGetName (attr), "printer-location") ||
765 !strcmp (ippGetName (attr), "printer-state-message")) &&
766 ippGetValueTag (attr) == IPP_TAG_TEXT) {
767 val = PyObj_from_UTF8 (ippGetString (attr, 0, NULL));
768 }
769 else if (!strcmp (ippGetName (attr),
770 "printer-state-reasons") &&
771 ippGetValueTag (attr) == IPP_TAG_KEYWORD) {
772 val = PyList_from_attr_values (attr);
773 }
774 else if (!strcmp (ippGetName (attr),
775 "printer-is-accepting-jobs") &&
776 ippGetValueTag (attr) == IPP_TAG_BOOLEAN) {
777 int b = ippGetBoolean (attr, 0);
778 val = PyLong_FromLong (b);
779 }
780 else if ((!strcmp (ippGetName (attr),
781 "printer-up-time") ||
782 !strcmp (ippGetName (attr),
783 "queued-job-count")) &&
784 ippGetValueTag (attr) == IPP_TAG_INTEGER) {
785 int u = ippGetInteger (attr, 0);
786 val = PyLong_FromLong (u);
787 }
788 else if ((!strcmp (ippGetName (attr), "device-uri") ||
789 !strcmp (ippGetName (attr), "printer-uri-supported")) &&
790 ippGetValueTag (attr) == IPP_TAG_URI) {
791 val = PyObj_from_UTF8 (ippGetString (attr, 0, NULL));
792 }
793 else if (!strcmp (ippGetName (attr), "printer-is-shared") &&
794 ippGetValueTag (attr) == IPP_TAG_BOOLEAN) {
795 val = PyBool_FromLong (ippGetBoolean (attr, 0));
796 }
797
798 if (val) {
799 debugprintf ("Added %s to dict\n", ippGetName (attr));
800 PyDict_SetItemString (dict, ippGetName (attr), val);
801 Py_DECREF (val);
802 }
803 }
804
805 if (printer) {
806 PyObject *key = PyObj_from_UTF8 (printer);
807 PyDict_SetItem (result, key, dict);
808 Py_DECREF (key);
809 }
810
811 Py_DECREF (dict);
812 if (!attr)
813 break;
814 }
815
816 ippDelete (answer);
817 debugprintf ("<- Connection_getPrinters() = dict\n");
818 return result;
819 }
820
821 static PyObject *
Connection_getClasses(Connection * self)822 Connection_getClasses (Connection *self)
823 {
824 PyObject *result;
825 ipp_t *request = ippNewRequest(CUPS_GET_CLASSES), *answer;
826 ipp_attribute_t *attr;
827 const char *attributes[] = {
828 "printer-name",
829 "member-names",
830 };
831
832 debugprintf ("-> Connection_getClasses()\n");
833 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
834 "requested-attributes",
835 sizeof (attributes) / sizeof (attributes[0]),
836 NULL, attributes);
837 debugprintf ("cupsDoRequest(\"/\")\n");
838 Connection_begin_allow_threads (self);
839 answer = cupsDoRequest (self->http, request, "/");
840 Connection_end_allow_threads (self);
841 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
842 if (answer && ippGetStatusCode (answer) == IPP_NOT_FOUND) {
843 // No classes.
844 debugprintf ("<- Connection_getClasses() = {} (no classes)\n");
845 ippDelete (answer);
846 return PyDict_New ();
847 }
848
849 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
850 answer ? NULL : cupsLastErrorString ());
851 if (answer)
852 ippDelete (answer);
853 debugprintf ("<- Connection_getClasses() (error)\n");
854 return NULL;
855 }
856
857 result = PyDict_New ();
858 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
859 PyObject *members = NULL;
860 char *classname = NULL;
861 char *printer_uri = NULL;
862
863 while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
864 attr = ippNextAttribute (answer);
865
866 if (!attr)
867 break;
868
869 for (; attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER;
870 attr = ippNextAttribute (answer)) {
871 debugprintf ("Attribute: %s\n", ippGetName (attr));
872 if (!strcmp (ippGetName (attr), "printer-name") &&
873 ippGetValueTag (attr) == IPP_TAG_NAME)
874 classname = (char *) ippGetString (attr, 0, NULL);
875 else if (!strcmp (ippGetName (attr), "printer-uri-supported") &&
876 ippGetValueTag (attr) == IPP_TAG_URI)
877 printer_uri = (char *) ippGetString (attr, 0, NULL);
878 else if (!strcmp (ippGetName (attr), "member-names") &&
879 ippGetValueTag (attr) == IPP_TAG_NAME) {
880 Py_XDECREF (members);
881 members = PyList_from_attr_values (attr);
882 }
883 }
884
885 if (printer_uri) {
886 Py_XDECREF (members);
887 members = PyObj_from_UTF8 (printer_uri);
888 }
889
890 if (!members)
891 members = PyList_New (0);
892
893 if (classname) {
894 PyObject *key = PyObj_from_UTF8 (classname);
895 debugprintf ("Added class %s\n", classname);
896 PyDict_SetItem (result, key, members);
897 Py_DECREF (key);
898 }
899
900 Py_DECREF (members);
901 if (!attr)
902 break;
903 }
904
905 ippDelete (answer);
906 debugprintf ("<- Connection_getClasses() = dict\n");
907 return result;
908 }
909
910 static PyObject *
do_getPPDs(Connection * self,PyObject * args,PyObject * kwds,int all_lists)911 do_getPPDs (Connection *self, PyObject *args, PyObject *kwds, int all_lists)
912 {
913 PyObject *result = NULL;
914 ipp_t *request, *answer;
915 ipp_attribute_t *attr;
916 int limit = 0;
917 PyObject *exclude_schemes_obj = NULL; /* string list */
918 PyObject *include_schemes_obj = NULL; /* string list */
919 char *ppd_natural_language = NULL;
920 PyObject *ppd_device_id_obj = NULL; /* UTF-8 string */
921 char *ppd_device_id;
922 PyObject *ppd_make_obj = NULL; /* UTF-8 string */
923 char *ppd_make;
924 PyObject *ppd_make_and_model_obj = NULL; /* UTF-8 string */
925 char *ppd_make_and_model;
926 int ppd_model_number = -1;
927 PyObject *ppd_product_obj = NULL; /* UTF-8 string */
928 char *ppd_product;
929 PyObject *ppd_psversion_obj = NULL; /* UTF-8 string */
930 char *ppd_psversion;
931 char *ppd_type = NULL;
932 static char *kwlist[] = { "limit",
933 "exclude_schemes",
934 "include_schemes",
935 "ppd_natural_language",
936 "ppd_device_id",
937 "ppd_make",
938 "ppd_make_and_model",
939 "ppd_model_number",
940 "ppd_product",
941 "ppd_psversion",
942 "ppd_type",
943 NULL };
944
945 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|iOOsOOOiOOs", kwlist,
946 &limit,
947 &exclude_schemes_obj, &include_schemes_obj,
948 &ppd_natural_language,
949 &ppd_device_id_obj, &ppd_make_obj,
950 &ppd_make_and_model_obj,
951 &ppd_model_number,
952 &ppd_product_obj, &ppd_psversion_obj,
953 &ppd_type))
954 return NULL;
955
956 request = ippNewRequest(CUPS_GET_PPDS);
957 if (limit > 0)
958 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
959 "limit", limit);
960
961 if (exclude_schemes_obj)
962 {
963 size_t i, n;
964 char **ss;
965 if (!PyList_Check (exclude_schemes_obj))
966 {
967 PyErr_SetString (PyExc_TypeError, "List required (exclude_schemes)");
968 ippDelete (request);
969 return NULL;
970 }
971
972 n = PyList_Size (exclude_schemes_obj);
973 ss = calloc (n + 1, sizeof (char *));
974 for (i = 0; i < n; i++)
975 {
976 PyObject *val = PyList_GetItem (exclude_schemes_obj, i); // borrowed
977 if (!PyUnicode_Check (val) && !PyBytes_Check (val))
978 {
979 PyErr_SetString (PyExc_TypeError,
980 "String list required (exclude_schemes)");
981 ippDelete (request);
982 while (i > 0)
983 free (ss[--i]);
984 free (ss);
985 return NULL;
986 }
987
988 UTF8_from_PyObj (&ss[i], val);
989 }
990 ss[n] = NULL;
991 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
992 "exclude-schemes", n, NULL, (const char **) ss);
993 for (i = 0; i < n; i++)
994 free (ss[i]);
995 free (ss);
996 }
997
998 if (include_schemes_obj)
999 {
1000 size_t i, n;
1001 char **ss;
1002 if (!PyList_Check (include_schemes_obj))
1003 {
1004 PyErr_SetString (PyExc_TypeError, "List required (include_schemes)");
1005 ippDelete (request);
1006 return NULL;
1007 }
1008
1009 n = PyList_Size (include_schemes_obj);
1010 ss = calloc (n + 1, sizeof (char *));
1011 for (i = 0; i < n; i++)
1012 {
1013 PyObject *val = PyList_GetItem (include_schemes_obj, i); // borrowed
1014 if (!PyUnicode_Check (val) && !PyBytes_Check (val))
1015 {
1016 PyErr_SetString (PyExc_TypeError,
1017 "String list required (include_schemes)");
1018 ippDelete (request);
1019 while (i > 0)
1020 free (ss[--i]);
1021 free (ss);
1022 return NULL;
1023 }
1024
1025 UTF8_from_PyObj (&ss[i], val);
1026 }
1027 ss[n] = NULL;
1028 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1029 "include-schemes", n, NULL, (const char **) ss);
1030 for (i = 0; i < n; i++)
1031 free (ss[i]);
1032 free (ss);
1033 }
1034
1035 if (ppd_device_id_obj)
1036 {
1037 if (UTF8_from_PyObj (&ppd_device_id, ppd_device_id_obj) == NULL)
1038 {
1039 ippDelete (request);
1040 return NULL;
1041 }
1042
1043 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1044 "ppd-device-id", NULL, ppd_device_id);
1045 free (ppd_device_id);
1046 }
1047
1048 if (ppd_make_obj)
1049 {
1050 if (UTF8_from_PyObj (&ppd_make, ppd_make_obj) == NULL)
1051 {
1052 ippDelete (request);
1053 return NULL;
1054 }
1055
1056 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1057 "ppd-make", NULL, ppd_make);
1058 free (ppd_make);
1059 }
1060
1061 if (ppd_make_and_model_obj)
1062 {
1063 if (UTF8_from_PyObj (&ppd_make_and_model, ppd_make_and_model_obj) == NULL)
1064 {
1065 ippDelete (request);
1066 return NULL;
1067 }
1068
1069 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1070 "ppd-make-and-model", NULL, ppd_make_and_model);
1071 free (ppd_make_and_model);
1072 }
1073
1074 if (ppd_model_number >= 0)
1075 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1076 "ppd-model-number", ppd_model_number);
1077
1078 if (ppd_product_obj)
1079 {
1080 if (UTF8_from_PyObj (&ppd_product, ppd_product_obj) == NULL)
1081 {
1082 ippDelete (request);
1083 return NULL;
1084 }
1085
1086 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1087 "ppd-product", NULL, ppd_product);
1088 free (ppd_product);
1089 }
1090
1091 if (ppd_psversion_obj)
1092 {
1093 if (UTF8_from_PyObj (&ppd_psversion, ppd_psversion_obj) == NULL)
1094 {
1095 ippDelete (request);
1096 return NULL;
1097 }
1098
1099 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1100 "ppd-psversion", NULL, ppd_psversion);
1101 free (ppd_psversion);
1102 }
1103
1104 if (ppd_natural_language)
1105 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1106 "ppd-natural-language", NULL, ppd_natural_language);
1107
1108 if (ppd_type)
1109 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1110 "ppd-type", NULL, ppd_type);
1111
1112 debugprintf ("-> Connection_getPPDs()\n");
1113 debugprintf ("cupsDoRequest(\"/\")\n");
1114 Connection_begin_allow_threads (self);
1115 answer = cupsDoRequest (self->http, request, "/");
1116 Connection_end_allow_threads (self);
1117 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1118 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1119 answer ? NULL : cupsLastErrorString ());
1120 if (answer)
1121 ippDelete (answer);
1122 debugprintf ("<- Connection_getPPDs() (error)\n");
1123 return NULL;
1124 }
1125
1126 result = PyDict_New ();
1127 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
1128 PyObject *dict;
1129 char *ppdname = NULL;
1130
1131 while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
1132 attr = ippNextAttribute (answer);
1133
1134 if (!attr)
1135 break;
1136
1137 dict = PyDict_New ();
1138 for (; attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER;
1139 attr = ippNextAttribute (answer)) {
1140 PyObject *val = NULL;
1141 debugprintf ("Attribute: %s\n", ippGetName (attr));
1142 if (!strcmp (ippGetName (attr), "ppd-name") &&
1143 ippGetValueTag (attr) == IPP_TAG_NAME)
1144 ppdname = (char *) ippGetString (attr, 0, NULL);
1145 else {
1146 if (all_lists)
1147 val = PyList_from_attr_values (attr);
1148 else
1149 val = PyObject_from_attr_value (attr, 0);
1150
1151 if (val) {
1152 debugprintf ("Adding %s to ppd dict\n", ippGetName (attr));
1153 PyDict_SetItemString (dict, ippGetName (attr), val);
1154 Py_DECREF (val);
1155 }
1156 }
1157 }
1158
1159 if (ppdname) {
1160 PyObject *key = PyObj_from_UTF8 (ppdname);
1161 debugprintf ("Adding %s to result dict\n", ppdname);
1162 PyDict_SetItem (result, key, dict);
1163 Py_DECREF (key);
1164 }
1165
1166 Py_DECREF (dict);
1167 if (!attr)
1168 break;
1169 }
1170
1171 ippDelete (answer);
1172 debugprintf ("<- Connection_getPPDs() = dict\n");
1173 return result;
1174 }
1175
1176 static PyObject *
Connection_getPPDs(Connection * self,PyObject * args,PyObject * kwds)1177 Connection_getPPDs (Connection *self, PyObject *args, PyObject *kwds)
1178 {
1179 return do_getPPDs (self, args, kwds, 0);
1180 }
1181
1182 static PyObject *
Connection_getPPDs2(Connection * self,PyObject * args,PyObject * kwds)1183 Connection_getPPDs2 (Connection *self, PyObject *args, PyObject *kwds)
1184 {
1185 return do_getPPDs (self, args, kwds, 1);
1186 }
1187
1188 static PyObject *
Connection_getServerPPD(Connection * self,PyObject * args)1189 Connection_getServerPPD (Connection *self, PyObject *args)
1190 {
1191 const char *ppd_name, *filename;
1192 if (!PyArg_ParseTuple (args, "s", &ppd_name))
1193 return NULL;
1194 debugprintf ("-> Connection_getServerPPD()\n");
1195 Connection_begin_allow_threads (self);
1196 filename = cupsGetServerPPD (self->http, ppd_name);
1197 Connection_end_allow_threads (self);
1198 if (!filename) {
1199 set_ipp_error (cupsLastError (), cupsLastErrorString ());
1200 debugprintf ("<- Connection_getServerPPD() (error)\n");
1201 return NULL;
1202 }
1203 debugprintf ("<- Connection_getServerPPD(\"%s\") = \"%s\"\n",
1204 ppd_name, filename);
1205 return PyUnicode_FromString (filename);
1206 }
1207
1208 static PyObject *
Connection_getDocument(Connection * self,PyObject * args)1209 Connection_getDocument (Connection *self, PyObject *args)
1210 {
1211 PyObject *dict;
1212 PyObject *obj;
1213 PyObject *uriobj;
1214 char *uri;
1215 int jobid, docnum;
1216 ipp_t *request, *answer;
1217 ipp_attribute_t *attr;
1218 const char *format = NULL;
1219 const char *name = NULL;
1220 char docfilename[PATH_MAX];
1221 int fd;
1222
1223 if (!PyArg_ParseTuple (args, "Oii", &uriobj, &jobid, &docnum))
1224 return NULL;
1225
1226 if (UTF8_from_PyObj (&uri, uriobj) == NULL)
1227 return NULL;
1228
1229 debugprintf ("-> Connection_getDocument(\"%s\",%d)\n", uri, jobid);
1230 request = ippNewRequest (CUPS_GET_DOCUMENT);
1231 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1232 "printer-uri", NULL, uri);
1233 free (uri);
1234 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1235 "job-id", jobid);
1236 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1237 "document-number", docnum);
1238
1239 snprintf(docfilename, sizeof (docfilename), "%s/jobdoc-XXXXXX", _PATH_TMP);
1240 fd = mkstemp (docfilename);
1241 if (fd < 0) {
1242 debugprintf ("<- Connection_getDocument() EXCEPTION\n");
1243 ippDelete (request);
1244 return PyErr_SetFromErrno (PyExc_RuntimeError);
1245 }
1246
1247 Connection_begin_allow_threads (self);
1248 answer = cupsDoIORequest (self->http, request, "/", -1, fd);
1249 Connection_end_allow_threads (self);
1250
1251 close (fd);
1252 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1253 unlink (docfilename);
1254 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1255 answer ? NULL : cupsLastErrorString ());
1256 if (answer)
1257 ippDelete (answer);
1258 debugprintf ("<- Connection_getDocument() (error)\n");
1259 return NULL;
1260 }
1261
1262 if ((attr = ippFindAttribute (answer, "document-format",
1263 IPP_TAG_MIMETYPE)) != NULL)
1264 format = ippGetString (attr, 0, NULL);
1265
1266 if ((attr = ippFindAttribute (answer, "document-name",
1267 IPP_TAG_NAME)) != NULL)
1268 name = ippGetString (attr, 0, NULL);
1269
1270 dict = PyDict_New ();
1271
1272 obj = PyUnicode_FromString (docfilename);
1273 PyDict_SetItemString (dict, "file", obj);
1274 Py_DECREF (obj);
1275
1276 if (format) {
1277 obj = PyUnicode_FromString (format);
1278 PyDict_SetItemString (dict, "document-format", obj);
1279 Py_DECREF (obj);
1280 }
1281
1282 if (name) {
1283 obj = PyObj_from_UTF8 (name);
1284 PyDict_SetItemString (dict, "document-name", obj);
1285 Py_DECREF (obj);
1286 }
1287
1288 debugprintf ("<- Connection_getDocument() = {'file':\"%s\","
1289 "'document-format':\"%s\",'document-name':\"%s\"}\n",
1290 docfilename, format ? format : "(nul)",
1291 name ? name : "(nul)");
1292 ippDelete (answer);
1293 return dict;
1294 }
1295
1296 static PyObject *
Connection_getDevices(Connection * self,PyObject * args,PyObject * kwds)1297 Connection_getDevices (Connection *self, PyObject *args, PyObject *kwds)
1298 {
1299 PyObject *result;
1300 ipp_t *request, *answer;
1301 ipp_attribute_t *attr;
1302 int limit = 0;
1303 int timeout = 0;
1304 PyObject *exclude_schemes = NULL;
1305 PyObject *include_schemes = NULL;
1306 static char *kwlist[] = { "limit",
1307 "exclude_schemes",
1308 "include_schemes",
1309 "timeout",
1310 NULL };
1311
1312 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|iOOi", kwlist, &limit,
1313 &exclude_schemes, &include_schemes,
1314 &timeout))
1315 return NULL;
1316
1317 request = ippNewRequest(CUPS_GET_DEVICES);
1318 if (limit > 0)
1319 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1320 "limit", limit);
1321
1322 if (exclude_schemes)
1323 {
1324 size_t i, n;
1325 char **ss;
1326 if (!PyList_Check (exclude_schemes))
1327 {
1328 PyErr_SetString (PyExc_TypeError, "List required (exclude_schemes)");
1329 ippDelete (request);
1330 return NULL;
1331 }
1332
1333 n = PyList_Size (exclude_schemes);
1334 ss = calloc (n + 1, sizeof (char *));
1335 for (i = 0; i < n; i++)
1336 {
1337 PyObject *val = PyList_GetItem (exclude_schemes, i); // borrowed ref
1338 if (!PyUnicode_Check (val) && !PyBytes_Check (val))
1339 {
1340 PyErr_SetString (PyExc_TypeError,
1341 "String list required (exclude_schemes)");
1342 ippDelete (request);
1343 while (i > 0)
1344 free (ss[--i]);
1345 free (ss);
1346 return NULL;
1347 }
1348
1349 UTF8_from_PyObj (&ss[i], val);
1350 }
1351
1352 ss[n] = NULL;
1353 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1354 "exclude-schemes", n, NULL, (const char **) ss);
1355 for (i = 0; i < n; i++)
1356 free (ss[i]);
1357 free (ss);
1358 }
1359
1360 if (include_schemes)
1361 {
1362 size_t i, n;
1363 char **ss;
1364 if (!PyList_Check (include_schemes))
1365 {
1366 PyErr_SetString (PyExc_TypeError, "List required (include_schemes)");
1367 ippDelete (request);
1368 return NULL;
1369 }
1370
1371 n = PyList_Size (include_schemes);
1372 ss = calloc (n + 1, sizeof (char *));
1373 for (i = 0; i < n; i++)
1374 {
1375 PyObject *val = PyList_GetItem (include_schemes, i); // borrowed ref
1376 if (!PyUnicode_Check (val) && !PyBytes_Check (val))
1377 {
1378 PyErr_SetString (PyExc_TypeError,
1379 "String list required (include_schemes)");
1380 ippDelete (request);
1381 while (i > 0)
1382 free (ss[--i]);
1383 free (ss);
1384 return NULL;
1385 }
1386
1387 UTF8_from_PyObj (&ss[i], val);
1388 }
1389
1390 ss[n] = NULL;
1391 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1392 "include-schemes", n, NULL, (const char **) ss);
1393 for (i = 0; i < n; i++)
1394 free (ss[i]);
1395 free (ss);
1396 }
1397
1398 if (timeout > 0)
1399 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1400 "timeout", timeout);
1401
1402 debugprintf ("-> Connection_getDevices()\n");
1403 debugprintf ("cupsDoRequest(\"/\")\n");
1404 Connection_begin_allow_threads (self);
1405 answer = cupsDoRequest (self->http, request, "/");
1406 Connection_end_allow_threads (self);
1407 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1408 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1409 answer ? NULL : cupsLastErrorString ());
1410 if (answer)
1411 ippDelete (answer);
1412 debugprintf ("<- Connection_getDevices() (error)\n");
1413 return NULL;
1414 }
1415
1416 result = PyDict_New ();
1417 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
1418 PyObject *dict;
1419 char *device_uri = NULL;
1420
1421 while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
1422 attr = ippNextAttribute (answer);
1423
1424 if (!attr)
1425 break;
1426
1427 dict = PyDict_New ();
1428 for (; attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER;
1429 attr = ippNextAttribute (answer)) {
1430 PyObject *val = NULL;
1431
1432 debugprintf ("Attribute: %s\n", ippGetName (attr));
1433 if (!strcmp (ippGetName (attr), "device-uri") &&
1434 ippGetValueTag (attr) == IPP_TAG_URI)
1435 device_uri = (char *) ippGetString (attr, 0, NULL);
1436 else
1437 val = PyObject_from_attr_value (attr, 0);
1438
1439 if (val) {
1440 debugprintf ("Adding %s to device dict\n", ippGetName (attr));
1441 PyDict_SetItemString (dict, ippGetName (attr), val);
1442 Py_DECREF (val);
1443 }
1444 }
1445
1446 if (device_uri) {
1447 PyObject *key = PyObj_from_UTF8 (device_uri);
1448 debugprintf ("Adding %s to result dict\n", device_uri);
1449 PyDict_SetItem (result, key, dict);
1450 Py_DECREF (key);
1451 }
1452
1453 Py_DECREF (dict);
1454 if (!attr)
1455 break;
1456 }
1457
1458 ippDelete (answer);
1459 debugprintf ("<- Connection_getDevices() = dict\n");
1460 return result;
1461 }
1462
1463 static int
get_requested_attrs(PyObject * requested_attrs,size_t * n_attrs,char *** attrs)1464 get_requested_attrs (PyObject *requested_attrs, size_t *n_attrs, char ***attrs)
1465 {
1466 long unsigned int i;
1467 size_t n;
1468 char **as;
1469
1470 if (!PyList_Check (requested_attrs)) {
1471 PyErr_SetString (PyExc_TypeError, "List required");
1472 return -1;
1473 }
1474
1475 n = PyList_Size (requested_attrs);
1476 as = malloc ((n + 1) * sizeof (char *));
1477 for (i = 0; i < n; i++) {
1478 PyObject *val = PyList_GetItem (requested_attrs, i); // borrowed ref
1479 if (!PyUnicode_Check (val) && !PyBytes_Check (val)) {
1480 PyErr_SetString (PyExc_TypeError, "String required");
1481 while (--i >= 0)
1482 free (as[i]);
1483 free (as);
1484 return -1;
1485 }
1486
1487 UTF8_from_PyObj (&as[i], val);
1488 }
1489 as[n] = NULL;
1490
1491 debugprintf ("Requested attributes:\n");
1492 for (i = 0; as[i] != NULL; i++)
1493 debugprintf (" %s\n", as[i]);
1494
1495 *n_attrs = n;
1496 *attrs = as;
1497 return 0;
1498 }
1499
1500 static void
free_requested_attrs(size_t n_attrs,char ** attrs)1501 free_requested_attrs (size_t n_attrs, char **attrs)
1502 {
1503 long unsigned int i;
1504 for (i = 0; i < n_attrs; i++)
1505 free (attrs[i]);
1506 free (attrs);
1507 }
1508
1509 static PyObject *
Connection_getJobs(Connection * self,PyObject * args,PyObject * kwds)1510 Connection_getJobs (Connection *self, PyObject *args, PyObject *kwds)
1511 {
1512 PyObject *result;
1513 ipp_t *request, *answer;
1514 ipp_attribute_t *attr;
1515 char *which = NULL;
1516 int my_jobs = 0;
1517 int limit = -1;
1518 int first_job_id = -1;
1519 PyObject *requested_attrs = NULL;
1520 char **attrs = NULL; /* initialised to calm compiler */
1521 size_t n_attrs = 0; /* initialised to calm compiler */
1522 static char *kwlist[] = { "which_jobs", "my_jobs", "limit", "first_job_id",
1523 "requested_attributes", NULL };
1524 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|siiiO", kwlist,
1525 &which, &my_jobs, &limit, &first_job_id,
1526 &requested_attrs))
1527 return NULL;
1528
1529 debugprintf ("-> Connection_getJobs(%s,%d)\n",
1530 which ? which : "(null)", my_jobs);
1531 request = ippNewRequest(IPP_GET_JOBS);
1532 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1533 NULL, "ipp://localhost/printers/");
1534
1535 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
1536 NULL, which ? which : "not-completed");
1537
1538 ippAddBoolean (request, IPP_TAG_OPERATION, "my-jobs", my_jobs);
1539 if (my_jobs)
1540 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1541 "requesting-user-name", NULL, cupsUser());
1542
1543 if (limit > 0)
1544 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1545 "limit", limit);
1546
1547 if (first_job_id > 0)
1548 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1549 "first-job-id", first_job_id);
1550
1551 if (requested_attrs) {
1552 if (get_requested_attrs (requested_attrs, &n_attrs, &attrs) == -1) {
1553 ippDelete (request);
1554 return NULL;
1555 }
1556
1557 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1558 "requested-attributes", n_attrs, NULL,
1559 (const char **) attrs);
1560 free_requested_attrs (n_attrs, attrs);
1561 }
1562
1563 debugprintf ("cupsDoRequest(\"/\")\n");
1564 Connection_begin_allow_threads (self);
1565 answer = cupsDoRequest (self->http, request, "/");
1566 Connection_end_allow_threads (self);
1567 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1568 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1569 answer ? NULL : cupsLastErrorString ());
1570 if (answer)
1571 ippDelete (answer);
1572 debugprintf ("<- Connection_getJobs() (error)\n");
1573 return NULL;
1574 }
1575
1576 result = PyDict_New ();
1577 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
1578 PyObject *dict;
1579 int job_id = -1;
1580
1581 while (attr && ippGetGroupTag (attr) != IPP_TAG_JOB)
1582 attr = ippNextAttribute (answer);
1583
1584 if (!attr)
1585 break;
1586
1587 dict = PyDict_New ();
1588 for (; attr && ippGetGroupTag (attr) == IPP_TAG_JOB;
1589 attr = ippNextAttribute (answer)) {
1590 PyObject *val = NULL;
1591
1592 debugprintf ("Attribute: %s\n", ippGetName (attr));
1593 if (!strcmp (ippGetName (attr), "job-id") &&
1594 ippGetValueTag (attr) == IPP_TAG_INTEGER)
1595 job_id = ippGetInteger (attr, 0);
1596 else if (((!strcmp (ippGetName (attr), "job-k-octets") ||
1597 !strcmp (ippGetName (attr), "job-priority") ||
1598 !strcmp (ippGetName (attr), "time-at-creation") ||
1599 !strcmp (ippGetName (attr), "time-at-processing") ||
1600 !strcmp (ippGetName (attr), "time-at-completed") ||
1601 !strcmp (ippGetName (attr), "job-media-sheets") ||
1602 !strcmp (ippGetName (attr), "job-media-sheets-completed")) &&
1603 ippGetValueTag (attr) == IPP_TAG_INTEGER) ||
1604 (!strcmp (ippGetName (attr), "job-state") &&
1605 ippGetValueTag (attr) == IPP_TAG_ENUM))
1606 val = PyLong_FromLong (ippGetInteger (attr, 0));
1607 else if ((!strcmp (ippGetName (attr), "job-name") &&
1608 ippGetValueTag (attr) == IPP_TAG_NAME) ||
1609 (!strcmp (ippGetName (attr), "job-originating-user-name") &&
1610 ippGetValueTag (attr) == IPP_TAG_NAME) ||
1611 (!strcmp (ippGetName (attr), "job-printer-uri") &&
1612 ippGetValueTag (attr) == IPP_TAG_URI))
1613 val = PyObj_from_UTF8 (ippGetString (attr, 0, NULL));
1614 else if (!strcmp (ippGetName (attr), "job-preserved") &&
1615 ippGetValueTag (attr) == IPP_TAG_BOOLEAN)
1616 val = PyBool_FromLong (ippGetInteger (attr, 0));
1617 else {
1618 if (ippGetCount (attr) > 1)
1619 val = PyList_from_attr_values (attr);
1620 else
1621 val = PyObject_from_attr_value (attr, 0);
1622 }
1623
1624 if (val) {
1625 debugprintf ("Adding %s to job dict\n", ippGetName (attr));
1626 PyDict_SetItemString (dict, ippGetName (attr), val);
1627 Py_DECREF (val);
1628 }
1629 }
1630
1631 if (job_id != -1) {
1632 debugprintf ("Adding %d to result dict\n", job_id);
1633 PyObject *job_obj = PyLong_FromLong (job_id);
1634 PyDict_SetItem (result, job_obj, dict);
1635 Py_DECREF (job_obj);
1636 }
1637
1638 Py_DECREF (dict);
1639
1640 if (!attr)
1641 break;
1642 }
1643
1644 ippDelete (answer);
1645 debugprintf ("<- Connection_getJobs() = dict\n");
1646 return result;
1647 }
1648
1649 static PyObject *
Connection_getJobAttributes(Connection * self,PyObject * args,PyObject * kwds)1650 Connection_getJobAttributes (Connection *self, PyObject *args, PyObject *kwds)
1651 {
1652 PyObject *result;
1653 ipp_t *request, *answer;
1654 ipp_attribute_t *attr;
1655 int job_id;
1656 PyObject *requested_attrs = NULL;
1657 char **attrs = NULL; /* initialised to calm compiler */
1658 size_t n_attrs = 0; /* initialised to calm compiler */
1659 char uri[1024];
1660 static char *kwlist[] = { "job_id", "requested_attributes", NULL };
1661 if (!PyArg_ParseTupleAndKeywords (args, kwds, "i|O", kwlist,
1662 &job_id, &requested_attrs))
1663 return NULL;
1664
1665 if (requested_attrs) {
1666 if (get_requested_attrs (requested_attrs, &n_attrs, &attrs) == -1)
1667 return NULL;
1668 }
1669
1670 debugprintf ("-> Connection_getJobAttributes(%d)\n", job_id);
1671 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1672 snprintf (uri, sizeof (uri), "ipp://localhost/jobs/%d", job_id);
1673 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
1674 NULL, uri);
1675 if (requested_attrs)
1676 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1677 "requested-attributes", n_attrs, NULL,
1678 (const char **) attrs);
1679
1680 debugprintf ("cupsDoRequest(\"/\")\n");
1681 Connection_begin_allow_threads (self);
1682 answer = cupsDoRequest (self->http, request, "/");
1683 Connection_end_allow_threads (self);
1684 if (requested_attrs)
1685 free_requested_attrs (n_attrs, attrs);
1686 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1687 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1688 answer ? NULL : cupsLastErrorString ());
1689 if (answer)
1690 ippDelete (answer);
1691 debugprintf ("<- Connection_getJobAttributes() (error)\n");
1692 return NULL;
1693 }
1694
1695 result = PyDict_New ();
1696 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
1697 PyObject *obj;
1698
1699 debugprintf ("Attr: %s\n", ippGetName (attr));
1700 if (ippGetCount (attr) > 1 ||
1701 !strcmp (ippGetName (attr), "job-printer-state-reasons"))
1702 obj = PyList_from_attr_values (attr);
1703 else
1704 obj = PyObject_from_attr_value (attr, 0);
1705
1706 if (!obj)
1707 // Can't represent this.
1708 continue;
1709
1710 PyDict_SetItemString (result, ippGetName (attr), obj);
1711 Py_DECREF (obj);
1712 }
1713
1714 ippDelete (answer);
1715 debugprintf ("<- Connection_getJobAttributes() = dict\n");
1716 return result;
1717 }
1718
1719 static PyObject *
Connection_cancelJob(Connection * self,PyObject * args,PyObject * kwds)1720 Connection_cancelJob (Connection *self, PyObject *args, PyObject *kwds)
1721 {
1722 ipp_t *request, *answer;
1723 int job_id;
1724 int purge_job = 0;
1725 char uri[1024];
1726 static char *kwlist[] = { "job_id", "purge_job", NULL };
1727 if (!PyArg_ParseTupleAndKeywords (args, kwds, "i|i", kwlist,
1728 &job_id, &purge_job))
1729 return NULL;
1730
1731 debugprintf ("-> Connection_cancelJob(%d)\n", job_id);
1732 request = ippNewRequest(IPP_CANCEL_JOB);
1733 snprintf (uri, sizeof (uri), "ipp://localhost/jobs/%d", job_id);
1734 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1735 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1736 "requesting-user-name", NULL, cupsUser ());
1737 if (purge_job)
1738 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
1739 debugprintf ("cupsDoRequest(\"/jobs/\")\n");
1740 Connection_begin_allow_threads (self);
1741 answer = cupsDoRequest (self->http, request, "/jobs/");
1742 Connection_end_allow_threads (self);
1743 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1744 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1745 answer ? NULL : cupsLastErrorString ());
1746 if (answer)
1747 ippDelete (answer);
1748 debugprintf ("<- Connection_cancelJob() (error)\n");
1749 return NULL;
1750 }
1751
1752 debugprintf ("<- Connection_cancelJob() = None\n");
1753 Py_RETURN_NONE;
1754 }
1755
1756 static PyObject *
Connection_cancelAllJobs(Connection * self,PyObject * args,PyObject * kwds)1757 Connection_cancelAllJobs (Connection *self, PyObject *args, PyObject *kwds)
1758 {
1759 PyObject *nameobj = NULL;
1760 char *name;
1761 PyObject *uriobj = NULL;
1762 char *uri;
1763 char consuri[HTTP_MAX_URI];
1764 ipp_t *request, *answer;
1765 int my_jobs = 0;
1766 int purge_jobs = 1;
1767 int i;
1768 static char *kwlist[] = { "name", "uri", "my_jobs", "purge_jobs", NULL };
1769 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|OOii", kwlist,
1770 &nameobj, &uriobj, &my_jobs, &purge_jobs))
1771 return NULL;
1772
1773 if (nameobj && uriobj) {
1774 PyErr_SetString (PyExc_RuntimeError,
1775 "name or uri must be specified but not both");
1776 return NULL;
1777 }
1778
1779 if (nameobj) {
1780 if (UTF8_from_PyObj (&name, nameobj) == NULL)
1781 return NULL;
1782 } else if (uriobj) {
1783 if (UTF8_from_PyObj (&uri, uriobj) == NULL)
1784 return NULL;
1785 } else {
1786 PyErr_SetString (PyExc_RuntimeError,
1787 "name or uri must be specified");
1788 return NULL;
1789 }
1790
1791 debugprintf ("-> Connection_cancelAllJobs(%s, my_jobs=%d, purge_jobs=%d)\n",
1792 nameobj ? name : uri, my_jobs, purge_jobs);
1793 if (nameobj) {
1794 construct_uri (consuri, sizeof (consuri),
1795 "ipp://localhost/printers/", name);
1796 uri = consuri;
1797 }
1798
1799 for (i = 0; i < 2; i++) {
1800 request = ippNewRequest(IPP_PURGE_JOBS);
1801 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1802 NULL, uri);
1803
1804 if (my_jobs)
1805 {
1806 ippAddBoolean (request, IPP_TAG_OPERATION, "my-jobs", my_jobs);
1807 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1808 "requesting-user-name", NULL, cupsUser());
1809 }
1810
1811 ippAddBoolean (request, IPP_TAG_OPERATION, "purge-jobs", purge_jobs);
1812 debugprintf ("cupsDoRequest(\"/admin/\") with printer-uri=%s\n", uri);
1813 Connection_begin_allow_threads (self);
1814 answer = cupsDoRequest (self->http, request, "/admin/");
1815 Connection_end_allow_threads (self);
1816 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
1817 ippDelete (answer);
1818 if (uriobj)
1819 break;
1820
1821 // Perhaps it's a class, not a printer.
1822 construct_uri (consuri, sizeof (consuri),
1823 "ipp://localhost/classes/", name);
1824 } else break;
1825 }
1826
1827 if (nameobj)
1828 free (name);
1829
1830 if (uriobj)
1831 free (uri);
1832
1833 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
1834 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
1835 answer ? NULL : cupsLastErrorString ());
1836 if (answer)
1837 ippDelete (answer);
1838
1839 debugprintf ("<- Connection_cancelAllJobs() (error)\n");
1840 return NULL;
1841 }
1842
1843 debugprintf ("<- Connection_cancelAllJobs() = None\n");
1844 Py_RETURN_NONE;
1845 }
1846
1847 static PyObject *
Connection_createJob(Connection * self,PyObject * args,PyObject * kwds)1848 Connection_createJob (Connection *self, PyObject *args, PyObject *kwds)
1849 {
1850 static char *kwlist[] = { "printer", "title", "options", NULL };
1851 PyObject *printer_obj;
1852 char *printer;
1853 PyObject *title_obj;
1854 char *title;
1855 PyObject *options_obj, *key, *val;
1856 int num_settings = 0;
1857 DICT_POS_TYPE pos = 0;
1858 cups_option_t *settings = NULL;
1859 int jobid;
1860
1861 if (!PyArg_ParseTupleAndKeywords (args, kwds, "OOO", kwlist,
1862 &printer_obj, &title_obj,
1863 &options_obj))
1864 return NULL;
1865
1866 if (UTF8_from_PyObj (&printer, printer_obj) == NULL)
1867 return NULL;
1868 if (UTF8_from_PyObj (&title, title_obj) == NULL) {
1869 free (printer);
1870 return NULL;
1871 }
1872 debugprintf ("-> Connection_createJob(printer=%s, title=%s)\n", printer, title);
1873
1874 if (!PyDict_Check (options_obj)) {
1875 free (title);
1876 free (printer);
1877 PyErr_SetString (PyExc_TypeError, "options must be a dict");
1878 return NULL;
1879 }
1880 while (PyDict_Next (options_obj, &pos, &key, &val)) {
1881 char *name, *value;
1882 if ((!PyUnicode_Check (key) && !PyBytes_Check (key)) ||
1883 (!PyUnicode_Check (val) && !PyBytes_Check (val))) {
1884 cupsFreeOptions (num_settings, settings);
1885 free (title);
1886 free (printer);
1887 PyErr_SetString (PyExc_TypeError, "Keys and values must be strings");
1888 return NULL;
1889 }
1890
1891 num_settings = cupsAddOption (UTF8_from_PyObj (&name, key),
1892 UTF8_from_PyObj (&value, val),
1893 num_settings,
1894 &settings);
1895 free (name);
1896 free (value);
1897 }
1898
1899 Connection_begin_allow_threads (self);
1900 jobid = cupsCreateJob(self->http, printer, title, num_settings, settings);
1901 Connection_end_allow_threads (self);
1902
1903 if (jobid == 0) {
1904 cupsFreeOptions (num_settings, settings);
1905 free (title);
1906 free (printer);
1907 set_ipp_error (cupsLastError (), cupsLastErrorString ());
1908 debugprintf ("<- Connection_createJob() = NULL\n");
1909 return NULL;
1910 }
1911
1912 cupsFreeOptions (num_settings, settings);
1913 free (title);
1914 free (printer);
1915 debugprintf ("<- Connection_createJob() = %d\n", jobid);
1916 return PyLong_FromLong (jobid);
1917 }
1918
1919 static PyObject *
Connection_startDocument(Connection * self,PyObject * args,PyObject * kwds)1920 Connection_startDocument (Connection *self, PyObject *args, PyObject *kwds)
1921 {
1922 static char *kwlist[] = { "printer", "job_id", "doc_name", "format", "last_document", NULL };
1923 PyObject *printer_obj;
1924 char *printer;
1925 int jobid;
1926 PyObject *doc_name_obj;
1927 char *doc_name;
1928 PyObject *format_obj;
1929 char *format;
1930 int last_document;
1931 http_status_t status; /* Write status */
1932
1933 if (!PyArg_ParseTupleAndKeywords (args, kwds, "OiOOi", kwlist,
1934 &printer_obj, &jobid, &doc_name_obj,
1935 &format_obj, &last_document))
1936 return NULL;
1937
1938 if (UTF8_from_PyObj (&printer, printer_obj) == NULL)
1939 return NULL;
1940 if (UTF8_from_PyObj (&doc_name, doc_name_obj) == NULL) {
1941 free (printer);
1942 return NULL;
1943 }
1944 if (UTF8_from_PyObj (&format, format_obj) == NULL) {
1945 free (doc_name);
1946 free (printer);
1947 return NULL;
1948 }
1949 debugprintf ("-> Connection_startDocument(printer=%s, jobid=%d, doc_name=%s, format=%s)\n",
1950 printer, jobid, doc_name, format);
1951
1952 Connection_begin_allow_threads (self);
1953 status = cupsStartDocument(self->http, printer, jobid, doc_name, format, last_document);
1954 Connection_end_allow_threads (self);
1955
1956 if (status != HTTP_CONTINUE)
1957 {
1958 free (format);
1959 free (doc_name);
1960 free (printer);
1961 set_ipp_error (cupsLastError (), cupsLastErrorString ());
1962 debugprintf ("<- Connection_startDocument() = NULL\n");
1963 return NULL;
1964 }
1965
1966 free (format);
1967 free (doc_name);
1968 free (printer);
1969 debugprintf ("<- Connection_startDocument() = %d\n", status);
1970 return PyLong_FromLong (status);
1971 }
1972
1973 static PyObject *
Connection_writeRequestData(Connection * self,PyObject * args,PyObject * kwds)1974 Connection_writeRequestData (Connection *self, PyObject *args, PyObject *kwds)
1975 {
1976 static char *kwlist[] = { "buffer", "length", NULL };
1977 PyObject *buffer_obj;
1978 char *buffer;
1979 int length;
1980 http_status_t status; /* Write status */
1981
1982 if (!PyArg_ParseTupleAndKeywords (args, kwds, "Oi", kwlist,
1983 &buffer_obj, &length))
1984 return NULL;
1985
1986 buffer = (char *) malloc((size_t) length);
1987 memcpy(buffer, PyBytes_AsString(buffer_obj), length);
1988 debugprintf ("-> Connection_writeRequestData(length=%d)\n", length);
1989
1990 Connection_begin_allow_threads (self);
1991 status = cupsWriteRequestData(self->http, buffer, length);
1992 Connection_end_allow_threads (self);
1993
1994 if (status != HTTP_CONTINUE)
1995 {
1996 free (buffer);
1997 set_ipp_error (cupsLastError (), cupsLastErrorString ());
1998 debugprintf ("<- Connection_writeRequestData() = NULL\n");
1999 return NULL;
2000 }
2001
2002 free (buffer);
2003 debugprintf ("<- Connection_writeRequestData() = %d\n", status);
2004 return PyLong_FromLong (status);
2005 }
2006
2007 static PyObject *
Connection_finishDocument(Connection * self,PyObject * args,PyObject * kwds)2008 Connection_finishDocument (Connection *self, PyObject *args, PyObject *kwds)
2009 {
2010 static char *kwlist[] = { "printer", NULL };
2011 PyObject *printer_obj;
2012 char *printer;
2013 int answer;
2014
2015 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O", kwlist, &printer_obj))
2016 return NULL;
2017
2018 if (UTF8_from_PyObj (&printer, printer_obj) == NULL)
2019 return NULL;
2020
2021 debugprintf ("-> Connection_finishDocument(printer=%s)\n", printer);
2022 Connection_begin_allow_threads (self);
2023 answer = cupsFinishDocument(self->http, printer);
2024 Connection_end_allow_threads (self);
2025
2026 if (answer != IPP_OK) {
2027 free (printer);
2028 set_ipp_error (cupsLastError (), cupsLastErrorString ());
2029 debugprintf ("<- Connection_finishDocument() = NULL\n");
2030 return NULL;
2031 }
2032
2033 free (printer);
2034 debugprintf ("<- Connection_finishDicument() = %d\n", answer);
2035 return PyLong_FromLong (answer);
2036 }
2037
2038 static PyObject *
Connection_moveJob(Connection * self,PyObject * args,PyObject * kwds)2039 Connection_moveJob (Connection *self, PyObject *args, PyObject *kwds)
2040 {
2041 int job_id = -1;
2042 PyObject *printeruriobj = NULL;
2043 char *printeruri;
2044 PyObject *jobprinteruriobj = NULL;
2045 char *jobprinteruri;
2046 ipp_t *request, *answer;
2047 static char *kwlist[] = { "printer_uri", "job_id", "job_printer_uri", NULL };
2048
2049 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|OiO", kwlist,
2050 &printeruriobj, &job_id,
2051 &jobprinteruriobj))
2052 return NULL;
2053
2054 if (!jobprinteruriobj) {
2055 PyErr_SetString (PyExc_RuntimeError,
2056 "No job_printer_uri (destination) given");
2057 return NULL;
2058 }
2059
2060 if (printeruriobj) {
2061 if (UTF8_from_PyObj (&printeruri, printeruriobj) == NULL)
2062 return NULL;
2063 } else if (job_id == -1) {
2064 PyErr_SetString (PyExc_RuntimeError,
2065 "job_id or printer_uri required");
2066 return NULL;
2067 }
2068
2069 if (UTF8_from_PyObj (&jobprinteruri, jobprinteruriobj) == NULL) {
2070 if (printeruriobj)
2071 free (printeruri);
2072 return NULL;
2073 }
2074
2075 request = ippNewRequest (CUPS_MOVE_JOB);
2076 if (!printeruriobj) {
2077 char joburi[HTTP_MAX_URI];
2078 snprintf (joburi, sizeof (joburi), "ipp://localhost/jobs/%d", job_id);
2079 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
2080 joburi);
2081 } else {
2082 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2083 printeruri);
2084 free (printeruri);
2085
2086 if (job_id != -1)
2087 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2088 job_id);
2089 }
2090
2091 ippAddString (request, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
2092 jobprinteruri);
2093 free (jobprinteruri);
2094 Connection_begin_allow_threads (self);
2095 answer = cupsDoRequest (self->http, request, "/jobs");
2096 Connection_end_allow_threads (self);
2097 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2098 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2099 answer ? NULL : cupsLastErrorString ());
2100 if (answer)
2101 ippDelete (answer);
2102 return NULL;
2103 }
2104
2105 ippDelete (answer);
2106 Py_RETURN_NONE;
2107 }
2108
2109 static PyObject *
Connection_authenticateJob(Connection * self,PyObject * args)2110 Connection_authenticateJob (Connection *self, PyObject *args)
2111 {
2112 ipp_t *request, *answer;
2113 int job_id;
2114 PyObject *auth_info_list = NULL;
2115 long unsigned int num_auth_info = 0; /* initialised to calm compiler */
2116 char *auth_info_values[3];
2117 long unsigned int i;
2118 char uri[1024];
2119
2120 if (!PyArg_ParseTuple (args, "i|O", &job_id, &auth_info_list))
2121 return NULL;
2122
2123 if (auth_info_list) {
2124 if (!PyList_Check (auth_info_list)) {
2125 PyErr_SetString (PyExc_TypeError, "List required");
2126 return NULL;
2127 }
2128
2129 num_auth_info = PyList_Size (auth_info_list);
2130 debugprintf ("sizeof values = %Zd\n", sizeof (auth_info_values));
2131 if (num_auth_info > sizeof (auth_info_values))
2132 num_auth_info = sizeof (auth_info_values);
2133
2134 for (i = 0; i < num_auth_info; i++) {
2135 PyObject *val = PyList_GetItem (auth_info_list, i); // borrowed ref
2136 if (UTF8_from_PyObj (&auth_info_values[i], val) == NULL) {
2137 while (--i >= 0)
2138 free (auth_info_values[i]);
2139 return NULL;
2140 }
2141 }
2142 }
2143
2144 debugprintf ("-> Connection_authenticateJob(%d)\n", job_id);
2145 request = ippNewRequest(CUPS_AUTHENTICATE_JOB);
2146 snprintf (uri, sizeof (uri), "ipp://localhost/jobs/%d", job_id);
2147 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
2148 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2149 "requesting-user-name", NULL, cupsUser ());
2150 if (auth_info_list)
2151 {
2152 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "auth-info",
2153 num_auth_info, NULL,
2154 (const char *const *) auth_info_values);
2155 for (i = 0; i < num_auth_info; i++)
2156 free (auth_info_values[i]);
2157 }
2158
2159 debugprintf ("cupsDoRequest(\"/jobs/\")\n");
2160 Connection_begin_allow_threads (self);
2161 answer = cupsDoRequest (self->http, request, "/jobs/");
2162 Connection_end_allow_threads (self);
2163 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2164 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2165 answer ? NULL : cupsLastErrorString ());
2166 if (answer)
2167 ippDelete (answer);
2168 debugprintf ("<- Connection_authenticateJob() (error)\n");
2169 return NULL;
2170 }
2171
2172 debugprintf ("<- Connection_authenticateJob() = None\n");
2173 Py_RETURN_NONE;
2174 }
2175
2176 static PyObject *
Connection_setJobHoldUntil(Connection * self,PyObject * args)2177 Connection_setJobHoldUntil (Connection *self, PyObject *args)
2178 {
2179 ipp_t *request, *answer;
2180 int job_id;
2181 PyObject *job_hold_until_obj;
2182 char *job_hold_until;
2183 char uri[1024];
2184 cups_option_t *options = NULL;
2185 int num_options = 0;
2186 if (!PyArg_ParseTuple (args, "iO", &job_id, &job_hold_until_obj))
2187 return NULL;
2188
2189 if (UTF8_from_PyObj (&job_hold_until, job_hold_until_obj) == NULL)
2190 return NULL;
2191
2192 debugprintf ("-> Connection_setJobHoldUntil(%d,%s)\n",
2193 job_id, job_hold_until);
2194 request = ippNewRequest(IPP_SET_JOB_ATTRIBUTES);
2195 snprintf (uri, sizeof (uri), "ipp://localhost/jobs/%d", job_id);
2196 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
2197 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2198 "requesting-user-name", NULL, cupsUser ());
2199 num_options = cupsAddOption ("job-hold-until", job_hold_until,
2200 num_options, &options);
2201 cupsEncodeOptions (request, num_options, options);
2202 free (job_hold_until);
2203
2204 debugprintf ("cupsDoRequest(\"/jobs/\")\n");
2205 Connection_begin_allow_threads (self);
2206 answer = cupsDoRequest (self->http, request, "/jobs/");
2207 Connection_end_allow_threads (self);
2208 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2209 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2210 answer ? NULL : cupsLastErrorString ());
2211 if (answer)
2212 ippDelete (answer);
2213 debugprintf ("<- Connection_setJobHoldUntil() (error)\n");
2214 return NULL;
2215 }
2216
2217 debugprintf ("<- Connection_setJobHoldUntil() = None\n");
2218 Py_RETURN_NONE;
2219 }
2220
2221 static PyObject *
Connection_restartJob(Connection * self,PyObject * args,PyObject * kwds)2222 Connection_restartJob (Connection *self, PyObject *args, PyObject *kwds)
2223 {
2224 static char *kwlist[] = { "job_id", "job_hold_until", NULL };
2225 ipp_t *request, *answer;
2226 int job_id;
2227 char *job_hold_until = NULL;
2228 char uri[1024];
2229 if (!PyArg_ParseTupleAndKeywords (args, kwds, "i|s", kwlist,
2230 &job_id, &job_hold_until))
2231 return NULL;
2232
2233 debugprintf ("-> Connection_restartJob(%d)\n", job_id);
2234 request = ippNewRequest(IPP_RESTART_JOB);
2235 snprintf (uri, sizeof (uri), "ipp://localhost/jobs/%d", job_id);
2236 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
2237 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2238 "requesting-user-name", NULL, cupsUser ());
2239 if (job_hold_until)
2240 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2241 "job-hold-until", NULL, job_hold_until);
2242
2243 debugprintf ("cupsDoRequest(\"/jobs/\")\n");
2244 Connection_begin_allow_threads (self);
2245 answer = cupsDoRequest (self->http, request, "/jobs/");
2246 Connection_end_allow_threads (self);
2247 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2248 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2249 answer ? NULL : cupsLastErrorString ());
2250 if (answer)
2251 ippDelete (answer);
2252 debugprintf ("<- Connection_restartJob() (error)\n");
2253 return NULL;
2254 }
2255
2256 debugprintf ("<- Connection_restartJob() = None\n");
2257 Py_RETURN_NONE;
2258 }
2259
2260 static PyObject *
Connection_getFile(Connection * self,PyObject * args,PyObject * kwds)2261 Connection_getFile (Connection *self, PyObject *args, PyObject *kwds)
2262 {
2263 static char *kwlist[] = { "resource", "filename", "fd", "file", NULL };
2264 const char *resource, *filename = NULL;
2265 int fd = -1;
2266 PyObject *fileobj = NULL;
2267 http_status_t status;
2268
2269 if (!PyArg_ParseTupleAndKeywords (args, kwds, "s|siO", kwlist,
2270 &resource, &filename, &fd, &fileobj))
2271 return NULL;
2272
2273 if ((fd > -1 && (filename || fileobj)) ||
2274 (filename && fileobj)) {
2275 PyErr_SetString (PyExc_RuntimeError,
2276 "Only one destination type may be specified");
2277 return NULL;
2278 }
2279
2280 if (fileobj) {
2281 fd = PyObject_AsFileDescriptor(fileobj);
2282 }
2283
2284 if (filename) {
2285 debugprintf ("-> Connection_getFile(%s, %s)\n", resource, filename);
2286 debugprintf ("cupsGetFile()\n");
2287 Connection_begin_allow_threads (self);
2288 status = cupsGetFile (self->http, resource, filename);
2289 Connection_end_allow_threads (self);
2290 } else {
2291 debugprintf ("-> Connection_getFile(%s, %d)\n", resource, fd);
2292 debugprintf ("cupsGetFd()\n");
2293 Connection_begin_allow_threads (self);
2294 status = cupsGetFd (self->http, resource, fd);
2295 Connection_end_allow_threads (self);
2296 }
2297
2298 if (status != HTTP_OK) {
2299 set_http_error (status);
2300 debugprintf ("<- Connection_getFile() (error)\n");
2301 return NULL;
2302 }
2303
2304 debugprintf ("<- Connection_getFile() = None\n");
2305 Py_RETURN_NONE;
2306 }
2307
2308 static PyObject *
Connection_putFile(Connection * self,PyObject * args,PyObject * kwds)2309 Connection_putFile (Connection *self, PyObject *args, PyObject *kwds)
2310 {
2311 static char *kwlist[] = { "resource", "filename", "fd", "file", NULL };
2312 const char *resource, *filename = NULL;
2313 int fd = -1;
2314 PyObject *fileobj = NULL;
2315 http_status_t status;
2316
2317 if (!PyArg_ParseTupleAndKeywords (args, kwds, "s|siO", kwlist,
2318 &resource, &filename, &fd, &fileobj))
2319 return NULL;
2320
2321 if ((fd > -1 && (filename || fileobj)) ||
2322 (filename && fileobj)) {
2323 PyErr_SetString (PyExc_RuntimeError,
2324 "Only one destination type may be specified");
2325 return NULL;
2326 }
2327
2328 if (fileobj) {
2329 fd = PyObject_AsFileDescriptor(fileobj);
2330 }
2331
2332 if (filename) {
2333 debugprintf ("-> Connection_putFile(%s, %s)\n", resource, filename);
2334 debugprintf ("cupsPutFile()\n");
2335 Connection_begin_allow_threads (self);
2336 status = cupsPutFile (self->http, resource, filename);
2337 Connection_end_allow_threads (self);
2338 } else {
2339 debugprintf ("-> Connection_putFile(%s, %d)\n", resource, fd);
2340 debugprintf ("cupsPutFd()\n");
2341 Connection_begin_allow_threads (self);
2342 status = cupsPutFd (self->http, resource, fd);
2343 Connection_end_allow_threads (self);
2344 }
2345
2346 if (status != HTTP_OK && status != HTTP_CREATED) {
2347 set_http_error (status);
2348 debugprintf ("<- Connection_putFile() (error)\n");
2349 return NULL;
2350 }
2351
2352 debugprintf ("<- Connection_putFile() = None\n");
2353 Py_RETURN_NONE;
2354 }
2355
2356 static ipp_t *
add_modify_printer_request(const char * name)2357 add_modify_printer_request (const char *name)
2358 {
2359 char uri[HTTP_MAX_URI];
2360 ipp_t *request = ippNewRequest (CUPS_ADD_MODIFY_PRINTER);
2361 construct_uri (uri, sizeof (uri), "ipp://localhost/printers/", name);
2362 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
2363 "printer-uri", NULL, uri);
2364 return request;
2365 }
2366
2367 static ipp_t *
add_modify_class_request(const char * name)2368 add_modify_class_request (const char *name)
2369 {
2370 char uri[HTTP_MAX_URI];
2371 ipp_t *request = ippNewRequest (CUPS_ADD_MODIFY_CLASS);
2372 construct_uri (uri, sizeof (uri), "ipp://localhost/classes/", name);
2373 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
2374 "printer-uri", NULL, uri);
2375 return request;
2376 }
2377
2378 static PyObject *
Connection_addPrinter(Connection * self,PyObject * args,PyObject * kwds)2379 Connection_addPrinter (Connection *self, PyObject *args, PyObject *kwds)
2380 {
2381 PyObject *nameobj = NULL;
2382 char *name = NULL;
2383 PyObject *ppdfileobj = NULL;
2384 char *ppdfile = NULL;
2385 PyObject *ppdnameobj = NULL;
2386 char *ppdname = NULL;
2387 PyObject *infoobj = NULL;
2388 char *info = NULL;
2389 PyObject *locationobj = NULL;
2390 char *location = NULL;
2391 PyObject *deviceobj = NULL;
2392 char *device = NULL;
2393 PyObject *ppd = NULL;
2394 ipp_t *request, *answer;
2395 int ppds_specified = 0;
2396 static char *kwlist[] = { "name", "filename", "ppdname", "info",
2397 "location", "device", "ppd", NULL };
2398
2399 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|OOOOOO", kwlist,
2400 &nameobj, &ppdfileobj, &ppdnameobj,
2401 &infoobj, &locationobj, &deviceobj, &ppd))
2402 return NULL;
2403
2404 if (UTF8_from_PyObj (&name, nameobj) == NULL ||
2405 (ppdfileobj && UTF8_from_PyObj (&ppdfile, ppdfileobj) == NULL) ||
2406 (ppdnameobj && UTF8_from_PyObj (&ppdname, ppdnameobj) == NULL) ||
2407 (infoobj && UTF8_from_PyObj (&info, infoobj) == NULL) ||
2408 (locationobj && UTF8_from_PyObj (&location, locationobj) == NULL) ||
2409 (deviceobj && UTF8_from_PyObj (&device, deviceobj) == NULL)) {
2410 free (name);
2411 free (ppdfile);
2412 free (ppdname);
2413 free (info);
2414 free (location);
2415 free (device);
2416 return NULL;
2417 }
2418
2419 debugprintf ("-> Connection_addPrinter(%s,%s,%s,%s,%s,%s,%s)\n",
2420 name, ppdfile ? ppdfile: "", ppdname ? ppdname: "",
2421 info ? info: "", location ? location: "",
2422 device ? device: "", ppd ? "(PPD object)": "");
2423
2424 if (ppdfile)
2425 ppds_specified++;
2426 if (ppdname)
2427 ppds_specified++;
2428 if (ppd) {
2429 if (!PyObject_TypeCheck (ppd, &cups_PPDType)) {
2430 PyErr_SetString (PyExc_TypeError, "Expecting cups.PPD");
2431 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2432 free (name);
2433 free (ppdfile);
2434 free (ppdname);
2435 free (info);
2436 free (location);
2437 free (device);
2438 return NULL;
2439 }
2440
2441 ppds_specified++;
2442 }
2443 if (ppds_specified > 1) {
2444 PyErr_SetString (PyExc_RuntimeError,
2445 "Only one PPD may be given");
2446 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2447 free (name);
2448 free (ppdfile);
2449 free (ppdname);
2450 free (info);
2451 free (location);
2452 free (device);
2453 return NULL;
2454 }
2455
2456 if (ppd) {
2457 // We've been given a cups.PPD object. Construct a PPD file.
2458 char template[PATH_MAX];
2459 int fd;
2460 PyObject *args, *result;
2461
2462 snprintf(template, sizeof (template), "%s/scp-ppd-XXXXXX", _PATH_TMP);
2463 ppdfile = strdup(template);
2464 fd = mkstemp (ppdfile);
2465 if (fd < 0) {
2466 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2467 free (name);
2468 free (ppdfile);
2469 free (ppdname);
2470 free (info);
2471 free (location);
2472 free (device);
2473 return PyErr_SetFromErrno (PyExc_RuntimeError);
2474 }
2475
2476 args = Py_BuildValue ("(i)", fd);
2477 result = PPD_writeFd ((PPD *) ppd, args);
2478 Py_DECREF (args);
2479 close (fd);
2480
2481 if (result == NULL) {
2482 unlink (ppdfile);
2483 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2484 free (name);
2485 free (ppdfile);
2486 free (ppdname);
2487 free (info);
2488 free (location);
2489 free (device);
2490 return NULL;
2491 }
2492 }
2493
2494 request = add_modify_printer_request (name);
2495 free (name);
2496 if (ppdname) {
2497 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2498 "ppd-name", NULL, ppdname);
2499 free (ppdname);
2500 }
2501 if (info) {
2502 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2503 "printer-info", NULL, info);
2504 free (info);
2505 }
2506 if (location) {
2507 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2508 "printer-location", NULL, location);
2509 free (location);
2510 }
2511 if (device) {
2512 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_URI,
2513 "device-uri", NULL, device);
2514 free (device);
2515 }
2516 if (ppds_specified)
2517 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2518 "printer-state-reasons", NULL, "none");
2519
2520 Connection_begin_allow_threads (self);
2521 if (ppdfile) {
2522 answer = cupsDoFileRequest (self->http, request, "/admin/", ppdfile);
2523 } else
2524 answer = cupsDoRequest (self->http, request, "/admin/");
2525 Connection_end_allow_threads (self);
2526
2527 if (ppd) {
2528 unlink (ppdfile);
2529 free (ppdfile);
2530 } else if (ppdfile)
2531 free (ppdfile);
2532
2533 if (PyErr_Occurred ()) {
2534 if (answer)
2535 ippDelete (answer);
2536 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2537 return NULL;
2538 }
2539
2540 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2541 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2542 answer ? NULL : cupsLastErrorString ());
2543 if (answer)
2544 ippDelete (answer);
2545
2546 debugprintf ("<- Connection_addPrinter() EXCEPTION\n");
2547 return NULL;
2548 }
2549
2550 ippDelete (answer);
2551 debugprintf ("<- Connection_addPrinter() = None\n");
2552 Py_RETURN_NONE;
2553 }
2554
2555 static PyObject *
Connection_setPrinterDevice(Connection * self,PyObject * args)2556 Connection_setPrinterDevice (Connection *self, PyObject *args)
2557 {
2558 PyObject *nameobj;
2559 PyObject *device_uriobj;
2560 char *name;
2561 char *device_uri;
2562 ipp_t *request, *answer;
2563
2564 if (!PyArg_ParseTuple (args, "OO", &nameobj, &device_uriobj))
2565 return NULL;
2566
2567 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2568 return NULL;
2569
2570 if (UTF8_from_PyObj (&device_uri, device_uriobj) == NULL) {
2571 free (name);
2572 return NULL;
2573 }
2574
2575 request = add_modify_printer_request (name);
2576 free (name);
2577 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_URI,
2578 "device-uri", NULL, device_uri);
2579 free (device_uri);
2580 Connection_begin_allow_threads (self);
2581 answer = cupsDoRequest (self->http, request, "/admin/");
2582 Connection_end_allow_threads (self);
2583 if (PyErr_Occurred ()) {
2584 if (answer)
2585 ippDelete (answer);
2586 return NULL;
2587 }
2588
2589 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2590 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2591 answer ? NULL : cupsLastErrorString ());
2592 if (answer)
2593 ippDelete (answer);
2594 return NULL;
2595 }
2596
2597 ippDelete (answer);
2598 Py_RETURN_NONE;
2599 }
2600
2601 static PyObject *
Connection_setPrinterInfo(Connection * self,PyObject * args)2602 Connection_setPrinterInfo (Connection *self, PyObject *args)
2603 {
2604 PyObject *nameobj;
2605 PyObject *infoobj;
2606 char *name;
2607 char *info;
2608 ipp_t *request, *answer;
2609 int i;
2610
2611 if (!PyArg_ParseTuple (args, "OO", &nameobj, &infoobj))
2612 return NULL;
2613
2614 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2615 return NULL;
2616
2617 if (UTF8_from_PyObj (&info, infoobj) == NULL) {
2618 free (name);
2619 return NULL;
2620 }
2621
2622 request = add_modify_printer_request (name);
2623 for (i = 0; i < 2; i++) {
2624 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2625 "printer-info", NULL, info);
2626 Connection_begin_allow_threads (self);
2627 answer = cupsDoRequest (self->http, request, "/admin/");
2628 Connection_end_allow_threads (self);
2629 if (PyErr_Occurred ()) {
2630 if (answer)
2631 ippDelete (answer);
2632 return NULL;
2633 }
2634
2635 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2636 // Perhaps it's a class, not a printer.
2637 ippDelete (answer);
2638 request = add_modify_class_request (name);
2639 } else break;
2640 }
2641
2642 free (name);
2643 free (info);
2644 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2645 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2646 answer ? NULL : cupsLastErrorString ());
2647 if (answer)
2648 ippDelete (answer);
2649 return NULL;
2650 }
2651
2652 ippDelete (answer);
2653 Py_RETURN_NONE;
2654 }
2655
2656 static PyObject *
Connection_setPrinterLocation(Connection * self,PyObject * args)2657 Connection_setPrinterLocation (Connection *self, PyObject *args)
2658 {
2659 PyObject *nameobj;
2660 PyObject *locationobj;
2661 char *name;
2662 char *location;
2663 ipp_t *request, *answer;
2664 int i;
2665
2666 if (!PyArg_ParseTuple (args, "OO", &nameobj, &locationobj))
2667 return NULL;
2668
2669 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2670 return NULL;
2671
2672 if (UTF8_from_PyObj (&location, locationobj) == NULL) {
2673 free (name);
2674 return NULL;
2675 }
2676
2677 request = add_modify_printer_request (name);
2678 for (i = 0; i < 2; i++) {
2679 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2680 "printer-location", NULL, location);
2681 Connection_begin_allow_threads (self);
2682 answer = cupsDoRequest (self->http, request, "/admin/");
2683 Connection_end_allow_threads (self);
2684 if (PyErr_Occurred ()) {
2685 if (answer)
2686 ippDelete (answer);
2687 return NULL;
2688 }
2689
2690 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2691 // Perhaps it's a class, not a printer.
2692 ippDelete (answer);
2693 request = add_modify_class_request (name);
2694 } else break;
2695 }
2696
2697 free (name);
2698 free (location);
2699 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2700 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2701 answer ? NULL : cupsLastErrorString ());
2702 if (answer)
2703 ippDelete (answer);
2704 return NULL;
2705 }
2706
2707 ippDelete (answer);
2708 Py_RETURN_NONE;
2709 }
2710
2711 static PyObject *
Connection_setPrinterShared(Connection * self,PyObject * args)2712 Connection_setPrinterShared (Connection *self, PyObject *args)
2713 {
2714 PyObject *nameobj;
2715 char *name;
2716 int sharing;
2717 ipp_t *request, *answer;
2718 int i;
2719
2720 if (!PyArg_ParseTuple (args, "Oi", &nameobj, &sharing))
2721 return NULL;
2722
2723 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2724 return NULL;
2725
2726 request = add_modify_printer_request (name);
2727 for (i = 0; i < 2; i++) {
2728 ippAddBoolean (request, IPP_TAG_OPERATION, "printer-is-shared", sharing);
2729 Connection_begin_allow_threads (self);
2730 answer = cupsDoRequest (self->http, request, "/admin/");
2731 Connection_end_allow_threads (self);
2732 if (PyErr_Occurred ()) {
2733 if (answer)
2734 ippDelete (answer);
2735 return NULL;
2736 }
2737
2738 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2739 // Perhaps it's a class, not a printer.
2740 ippDelete (answer);
2741 request = add_modify_class_request (name);
2742 } else break;
2743 }
2744
2745 free (name);
2746 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2747 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2748 answer ? NULL : cupsLastErrorString ());
2749 if (answer)
2750 ippDelete (answer);
2751 return NULL;
2752 }
2753
2754 ippDelete (answer);
2755 Py_RETURN_NONE;
2756 }
2757
2758 static PyObject *
Connection_setPrinterJobSheets(Connection * self,PyObject * args)2759 Connection_setPrinterJobSheets (Connection *self, PyObject *args)
2760 {
2761 PyObject *nameobj;
2762 PyObject *startobj;
2763 PyObject *endobj;
2764 char *name;
2765 char *start;
2766 char *end;
2767 ipp_t *request, *answer;
2768 ipp_attribute_t *a;
2769 int i;
2770
2771 if (!PyArg_ParseTuple (args, "OOO", &nameobj, &startobj, &endobj))
2772 return NULL;
2773
2774 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2775 return NULL;
2776
2777 if (UTF8_from_PyObj (&start, startobj) == NULL) {
2778 free (name);
2779 return NULL;
2780 }
2781
2782 if (UTF8_from_PyObj (&end, endobj) == NULL) {
2783 free (name);
2784 free (start);
2785 return NULL;
2786 }
2787
2788 request = add_modify_printer_request (name);
2789 for (i = 0; i < 2; i++) {
2790 a = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2791 "job-sheets-default", 2, NULL, NULL);
2792 ippSetString(request, &a, 0, strdup (start));
2793 ippSetString(request, &a, 1, strdup (end));
2794 Connection_begin_allow_threads (self);
2795 answer = cupsDoRequest (self->http, request, "/admin/");
2796 Connection_end_allow_threads (self);
2797 if (PyErr_Occurred ()) {
2798 if (answer)
2799 ippDelete (answer);
2800 return NULL;
2801 }
2802
2803 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2804 ippDelete (answer);
2805 // Perhaps it's a class, not a printer.
2806 request = add_modify_class_request (name);
2807 } else break;
2808 }
2809
2810 free (name);
2811 free (start);
2812 free (end);
2813 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2814 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2815 answer ? NULL : cupsLastErrorString ());
2816 if (answer)
2817 ippDelete (answer);
2818 return NULL;
2819 }
2820
2821 ippDelete (answer);
2822 Py_RETURN_NONE;
2823 }
2824
2825 static PyObject *
Connection_setPrinterErrorPolicy(Connection * self,PyObject * args)2826 Connection_setPrinterErrorPolicy (Connection *self, PyObject *args)
2827 {
2828 PyObject *nameobj;
2829 char *name;
2830 PyObject *policyobj;
2831 char *policy;
2832 ipp_t *request, *answer;
2833 int i;
2834
2835 if (!PyArg_ParseTuple (args, "OO", &nameobj, &policyobj))
2836 return NULL;
2837
2838 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2839 return NULL;
2840
2841 if (UTF8_from_PyObj (&policy, policyobj) == NULL) {
2842 free (name);
2843 return NULL;
2844 }
2845
2846 request = add_modify_printer_request (name);
2847 for (i = 0; i < 2; i++) {
2848 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2849 "printer-error-policy", NULL, policy);
2850 Connection_begin_allow_threads (self);
2851 answer = cupsDoRequest (self->http, request, "/admin/");
2852 Connection_end_allow_threads (self);
2853 if (PyErr_Occurred ()) {
2854 if (answer)
2855 ippDelete (answer);
2856 return NULL;
2857 }
2858
2859 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2860 ippDelete (answer);
2861 // Perhaps it's a class, not a printer.
2862 request = add_modify_class_request (name);
2863 } else break;
2864 }
2865
2866 free (name);
2867 free (policy);
2868 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2869 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2870 answer ? NULL : cupsLastErrorString ());
2871 if (answer)
2872 ippDelete (answer);
2873 return NULL;
2874 }
2875
2876 ippDelete (answer);
2877 Py_RETURN_NONE;
2878 }
2879
2880 static PyObject *
Connection_setPrinterOpPolicy(Connection * self,PyObject * args)2881 Connection_setPrinterOpPolicy (Connection *self, PyObject *args)
2882 {
2883 PyObject *nameobj;
2884 char *name;
2885 PyObject *policyobj;
2886 char *policy;
2887 ipp_t *request, *answer;
2888 int i;
2889
2890 if (!PyArg_ParseTuple (args, "OO", &nameobj, &policyobj))
2891 return NULL;
2892
2893 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2894 return NULL;
2895
2896 if (UTF8_from_PyObj (&policy, policyobj) == NULL) {
2897 free (name);
2898 return NULL;
2899 }
2900
2901 request = add_modify_printer_request (name);
2902 for (i = 0; i < 2; i++) {
2903 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2904 "printer-op-policy", NULL, policy);
2905 Connection_begin_allow_threads (self);
2906 answer = cupsDoRequest (self->http, request, "/admin/");
2907 Connection_end_allow_threads (self);
2908 if (PyErr_Occurred ()) {
2909 if (answer)
2910 ippDelete (answer);
2911 return NULL;
2912 }
2913
2914 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2915 ippDelete (answer);
2916 // Perhaps it's a class, not a printer.
2917 request = add_modify_class_request (name);
2918 } else break;
2919 }
2920
2921 free (name);
2922 free (policy);
2923 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
2924 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
2925 answer ? NULL : cupsLastErrorString ());
2926 if (answer)
2927 ippDelete (answer);
2928 return NULL;
2929 }
2930
2931 ippDelete (answer);
2932 Py_RETURN_NONE;
2933 }
2934
2935 static PyObject *
do_requesting_user_names(Connection * self,PyObject * args,const char * requeststr)2936 do_requesting_user_names (Connection *self, PyObject *args,
2937 const char *requeststr)
2938 {
2939 PyObject *nameobj;
2940 char *name;
2941 char *tmp;
2942 PyObject *users;
2943 int num_users, i, j;
2944 ipp_t *request, *answer;
2945 ipp_attribute_t *attr;
2946
2947 if (!PyArg_ParseTuple (args, "OO", &nameobj, &users))
2948 return NULL;
2949
2950 if (UTF8_from_PyObj (&name, nameobj) == NULL)
2951 return NULL;
2952
2953 if (!PyList_Check (users)) {
2954 PyErr_SetString (PyExc_TypeError, "List required");
2955 return NULL;
2956 }
2957 request = add_modify_printer_request (name);
2958 for (i = 0; i < 2; i++) {
2959 num_users = PyList_Size (users);
2960 if (num_users) {
2961 attr = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2962 requeststr, num_users, NULL, NULL);
2963 for (j = 0; j < num_users; j++) {
2964 PyObject *username = PyList_GetItem (users, j); // borrowed ref
2965 if (!PyUnicode_Check (username) && !PyBytes_Check (username)) {
2966 int k;
2967 PyErr_SetString (PyExc_TypeError, "String required");
2968 for (k = 0; k < j; k++) {
2969 free ((void *)ippGetString (attr, k, NULL));
2970 ippSetString(request, &attr, k, NULL);
2971 }
2972 ippDelete (request);
2973 return NULL;
2974 }
2975 ippSetString(request, &attr, j, UTF8_from_PyObj (&tmp, username));
2976 free(tmp);
2977 }
2978 } else {
2979 attr = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2980 requeststr, 1, NULL, NULL);
2981 if (strstr (requeststr, "denied"))
2982 ippSetString(request, &attr, 0, strdup ("none"));
2983 else
2984 ippSetString(request, &attr, 0, strdup ("all"));
2985 }
2986 Connection_begin_allow_threads (self);
2987 answer = cupsDoRequest (self->http, request, "/admin/");
2988 Connection_end_allow_threads (self);
2989 if (PyErr_Occurred ()) {
2990 if (answer)
2991 ippDelete (answer);
2992 return NULL;
2993 }
2994
2995 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
2996 ippDelete (answer);
2997 // Perhaps it's a class, not a printer.
2998 request = add_modify_class_request (name);
2999 } else break;
3000 }
3001
3002 free (name);
3003 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3004 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3005 answer ? NULL : cupsLastErrorString ());
3006 if (answer)
3007 ippDelete (answer);
3008 return NULL;
3009 }
3010
3011 ippDelete (answer);
3012 Py_RETURN_NONE;
3013 }
3014
3015 static PyObject *
Connection_setPrinterUsersAllowed(Connection * self,PyObject * args)3016 Connection_setPrinterUsersAllowed (Connection *self, PyObject *args)
3017 {
3018 return do_requesting_user_names (self, args, "requesting-user-name-allowed");
3019 }
3020
3021 static PyObject *
Connection_setPrinterUsersDenied(Connection * self,PyObject * args)3022 Connection_setPrinterUsersDenied (Connection *self, PyObject *args)
3023 {
3024 return do_requesting_user_names (self, args, "requesting-user-name-denied");
3025 }
3026
3027 static char *
PyObject_to_string(PyObject * pyvalue)3028 PyObject_to_string (PyObject *pyvalue)
3029 {
3030 char string[BUFSIZ];
3031 char *value = "{unknown type}";
3032
3033 if (PyUnicode_Check (pyvalue) || PyBytes_Check (pyvalue)) {
3034 UTF8_from_PyObj (&value, pyvalue);
3035 } else if (PyBool_Check (pyvalue)) {
3036 value = (pyvalue == Py_True) ? "true" : "false";
3037 } else if (PyLong_Check (pyvalue)) {
3038 long v = PyLong_AsLong (pyvalue);
3039 snprintf (string, sizeof (string), "%ld", v);
3040 value = string;
3041 } else if (PyFloat_Check (pyvalue)) {
3042 double v = PyFloat_AsDouble (pyvalue);
3043 snprintf (string, sizeof (string), "%f", v);
3044 value = string;
3045 }
3046
3047 return strdup (value);
3048 }
3049
3050 static PyObject *
Connection_addPrinterOptionDefault(Connection * self,PyObject * args)3051 Connection_addPrinterOptionDefault (Connection *self, PyObject *args)
3052 {
3053 PyObject *nameobj;
3054 char *name;
3055 PyObject *optionobj;
3056 char *option;
3057 PyObject *pyvalue;
3058 const char suffix[] = "-default";
3059 char *opt;
3060 ipp_t *request, *answer;
3061 int i;
3062 size_t optionlen;
3063
3064 if (!PyArg_ParseTuple (args, "OOO", &nameobj, &optionobj, &pyvalue))
3065 return NULL;
3066
3067 if (UTF8_from_PyObj (&name, nameobj) == NULL)
3068 return NULL;
3069
3070 if (UTF8_from_PyObj (&option, optionobj) == NULL) {
3071 free (name);
3072 return NULL;
3073 }
3074
3075 optionlen = strlen (option);
3076 opt = malloc (optionlen + sizeof (suffix) + 1);
3077 memcpy (opt, option, optionlen);
3078 strcpy (opt + optionlen, suffix);
3079 request = add_modify_printer_request (name);
3080 for (i = 0; i < 2; i++) {
3081 if (!PyUnicode_Check (pyvalue) && !PyBytes_Check (pyvalue) &&
3082 PySequence_Check (pyvalue)) {
3083 ipp_attribute_t *attr;
3084 int len = PySequence_Size (pyvalue);
3085 int j;
3086 attr = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3087 opt, len, NULL, NULL);
3088 for (j = 0; j < len; j++) {
3089 PyObject *each = PySequence_GetItem (pyvalue, j);
3090 ippSetString(request, &attr, j, PyObject_to_string (each));
3091 }
3092 } else
3093 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3094 opt, NULL, PyObject_to_string (pyvalue));
3095
3096 Connection_begin_allow_threads (self);
3097 answer = cupsDoRequest (self->http, request, "/admin/");
3098 Connection_end_allow_threads (self);
3099 if (PyErr_Occurred ()) {
3100 if (answer)
3101 ippDelete (answer);
3102 return NULL;
3103 }
3104
3105 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
3106 ippDelete (answer);
3107 // Perhaps it's a class, not a printer.
3108 request = add_modify_class_request (name);
3109 } else break;
3110 }
3111
3112 free (name);
3113 free (option);
3114 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3115 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3116 answer ? NULL : cupsLastErrorString ());
3117 if (answer)
3118 ippDelete (answer);
3119 return NULL;
3120 }
3121
3122 ippDelete (answer);
3123 Py_RETURN_NONE;
3124 }
3125
3126 static PyObject *
Connection_deletePrinterOptionDefault(Connection * self,PyObject * args)3127 Connection_deletePrinterOptionDefault (Connection *self, PyObject *args)
3128 {
3129 PyObject *nameobj;
3130 char *name;
3131 PyObject *optionobj;
3132 char *option;
3133 const char suffix[] = "-default";
3134 char *opt;
3135 ipp_t *request, *answer;
3136 int i;
3137 size_t optionlen;
3138
3139 if (!PyArg_ParseTuple (args, "OO", &nameobj, &optionobj))
3140 return NULL;
3141
3142 if (UTF8_from_PyObj (&name, nameobj) == NULL)
3143 return NULL;
3144
3145 if (UTF8_from_PyObj (&option, optionobj) == NULL) {
3146 free (name);
3147 return NULL;
3148 }
3149
3150 optionlen = strlen (option);
3151 opt = malloc (optionlen + sizeof (suffix) + 1);
3152 memcpy (opt, option, optionlen);
3153 strcpy (opt + optionlen, suffix);
3154 request = add_modify_printer_request (name);
3155 for (i = 0; i < 2; i++) {
3156 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR,
3157 opt, NULL, NULL);
3158 Connection_begin_allow_threads (self);
3159 answer = cupsDoRequest (self->http, request, "/admin/");
3160 Connection_end_allow_threads (self);
3161 if (PyErr_Occurred ()) {
3162 if (answer)
3163 ippDelete (answer);
3164 return NULL;
3165 }
3166
3167 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
3168 ippDelete (answer);
3169 // Perhaps it's a class, not a printer.
3170 request = add_modify_class_request (name);
3171 } else break;
3172 }
3173
3174 free (name);
3175 free (option);
3176 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3177 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3178 answer ? NULL : cupsLastErrorString ());
3179 if (answer)
3180 ippDelete (answer);
3181 return NULL;
3182 }
3183
3184 ippDelete (answer);
3185 Py_RETURN_NONE;
3186 }
3187
3188 static PyObject *
Connection_deletePrinter(Connection * self,PyObject * args,PyObject * kwds)3189 Connection_deletePrinter (Connection *self, PyObject *args, PyObject *kwds)
3190 {
3191 return do_printer_request (self, args, kwds, CUPS_DELETE_PRINTER);
3192 }
3193
3194 static PyObject *
Connection_getPrinterAttributes(Connection * self,PyObject * args,PyObject * kwds)3195 Connection_getPrinterAttributes (Connection *self, PyObject *args,
3196 PyObject *kwds)
3197 {
3198 PyObject *ret;
3199 PyObject *nameobj = NULL;
3200 char *name;
3201 PyObject *uriobj = NULL;
3202 char *uri;
3203 PyObject *requested_attrs = NULL;
3204 char **attrs = NULL; /* initialised to calm compiler */
3205 size_t n_attrs = 0; /* initialised to calm compiler */
3206 ipp_t *request, *answer;
3207 ipp_attribute_t *attr;
3208 char consuri[HTTP_MAX_URI];
3209 int i;
3210 static char *kwlist[] = { "name", "uri", "requested_attributes", NULL };
3211
3212 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|OOO", kwlist,
3213 &nameobj, &uriobj, &requested_attrs))
3214 return NULL;
3215
3216 if (nameobj && uriobj) {
3217 PyErr_SetString (PyExc_RuntimeError,
3218 "name or uri must be specified but not both");
3219 return NULL;
3220 }
3221
3222 if (nameobj) {
3223 if (UTF8_from_PyObj (&name, nameobj) == NULL)
3224 return NULL;
3225 } else if (uriobj) {
3226 if (UTF8_from_PyObj (&uri, uriobj) == NULL)
3227 return NULL;
3228 } else {
3229 PyErr_SetString (PyExc_RuntimeError,
3230 "name or uri must be specified");
3231 return NULL;
3232 }
3233
3234 if (requested_attrs) {
3235 if (get_requested_attrs (requested_attrs, &n_attrs, &attrs) == -1) {
3236 if (nameobj)
3237 free (name);
3238 else if (uriobj)
3239 free (uri);
3240 return NULL;
3241 }
3242 }
3243
3244 debugprintf ("-> Connection_getPrinterAttributes(%s)\n",
3245 nameobj ? name : uri);
3246
3247 if (nameobj) {
3248 construct_uri (consuri, sizeof (consuri),
3249 "ipp://localhost/printers/", name);
3250 uri = consuri;
3251 }
3252
3253 for (i = 0; i < 2; i++) {
3254 request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
3255 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3256 "printer-uri", NULL, uri);
3257 if (requested_attrs)
3258 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3259 "requested-attributes", n_attrs, NULL,
3260 (const char **) attrs);
3261 debugprintf ("trying request with uri %s\n", uri);
3262 Connection_begin_allow_threads (self);
3263 answer = cupsDoRequest (self->http, request, "/");
3264 Connection_end_allow_threads (self);
3265 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
3266 ippDelete (answer);
3267 if (uriobj)
3268 break;
3269
3270 // Perhaps it's a class, not a printer.
3271 construct_uri (consuri, sizeof (consuri),
3272 "ipp://localhost/classes/", name);
3273 } else break;
3274 }
3275
3276 if (nameobj)
3277 free (name);
3278
3279 if (uriobj)
3280 free (uri);
3281
3282 if (requested_attrs)
3283 free_requested_attrs (n_attrs, attrs);
3284
3285 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3286 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3287 answer ? NULL : cupsLastErrorString ());
3288 if (answer)
3289 ippDelete (answer);
3290
3291 debugprintf ("<- Connection_getPrinterAttributes() (error)\n");
3292 return NULL;
3293 }
3294
3295 ret = PyDict_New ();
3296 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
3297 while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
3298 attr = ippNextAttribute (answer);
3299
3300 if (!attr)
3301 break;
3302
3303 for (; attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER;
3304 attr = ippNextAttribute (answer)) {
3305 size_t namelen = strlen (ippGetName (attr));
3306 int is_list = ippGetCount (attr) > 1;
3307
3308 debugprintf ("Attribute: %s\n", ippGetName (attr));
3309 // job-sheets-default is special, since it is always two values.
3310 // Make it a tuple.
3311 if (!strcmp (ippGetName (attr), "job-sheets-default") &&
3312 ippGetValueTag (attr) == IPP_TAG_NAME) {
3313 PyObject *startobj, *endobj, *tuple;
3314 const char *start, *end;
3315 start = ippGetString (attr, 0, NULL);
3316 if (ippGetCount (attr) >= 2)
3317 end = ippGetString (attr, 1, NULL);
3318 else
3319 end = "";
3320
3321 startobj = PyObj_from_UTF8 (start);
3322 endobj = PyObj_from_UTF8 (end);
3323 tuple = Py_BuildValue ("(OO)", startobj, endobj);
3324 Py_DECREF (startobj);
3325 Py_DECREF (endobj);
3326 PyDict_SetItemString (ret, "job-sheets-default", tuple);
3327 Py_DECREF (tuple);
3328 continue;
3329 }
3330
3331 // Check for '-supported' suffix. Any xxx-supported attribute
3332 // that is a text type must be a list.
3333 //
3334 // Also check for attributes that are known to allow multiple
3335 // string values, and make them lists.
3336 if (!is_list && namelen > 10) {
3337 const char *multivalue_options[] =
3338 {
3339 "notify-events-default",
3340 "requesting-user-name-allowed",
3341 "requesting-user-name-denied",
3342 "printer-state-reasons",
3343 "marker-colors",
3344 "marker-names",
3345 "marker-types",
3346 "marker-levels",
3347 "member-names",
3348 NULL
3349 };
3350
3351 switch (ippGetValueTag (attr)) {
3352 case IPP_TAG_NAME:
3353 case IPP_TAG_TEXT:
3354 case IPP_TAG_KEYWORD:
3355 case IPP_TAG_URI:
3356 case IPP_TAG_CHARSET:
3357 case IPP_TAG_MIMETYPE:
3358 case IPP_TAG_LANGUAGE:
3359 case IPP_TAG_ENUM:
3360 case IPP_TAG_INTEGER:
3361 case IPP_TAG_RESOLUTION:
3362 is_list = !strcmp (ippGetName (attr) + namelen - 10, "-supported");
3363
3364 if (!is_list) {
3365 const char **opt;
3366 for (opt = multivalue_options; !is_list && *opt; opt++)
3367 is_list = !strcmp (ippGetName (attr), *opt);
3368 }
3369
3370 default:
3371 break;
3372 }
3373 }
3374
3375 if (is_list) {
3376 PyObject *list = PyList_from_attr_values (attr);
3377 PyDict_SetItemString (ret, ippGetName (attr), list);
3378 Py_DECREF (list);
3379 } else {
3380 PyObject *val = PyObject_from_attr_value (attr, i);
3381 PyDict_SetItemString (ret, ippGetName (attr), val);
3382 }
3383 }
3384
3385 if (!attr)
3386 break;
3387 }
3388
3389 debugprintf ("<- Connection_getPrinterAttributes() = dict\n");
3390 return ret;
3391 }
3392
3393 static PyObject *
Connection_addPrinterToClass(Connection * self,PyObject * args)3394 Connection_addPrinterToClass (Connection *self, PyObject *args)
3395 {
3396 PyObject *printernameobj;
3397 char *printername;
3398 PyObject *classnameobj;
3399 char *classname;
3400 char classuri[HTTP_MAX_URI];
3401 char printeruri[HTTP_MAX_URI];
3402 ipp_t *request, *answer;
3403
3404 if (!PyArg_ParseTuple (args, "OO", &printernameobj, &classnameobj))
3405 return NULL;
3406
3407 if (UTF8_from_PyObj (&printername, printernameobj) == NULL)
3408 return NULL;
3409
3410 if (UTF8_from_PyObj (&classname, classnameobj) == NULL) {
3411 free (printername);
3412 return NULL;
3413 }
3414
3415 // Does the class exist, and is the printer already in it?
3416 request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
3417 construct_uri (classuri, sizeof (classuri),
3418 "ipp://localhost/classes/", classname);
3419
3420 free (classname);
3421 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3422 "printer-uri", NULL, classuri);
3423 Connection_begin_allow_threads (self);
3424 answer = cupsDoRequest (self->http, request, "/");
3425 Connection_end_allow_threads (self);
3426 if (answer) {
3427 ipp_attribute_t *printers;
3428 printers = ippFindAttribute (answer, "member-names", IPP_TAG_NAME);
3429 if (printers) {
3430 int i;
3431 for (i = 0; i < ippGetCount (printers); i++) {
3432 if (!strcasecmp (ippGetString (printers, i, NULL), printername)) {
3433 ippDelete (answer);
3434 PyErr_SetString (PyExc_RuntimeError, "Printer already in class");
3435 free (printername);
3436 return NULL;
3437 }
3438 }
3439 }
3440 }
3441
3442 request = ippNewRequest (CUPS_ADD_CLASS);
3443 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3444 "printer-uri", NULL, classuri);
3445 construct_uri (printeruri, sizeof (printeruri),
3446 "ipp://localhost/printers/", printername);
3447 free (printername);
3448 if (answer) {
3449 ipp_attribute_t *printers;
3450 printers = ippFindAttribute (answer, "member-uris", IPP_TAG_URI);
3451 if (printers) {
3452 ipp_attribute_t *attr;
3453 int i;
3454 attr = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_URI,
3455 "member-uris", ippGetCount (printers) + 1,
3456 NULL, NULL);
3457 for (i = 0; i < ippGetCount (printers); i++)
3458 ippSetString(request, &attr, i,
3459 strdup (ippGetString (printers, i, NULL)));
3460 ippSetString(request, &attr, ippGetCount (printers), strdup (printeruri));
3461
3462 }
3463
3464 ippDelete (answer);
3465 }
3466
3467 // If the class didn't exist, create a new one.
3468 if (!ippFindAttribute (request, "member-uris", IPP_TAG_URI))
3469 ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_URI,
3470 "member-uris", NULL, printeruri);
3471
3472 Connection_begin_allow_threads (self);
3473 answer = cupsDoRequest (self->http, request, "/admin/");
3474 Connection_end_allow_threads (self);
3475 if (PyErr_Occurred ()) {
3476 if (answer)
3477 ippDelete (answer);
3478 return NULL;
3479 }
3480
3481 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3482 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3483 answer ? NULL : cupsLastErrorString ());
3484 if (answer)
3485 ippDelete (answer);
3486 return NULL;
3487 }
3488
3489 ippDelete (answer);
3490 Py_RETURN_NONE;
3491 }
3492
3493 static PyObject *
Connection_deletePrinterFromClass(Connection * self,PyObject * args)3494 Connection_deletePrinterFromClass (Connection *self, PyObject *args)
3495 {
3496 const char *requested_attrs[] = {
3497 "member-names",
3498 "member-uris"
3499 };
3500 PyObject *printernameobj;
3501 char *printername;
3502 PyObject *classnameobj;
3503 char *classname;
3504 char classuri[HTTP_MAX_URI];
3505 ipp_t *request, *answer;
3506 ipp_attribute_t *printers;
3507 int i;
3508
3509 if (!PyArg_ParseTuple (args, "OO", &printernameobj, &classnameobj))
3510 return NULL;
3511
3512 if (UTF8_from_PyObj (&printername, printernameobj) == NULL)
3513 return NULL;
3514
3515 if (UTF8_from_PyObj (&classname, classnameobj) == NULL) {
3516 free (printername);
3517 return NULL;
3518 }
3519
3520 // Does the class exist, and is the printer in it?
3521 request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
3522 construct_uri (classuri, sizeof (classuri),
3523 "ipp://localhost/classes/", classname);
3524 free (classname);
3525 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3526 "printer-uri", NULL, classuri);
3527 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3528 "requested-attributes",
3529 sizeof(requested_attrs) / sizeof(requested_attrs[0]),
3530 NULL, requested_attrs);
3531 Connection_begin_allow_threads (self);
3532 answer = cupsDoRequest (self->http, request, "/");
3533 Connection_end_allow_threads (self);
3534 if (!answer) {
3535 set_ipp_error (cupsLastError (), cupsLastErrorString ());
3536 free (printername);
3537 return NULL;
3538 }
3539
3540 printers = ippFindAttribute (answer, "member-names", IPP_TAG_NAME);
3541 for (i = 0; printers && i < ippGetCount (printers); i++)
3542 if (!strcasecmp (ippGetString (printers, i, NULL), printername))
3543 break;
3544
3545 free (printername);
3546 if (!printers || i == ippGetCount (printers)) {
3547 ippDelete (answer);
3548 PyErr_SetString (PyExc_RuntimeError, "Printer not in class");
3549 return NULL;
3550 }
3551
3552 printers = ippFindAttribute (answer, "member-uris", IPP_TAG_URI);
3553 if (!printers || i >= ippGetCount (printers)) {
3554 ippDelete (answer);
3555 PyErr_SetString (PyExc_RuntimeError, "No member URIs returned");
3556 return NULL;
3557 }
3558
3559 request = ippNewRequest (CUPS_ADD_CLASS);
3560 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3561 "printer-uri", NULL, classuri);
3562
3563 // Only printer in class? Delete the class.
3564 if (ippGetCount (printers) == 1)
3565 ippSetOperation (request, CUPS_DELETE_CLASS);
3566 else {
3567 // Trim the printer from the list.
3568 ipp_attribute_t *newlist;
3569 int j;
3570 newlist = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_URI,
3571 "member-uris", ippGetCount (printers) - 1,
3572 NULL, NULL);
3573 for (j = 0; j < i; j++)
3574 ippSetString(request, &newlist, j,
3575 strdup (ippGetString (printers, j, NULL)));
3576 for (j = i; j < ippGetCount(newlist); j++)
3577 ippSetString(request, &newlist, j,
3578 strdup (ippGetString (printers, j+1, NULL)));
3579 }
3580
3581 ippDelete (answer);
3582 Connection_begin_allow_threads (self);
3583 answer = cupsDoRequest (self->http, request, "/admin/");
3584 Connection_end_allow_threads (self);
3585 if (PyErr_Occurred ()) {
3586 if (answer)
3587 ippDelete (answer);
3588 return NULL;
3589 }
3590
3591 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3592 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3593 answer ? NULL : cupsLastErrorString ());
3594 if (answer)
3595 ippDelete (answer);
3596 return NULL;
3597 }
3598
3599 ippDelete (answer);
3600 Py_RETURN_NONE;
3601 }
3602
3603 static PyObject *
Connection_deleteClass(Connection * self,PyObject * args)3604 Connection_deleteClass (Connection *self, PyObject *args)
3605 {
3606 PyObject *classnameobj;
3607 char *classname;
3608 char classuri[HTTP_MAX_URI];
3609 ipp_t *request, *answer;
3610
3611 if (!PyArg_ParseTuple (args, "O", &classnameobj))
3612 return NULL;
3613
3614 if (UTF8_from_PyObj (&classname, classnameobj) == NULL)
3615 return NULL;
3616
3617 request = ippNewRequest (CUPS_DELETE_CLASS);
3618 construct_uri (classuri, sizeof (classuri),
3619 "ipp://localhost/classes/", classname);
3620 free (classname);
3621 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3622 "printer-uri", NULL, classuri);
3623 Connection_begin_allow_threads (self);
3624 answer = cupsDoRequest (self->http, request, "/admin/");
3625 Connection_end_allow_threads (self);
3626 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3627 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3628 answer ? NULL : cupsLastErrorString ());
3629 if (answer)
3630 ippDelete (answer);
3631 return NULL;
3632 }
3633
3634 ippDelete (answer);
3635 Py_RETURN_NONE;
3636 }
3637
3638 static PyObject *
Connection_enablePrinter(Connection * self,PyObject * args,PyObject * kwds)3639 Connection_enablePrinter (Connection *self, PyObject *args, PyObject *kwds)
3640 {
3641 return do_printer_request (self, args, kwds, IPP_RESUME_PRINTER);
3642 }
3643
3644 static PyObject *
Connection_disablePrinter(Connection * self,PyObject * args,PyObject * kwds)3645 Connection_disablePrinter (Connection *self, PyObject *args, PyObject *kwds)
3646 {
3647 return do_printer_request (self, args, kwds, IPP_PAUSE_PRINTER);
3648 }
3649
3650 static PyObject *
Connection_acceptJobs(Connection * self,PyObject * args,PyObject * kwds)3651 Connection_acceptJobs (Connection *self, PyObject *args, PyObject *kwds)
3652 {
3653 return do_printer_request (self, args, kwds, CUPS_ACCEPT_JOBS);
3654 }
3655
3656 static PyObject *
Connection_rejectJobs(Connection * self,PyObject * args,PyObject * kwds)3657 Connection_rejectJobs (Connection *self, PyObject *args, PyObject *kwds)
3658 {
3659 return do_printer_request (self, args, kwds, CUPS_REJECT_JOBS);
3660 }
3661
3662 static PyObject *
Connection_getDefault(Connection * self,PyObject * args)3663 Connection_getDefault (Connection *self, PyObject *args)
3664 {
3665 const char *def;
3666 PyObject *ret;
3667 debugprintf ("-> Connection_getDefault()\n");
3668 Connection_begin_allow_threads (self);
3669 def = cupsGetDefault2 (self->http);
3670 Connection_end_allow_threads (self);
3671 if (def == NULL) {
3672 debugprintf ("<- Connection_getDefault() = None\n");
3673 Py_RETURN_NONE;
3674 } else {
3675 debugprintf ("<- Connection_getDefault() = \"%s\"\n", def);
3676 ret = PyUnicode_FromString (def);
3677 }
3678
3679 return ret;
3680 }
3681
3682 static PyObject *
Connection_setDefault(Connection * self,PyObject * args,PyObject * kwds)3683 Connection_setDefault (Connection *self, PyObject *args, PyObject *kwds)
3684 {
3685 return do_printer_request (self, args, kwds, CUPS_SET_DEFAULT);
3686 }
3687
3688 static PyObject *
Connection_getPPD(Connection * self,PyObject * args)3689 Connection_getPPD (Connection *self, PyObject *args)
3690 {
3691 PyObject *ret;
3692 PyObject *printerobj;
3693 char *printer;
3694 const char *ppdfile;
3695
3696 if (!PyArg_ParseTuple (args, "O", &printerobj))
3697 return NULL;
3698
3699 if (UTF8_from_PyObj (&printer, printerobj) == NULL)
3700 return NULL;
3701
3702 debugprintf ("-> Connection_getPPD()\n");
3703 Connection_begin_allow_threads (self);
3704 ppdfile = cupsGetPPD2 (self->http, printer);
3705 Connection_end_allow_threads (self);
3706 free (printer);
3707 if (!ppdfile) {
3708 ipp_status_t err = cupsLastError ();
3709 if (err)
3710 set_ipp_error (err, cupsLastErrorString ());
3711 else
3712 PyErr_SetString (PyExc_RuntimeError, "cupsGetPPD2 failed");
3713
3714 debugprintf ("<- Connection_getPPD() (error)\n");
3715 return NULL;
3716 }
3717
3718 ret = PyUnicode_FromString (ppdfile);
3719 debugprintf ("<- Connection_getPPD() = %s\n", ppdfile);
3720 return ret;
3721 }
3722
3723 static PyObject *
Connection_getPPD3(Connection * self,PyObject * args,PyObject * kwds)3724 Connection_getPPD3 (Connection *self, PyObject *args, PyObject *kwds)
3725 {
3726 PyObject *ret, *obj;
3727 PyObject *printerobj;
3728 PyObject *fmodtime = NULL;
3729 char *printer;
3730 time_t modtime;
3731 PyObject *filenameobj = NULL;
3732 char *filename = NULL;
3733 char fname[PATH_MAX];
3734 http_status_t status;
3735 static char *kwlist[] = { "name", "modtime", "filename", NULL };
3736
3737 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|OO", kwlist,
3738 &printerobj, &fmodtime, &filenameobj))
3739 return NULL;
3740
3741 if (UTF8_from_PyObj (&printer, printerobj) == NULL)
3742 return NULL;
3743
3744 if (fmodtime) {
3745 double d = PyFloat_AsDouble (fmodtime);
3746 if (PyErr_Occurred()) {
3747 free (printer);
3748 return NULL;
3749 }
3750
3751 modtime = (time_t) d;
3752 } else
3753 modtime = 0;
3754
3755 if (filenameobj)
3756 if (UTF8_from_PyObj (&filename, filenameobj) == NULL) {
3757 free (printer);
3758 return NULL;
3759 }
3760 if (filename) {
3761 if (strlen (filename) > sizeof (fname)) {
3762 PyErr_SetString (PyExc_TypeError, "overlength filename");
3763 free (printer);
3764 free (filename);
3765 return NULL;
3766 }
3767 strcpy (fname, filename);
3768 } else
3769 fname[0] = '\0';
3770
3771 debugprintf ("-> Connection_getPPD3()\n");
3772 Connection_begin_allow_threads (self);
3773 status = cupsGetPPD3 (self->http, printer, &modtime,
3774 fname, sizeof (fname));
3775 Connection_end_allow_threads (self);
3776
3777 free (printer);
3778 free (filename);
3779
3780 ret = PyTuple_New (3);
3781 if (!ret)
3782 return NULL;
3783
3784 obj = PyLong_FromLong ((long) status);
3785
3786 if (!obj) {
3787 Py_DECREF (ret);
3788 return NULL;
3789 }
3790
3791 PyTuple_SetItem (ret, 0, obj);
3792
3793 obj = PyFloat_FromDouble ((double) modtime);
3794 if (!obj) {
3795 Py_DECREF (ret);
3796 return NULL;
3797 }
3798
3799 PyTuple_SetItem (ret, 1, obj);
3800
3801 obj = PyUnicode_FromString (fname);
3802 if (!obj) {
3803 Py_DECREF (ret);
3804 return NULL;
3805 }
3806
3807 PyTuple_SetItem (ret, 2, obj);
3808
3809 debugprintf ("<- Connection_getPPD3() = (%d,%ld,%s)\n",
3810 status, modtime, fname);
3811 return ret;
3812 }
3813
3814 static PyObject *
Connection_printTestPage(Connection * self,PyObject * args,PyObject * kwds)3815 Connection_printTestPage (Connection *self, PyObject *args, PyObject *kwds)
3816 {
3817 PyObject *printerobj;
3818 char *printer;
3819 PyObject *fileobj = NULL;
3820 char *file = NULL;
3821 PyObject *titleobj = NULL;
3822 char *title = NULL;
3823 PyObject *formatobj = NULL;
3824 char *format = NULL;
3825 PyObject *userobj = NULL;
3826 char *user = NULL;
3827 const char *datadir;
3828 char filename[PATH_MAX];
3829 char uri[HTTP_MAX_URI];
3830 ipp_t *request, *answer;
3831 ipp_attribute_t *attr;
3832 char *resource;
3833 int jobid = 0;
3834 int i;
3835 static char *kwlist[] = { "name", "file", "title", "format", "user", NULL };
3836
3837 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|OOOO", kwlist,
3838 &printerobj, &fileobj, &titleobj,
3839 &formatobj, &userobj))
3840 return NULL;
3841
3842 if (UTF8_from_PyObj (&printer, printerobj) == NULL)
3843 return NULL;
3844
3845 if ((fileobj && UTF8_from_PyObj (&file, fileobj) == NULL) ||
3846 (titleobj && UTF8_from_PyObj (&title, titleobj) == NULL) ||
3847 (formatobj && UTF8_from_PyObj (&format, formatobj) == NULL) ||
3848 (userobj && UTF8_from_PyObj (&user, userobj) == NULL)) {
3849 free (printer);
3850 free (file);
3851 free (title);
3852 free (format);
3853 free (user);
3854 return NULL;
3855 }
3856
3857 if (!fileobj) {
3858 const char *testprint[] = { "%s/data/testprint",
3859 "%s/data/testprint.ps",
3860 NULL };
3861 if ((datadir = getenv ("CUPS_DATADIR")) != NULL) {
3862 const char **pattern;
3863 for (pattern = testprint; *pattern != NULL; pattern++) {
3864 snprintf (filename, sizeof (filename), *pattern, datadir);
3865 if (access (filename, R_OK) == 0)
3866 break;
3867 }
3868 } else {
3869 const char *const dirs[] = { "/usr/share/cups",
3870 "/usr/local/share/cups",
3871 NULL };
3872 int found = 0;
3873 int i;
3874 for (i = 0; (datadir = dirs[i]) != NULL; i++) {
3875 const char **pattern;
3876 for (pattern = testprint; *pattern != NULL; pattern++) {
3877 snprintf (filename, sizeof (filename), *pattern, datadir);
3878 if (access (filename, R_OK) == 0) {
3879 found = 1;
3880 break;
3881 }
3882 }
3883
3884 if (found)
3885 break;
3886 }
3887
3888 if (datadir == NULL)
3889 /* We haven't found the testprint.ps file, so just pick a path
3890 * and try it. This will certainly fail with
3891 * client-error-not-found, but we'll let that happen rather
3892 * than raising an exception so as to be consistent with the
3893 * case where CUPS_DATADIR is set and we trust it. */
3894 snprintf (filename, sizeof (filename), testprint[0], dirs[0]);
3895 }
3896
3897 file = filename;
3898 }
3899
3900 if (!titleobj)
3901 title = "Test Page";
3902
3903 if (!userobj)
3904 user = (char *) cupsUser();
3905
3906 construct_uri (uri, sizeof (uri),
3907 "ipp://localhost/printers/", printer);
3908 resource = uri + strlen ("ipp://localhost");
3909 for (i = 0; i < 2; i++) {
3910 request = ippNewRequest (IPP_PRINT_JOB);
3911 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3912 NULL, uri);
3913 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3914 "requesting-user-name", NULL, user);
3915 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
3916 NULL, title);
3917 if (format)
3918 ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
3919 NULL, format);
3920
3921 Connection_begin_allow_threads (self);
3922 answer = cupsDoFileRequest (self->http, request, resource, file);
3923 Connection_end_allow_threads (self);
3924 if (answer && ippGetStatusCode (answer) == IPP_NOT_POSSIBLE) {
3925 ippDelete (answer);
3926 // Perhaps it's a class, not a printer.
3927 construct_uri (uri, sizeof (uri),
3928 "ipp://localhost/classes/", printer);
3929 } else break;
3930 }
3931
3932 free (printer);
3933 if (fileobj)
3934 free (file);
3935 if (titleobj)
3936 free (title);
3937 if (formatobj)
3938 free (format);
3939 if (userobj)
3940 free (user);
3941
3942 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
3943 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
3944 answer ? NULL : cupsLastErrorString ());
3945 if (answer)
3946 ippDelete (answer);
3947 return NULL;
3948 }
3949
3950 attr = ippFindAttribute (answer, "job-id", IPP_TAG_INTEGER);
3951 if (attr)
3952 jobid = ippGetInteger (attr, 0);
3953
3954 ippDelete (answer);
3955 return Py_BuildValue ("i", jobid);
3956 }
3957
3958 static PyObject *
Connection_adminExportSamba(Connection * self,PyObject * args)3959 Connection_adminExportSamba (Connection *self, PyObject *args)
3960 {
3961 int ret;
3962 PyObject *nameobj;
3963 char *name;
3964 PyObject *serverobj;
3965 char *server;
3966 PyObject *userobj;
3967 char *user;
3968 PyObject *passwordobj;
3969 char *password;
3970 char ppdfile[1024];
3971 FILE *tmpfile(void);
3972 FILE *tf;
3973 char str[80];
3974
3975 if (!PyArg_ParseTuple (args, "OOOO", &nameobj, &serverobj, &userobj,
3976 &passwordobj))
3977 return NULL;
3978
3979 if (UTF8_from_PyObj (&name, nameobj) == NULL ||
3980 UTF8_from_PyObj (&server, serverobj) == NULL ||
3981 UTF8_from_PyObj (&user, userobj) == NULL ||
3982 UTF8_from_PyObj (&password, passwordobj) == NULL) {
3983 free (name);
3984 free (server);
3985 free (user);
3986 free (password);
3987 PyErr_SetString(PyExc_RuntimeError,
3988 "name, samba_server, samba_username, samba_password "
3989 "must be specified");
3990 return NULL;
3991 }
3992
3993 if (!cupsAdminCreateWindowsPPD(self->http, name, ppdfile, sizeof(ppdfile))) {
3994 PyErr_SetString(PyExc_RuntimeError,
3995 "No PPD file found for the printer");
3996 return NULL;
3997 }
3998
3999 debugprintf ("-> Connection_adminExportSamba()\n");
4000 tf = tmpfile();
4001 Connection_begin_allow_threads (self);
4002 ret = cupsAdminExportSamba(name, ppdfile, server, user, password, tf);
4003 Connection_end_allow_threads (self);
4004
4005 free (name);
4006 free (server);
4007 free (user);
4008 free (password);
4009 unlink (ppdfile);
4010
4011 if (!ret) {
4012 rewind(tf);
4013 // Read logfile line by line to get Exit status message on the last line.
4014 while (fgets (str, sizeof(str), tf) != NULL) { }
4015 fclose (tf);
4016 if (str[strlen(str) -1] == '\n') {
4017 str[strlen(str) -1] = '\0';
4018 }
4019 PyErr_SetString (PyExc_RuntimeError, str);
4020 debugprintf ("<- Connection_adminExportSamba() EXCEPTION\n");
4021 return NULL;
4022 }
4023 fclose (tf);
4024 debugprintf ("<- Connection_adminExportSamba()\n");
4025 Py_RETURN_NONE;
4026 }
4027
4028 static PyObject *
Connection_adminGetServerSettings(Connection * self)4029 Connection_adminGetServerSettings (Connection *self)
4030 {
4031 PyObject *ret = PyDict_New ();
4032 int num_settings, i;
4033 cups_option_t *settings;
4034 debugprintf ("-> Connection_adminGetServerSettings()\n");
4035 Connection_begin_allow_threads (self);
4036 cupsAdminGetServerSettings (self->http, &num_settings, &settings);
4037 Connection_end_allow_threads (self);
4038 for (i = 0; i < num_settings; i++) {
4039 PyObject *string = PyUnicode_FromString (settings[i].value);
4040 PyDict_SetItemString (ret, settings[i].name, string);
4041 Py_DECREF (string);
4042 }
4043
4044 cupsFreeOptions (num_settings, settings);
4045 debugprintf ("<- Connection_adminGetServerSettings()\n");
4046 return ret;
4047 }
4048
4049 static PyObject *
Connection_adminSetServerSettings(Connection * self,PyObject * args)4050 Connection_adminSetServerSettings (Connection *self, PyObject *args)
4051 {
4052 PyObject *dict, *key, *val;
4053 int ret;
4054 int num_settings = 0;
4055 DICT_POS_TYPE pos = 0;
4056 cups_option_t *settings = NULL;
4057 if (!PyArg_ParseTuple (args, "O", &dict))
4058 return NULL;
4059 if (!PyDict_Check (dict)) {
4060 PyErr_SetString (PyExc_TypeError, "Expecting dict");
4061 return NULL;
4062 }
4063
4064 debugprintf ("-> Connection_adminSetServerSettings()\n");
4065 while (PyDict_Next (dict, &pos, &key, &val)) {
4066 char *name, *value;
4067 if ((!PyUnicode_Check (key) && !PyBytes_Check (key)) ||
4068 (!PyUnicode_Check (val) && !PyBytes_Check (val))) {
4069 cupsFreeOptions (num_settings, settings);
4070 PyErr_SetString (PyExc_TypeError, "Keys and values must be strings");
4071 debugprintf ("<- Connection_adminSetServerSettings() EXCEPTION\n");
4072 return NULL;
4073 }
4074
4075 UTF8_from_PyObj (&name, key);
4076 UTF8_from_PyObj (&value, val);
4077 debugprintf ("%s: %s\n", name, value);
4078 num_settings = cupsAddOption (name,
4079 value,
4080 num_settings,
4081 &settings);
4082 free (name);
4083 free (value);
4084 }
4085
4086 debugprintf ("num_settings=%d, settings=%p\n", num_settings, settings);
4087 Connection_begin_allow_threads (self);
4088 ret = cupsAdminSetServerSettings (self->http, num_settings, settings);
4089 Connection_end_allow_threads (self);
4090 if (!ret) {
4091 cupsFreeOptions (num_settings, settings);
4092 PyErr_SetString (PyExc_RuntimeError, "Failed to set settings");
4093 debugprintf ("<- Connection_adminSetServerSettings() EXCEPTION\n");
4094 return NULL;
4095 }
4096
4097 cupsFreeOptions (num_settings, settings);
4098 debugprintf ("<- Connection_adminSetServerSettings()\n");
4099 Py_RETURN_NONE;
4100 }
4101
4102 static PyObject *
Connection_getSubscriptions(Connection * self,PyObject * args,PyObject * kwds)4103 Connection_getSubscriptions (Connection *self, PyObject *args, PyObject *kwds)
4104 {
4105 PyObject *uriobj;
4106 char *uri;
4107 PyObject *my_subscriptions = Py_False;
4108 int job_id = -1;
4109 ipp_t *request, *answer;
4110 ipp_attribute_t *attr;
4111 PyObject *result, *subscription;
4112 static char *kwlist[] = { "uri", "my_subscriptions", "job_id", NULL };
4113
4114 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|Oi", kwlist,
4115 &uriobj, &my_subscriptions, &job_id))
4116 return NULL;
4117
4118 if (UTF8_from_PyObj (&uri, uriobj) == NULL)
4119 return NULL;
4120
4121 if (my_subscriptions && !PyBool_Check (my_subscriptions)) {
4122 PyErr_SetString (PyExc_TypeError, "my_subscriptions must be a bool");
4123 return NULL;
4124 }
4125
4126 debugprintf ("-> Connection_getSubscriptions()\n");
4127 request = ippNewRequest (IPP_GET_SUBSCRIPTIONS);
4128 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
4129 "printer-uri", NULL, uri);
4130 free (uri);
4131 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
4132 "requesting-user-name", NULL, cupsUser ());
4133
4134 if (my_subscriptions == Py_True)
4135 ippAddBoolean (request, IPP_TAG_OPERATION, "my-subscriptions", 1);
4136
4137 if (job_id != -1)
4138 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4139 "job-id", job_id);
4140
4141 Connection_begin_allow_threads (self);
4142 answer = cupsDoRequest (self->http, request, "/");
4143 Connection_end_allow_threads (self);
4144 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
4145 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
4146 answer ? NULL : cupsLastErrorString ());
4147 if (answer)
4148 ippDelete (answer);
4149 debugprintf ("<- Connection_getSubscriptions() EXCEPTION\n");
4150 return NULL;
4151 }
4152
4153 result = PyList_New (0);
4154 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer))
4155 if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION)
4156 break;
4157
4158 subscription = NULL;
4159 for (; attr; attr = ippNextAttribute (answer)) {
4160 PyObject *obj;
4161 if (ippGetGroupTag (attr) == IPP_TAG_ZERO) {
4162 // End of subscription.
4163 if (subscription) {
4164 PyList_Append (result, subscription);
4165 Py_DECREF (subscription);
4166 }
4167
4168 subscription = NULL;
4169 continue;
4170 }
4171
4172 if (ippGetCount (attr) > 1 || !strcmp (ippGetName (attr), "notify-events"))
4173 obj = PyList_from_attr_values (attr);
4174 else
4175 obj = PyObject_from_attr_value (attr, 0);
4176
4177 if (!obj)
4178 // Can't represent this.
4179 continue;
4180
4181 if (!subscription)
4182 subscription = PyDict_New ();
4183
4184 PyDict_SetItemString (subscription, ippGetName (attr), obj);
4185 Py_DECREF (obj);
4186 }
4187
4188 if (subscription) {
4189 PyList_Append (result, subscription);
4190 Py_DECREF (subscription);
4191 }
4192
4193 ippDelete (answer);
4194 debugprintf ("<- Connection_getSubscriptions()\n");
4195 return result;
4196 }
4197
4198 static PyObject *
Connection_createSubscription(Connection * self,PyObject * args,PyObject * kwds)4199 Connection_createSubscription (Connection *self, PyObject *args,
4200 PyObject *kwds)
4201 {
4202 PyObject *resource_uriobj;
4203 char *resource_uri;
4204 PyObject *events = NULL;
4205 int job_id = -1, lease_duration = -1, time_interval = -1;
4206 PyObject *recipient_uriobj = NULL, *user_dataobj = NULL;
4207 char *recipient_uri = NULL, *user_data = NULL;
4208 ipp_t *request, *answer;
4209 int i, n = 0;
4210 char *tmp;
4211 ipp_attribute_t *attr;
4212 static char *kwlist[] = { "uri", "events", "job_id", "recipient_uri",
4213 "lease_duration", "time_interval", "user_data",
4214 NULL };
4215
4216 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|OiOiiO", kwlist,
4217 &resource_uriobj, &events, &job_id,
4218 &recipient_uriobj, &lease_duration,
4219 &time_interval, &user_dataobj))
4220 return NULL;
4221
4222 if (UTF8_from_PyObj (&resource_uri, resource_uriobj) == NULL)
4223 return NULL;
4224
4225 if (recipient_uriobj &&
4226 UTF8_from_PyObj (&recipient_uri, recipient_uriobj) == NULL) {
4227 free (resource_uri);
4228 return NULL;
4229 }
4230
4231 if (user_dataobj && UTF8_from_PyObj (&user_data, user_dataobj) == NULL) {
4232 free (resource_uri);
4233 if (recipient_uriobj)
4234 free (recipient_uri);
4235 return NULL;
4236 }
4237
4238 if (events) {
4239 if (!PyList_Check (events)) {
4240 PyErr_SetString (PyExc_TypeError, "events must be a list");
4241 return NULL;
4242 }
4243
4244 n = PyList_Size (events);
4245 for (i = 0; i < n; i++) {
4246 PyObject *event = PyList_GetItem (events, i);
4247 if (!PyUnicode_Check (event) && !PyBytes_Check (event)) {
4248 PyErr_SetString (PyExc_TypeError, "events must be a list of strings");
4249 return NULL;
4250 }
4251 }
4252 }
4253
4254 debugprintf ("-> Connection_createSubscription(%s)\n", resource_uri);
4255 request = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
4256 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
4257 "printer-uri", NULL, resource_uri);
4258 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
4259 "notify-pull-method", NULL, "ippget");
4260 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
4261 "notify-charset", NULL, "utf-8");
4262 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
4263 "requesting-user-name", NULL, cupsUser ());
4264
4265 if (recipient_uriobj) {
4266 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4267 "notify-recipient-uri", NULL, recipient_uri);
4268 free (recipient_uri);
4269 }
4270
4271 if (user_dataobj) {
4272 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_STRING,
4273 "notify-user-data", NULL, user_data);
4274 free (user_data);
4275 }
4276
4277 if (events) {
4278 attr = ippAddStrings (request, IPP_TAG_SUBSCRIPTION,
4279 IPP_TAG_KEYWORD, "notify-events",
4280 n, NULL, NULL);
4281 for (i = 0; i < n; i++) {
4282 PyObject *event = PyList_GetItem (events, i);
4283 ippSetString(request, &attr, i, UTF8_from_PyObj (&tmp, event));
4284 free(tmp);
4285 }
4286 }
4287
4288 if (lease_duration != -1)
4289 ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4290 "notify-lease-duration", lease_duration);
4291
4292 if (time_interval != -1)
4293 ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4294 "notify-time-interval", time_interval);
4295
4296 if (job_id != -1)
4297 ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4298 "notify-job-id", job_id);
4299
4300 Connection_begin_allow_threads (self);
4301 answer = cupsDoRequest (self->http, request, "/");
4302 Connection_end_allow_threads (self);
4303 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
4304 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
4305 answer ? NULL : cupsLastErrorString ());
4306 if (answer)
4307 ippDelete (answer);
4308 debugprintf ("<- Connection_createSubscription() EXCEPTION\n");
4309 return NULL;
4310 }
4311
4312 i = -1;
4313 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer)) {
4314 if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
4315 if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
4316 !strcmp (ippGetName (attr), "notify-subscription-id"))
4317 i = ippGetInteger (attr, 0);
4318 else if (ippGetValueTag (attr) == IPP_TAG_ENUM &&
4319 !strcmp (ippGetName (attr), "notify-status-code"))
4320 debugprintf ("notify-status-code = %d\n", ippGetInteger (attr, 0));
4321 }
4322 }
4323
4324 ippDelete (answer);
4325 debugprintf ("<- Connection_createSubscription() = %d\n", i);
4326 return PyLong_FromLong (i);
4327 }
4328
4329 static PyObject *
Connection_getNotifications(Connection * self,PyObject * args,PyObject * kwds)4330 Connection_getNotifications (Connection *self, PyObject *args, PyObject *kwds)
4331 {
4332 PyObject *subscription_ids, *sequence_numbers = NULL;
4333 ipp_t *request, *answer;
4334 int i, num_ids, num_seqs = 0;
4335 ipp_attribute_t *attr;
4336 PyObject *result, *events, *event;
4337 static char *kwlist[] = { "subscription_ids", "sequence_numbers", NULL };
4338
4339 if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist,
4340 &subscription_ids, &sequence_numbers))
4341 return NULL;
4342
4343 if (!PyList_Check (subscription_ids)) {
4344 PyErr_SetString (PyExc_TypeError, "subscriptions_ids must be a list");
4345 return NULL;
4346 }
4347
4348 num_ids = PyList_Size (subscription_ids);
4349 for (i = 0; i < num_ids; i++) {
4350 PyObject *id = PyList_GetItem (subscription_ids, i);
4351 if (!PyLong_Check (id)) {
4352 PyErr_SetString (PyExc_TypeError, "subscription_ids must be a list "
4353 "of integers");
4354 return NULL;
4355 }
4356 }
4357
4358 if (sequence_numbers) {
4359 if (!PyList_Check (sequence_numbers)) {
4360 PyErr_SetString (PyExc_TypeError, "sequence_numbers must be a list");
4361 return NULL;
4362 }
4363
4364 num_seqs = PyList_Size (sequence_numbers);
4365 for (i = 0; i < num_seqs; i++) {
4366 PyObject *id = PyList_GetItem (sequence_numbers, i);
4367 if (!PyLong_Check (id)) {
4368 PyErr_SetString (PyExc_TypeError, "sequence_numbers must be a list "
4369 "of integers");
4370 return NULL;
4371 }
4372 }
4373 }
4374
4375 debugprintf ("-> Connection_getNotifications()\n");
4376 request = ippNewRequest (IPP_GET_NOTIFICATIONS);
4377 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
4378 "printer-uri", NULL, "/");
4379 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
4380 "requesting-user-name", NULL, cupsUser ());
4381
4382 attr = ippAddIntegers (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4383 "notify-subscription-ids", num_ids, NULL);
4384 for (i = 0; i < num_ids; i++) {
4385 PyObject *id = PyList_GetItem (subscription_ids, i);
4386 ippSetInteger (request, &attr, i, PyLong_AsLong (id));
4387 }
4388
4389 if (sequence_numbers) {
4390 attr = ippAddIntegers (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4391 "notify-sequence-numbers", num_seqs, NULL);
4392 for (i = 0; i < num_seqs; i++) {
4393 PyObject *num = PyList_GetItem (sequence_numbers, i);
4394 ippSetInteger (request, &attr, i, PyLong_AsLong (num));
4395 }
4396 }
4397
4398 Connection_begin_allow_threads (self);
4399 answer = cupsDoRequest (self->http, request, "/");
4400 Connection_end_allow_threads (self);
4401 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
4402 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
4403 answer ? NULL : cupsLastErrorString ());
4404 if (answer)
4405 ippDelete (answer);
4406 debugprintf ("<- Connection_getNotifications() EXCEPTION\n");
4407 return NULL;
4408 }
4409
4410 result = PyDict_New ();
4411
4412 // Result-wide attributes.
4413 attr = ippFindAttribute (answer, "notify-get-interval", IPP_TAG_INTEGER);
4414 if (attr) {
4415 PyObject *val = PyLong_FromLong (ippGetInteger (attr, 0));
4416 PyDict_SetItemString (result, ippGetName (attr), val);
4417 Py_DECREF (val);
4418 }
4419
4420 attr = ippFindAttribute (answer, "printer-up-time", IPP_TAG_INTEGER);
4421 if (attr) {
4422 PyObject *val = PyLong_FromLong (ippGetInteger (attr, 0));
4423 PyDict_SetItemString (result, ippGetName (attr), val);
4424 Py_DECREF (val);
4425 }
4426
4427 events = PyList_New (0);
4428 for (attr = ippFirstAttribute (answer); attr; attr = ippNextAttribute (answer))
4429 if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION)
4430 break;
4431
4432 event = NULL;
4433 for (; attr; attr = ippNextAttribute (answer)) {
4434 PyObject *obj;
4435 if (ippGetGroupTag (attr) == IPP_TAG_ZERO) {
4436 // End of event notification.
4437 if (event) {
4438 PyList_Append (events, event);
4439 Py_DECREF (event);
4440 }
4441
4442 event = NULL;
4443 continue;
4444 }
4445
4446 if (ippGetCount (attr) > 1 ||
4447 !strcmp (ippGetName (attr), "notify-events") ||
4448 !strcmp (ippGetName (attr), "printer-state-reasons") ||
4449 !strcmp (ippGetName (attr), "job-printer-state-reasons"))
4450 obj = PyList_from_attr_values (attr);
4451 else
4452 obj = PyObject_from_attr_value (attr, 0);
4453
4454 if (!obj)
4455 // Can't represent this.
4456 continue;
4457
4458 if (!event)
4459 event = PyDict_New ();
4460
4461 PyDict_SetItemString (event, ippGetName (attr), obj);
4462 Py_DECREF (obj);
4463 }
4464
4465 if (event) {
4466 PyList_Append (events, event);
4467 Py_DECREF (event);
4468 }
4469
4470 ippDelete (answer);
4471 PyDict_SetItemString (result, "events", events);
4472 Py_DECREF (events);
4473 debugprintf ("<- Connection_getNotifications()\n");
4474 return result;
4475 }
4476
4477 static PyObject *
Connection_renewSubscription(Connection * self,PyObject * args,PyObject * kwds)4478 Connection_renewSubscription (Connection *self, PyObject *args, PyObject *kwds)
4479 {
4480 int id;
4481 int lease_duration = -1;
4482 ipp_t *request, *answer;
4483 static char *kwlist[] = { "id", "lease_duration", NULL };
4484
4485 if (!PyArg_ParseTupleAndKeywords (args, kwds, "i|i", kwlist,
4486 &id, &lease_duration))
4487 return NULL;
4488
4489 debugprintf ("-> Connection_renewSubscription()\n");
4490 request = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
4491 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
4492 "printer-uri", NULL, "/");
4493 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
4494 "requesting-user-name", NULL, cupsUser ());
4495 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4496 "notify-subscription-id", id);
4497
4498 if (lease_duration != -1)
4499 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4500 "notify-lease-duration", lease_duration);
4501
4502 Connection_begin_allow_threads (self);
4503 answer = cupsDoRequest (self->http, request, "/");
4504 Connection_end_allow_threads (self);
4505 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
4506 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
4507 answer ? NULL : cupsLastErrorString ());
4508 if (answer)
4509 ippDelete (answer);
4510 debugprintf ("<- Connection_renewSubscription() EXCEPTION\n");
4511 return NULL;
4512 }
4513
4514 ippDelete (answer);
4515 debugprintf ("<- Connection_renewSubscription()\n");
4516 Py_RETURN_NONE;
4517 }
4518
4519 static PyObject *
Connection_cancelSubscription(Connection * self,PyObject * args)4520 Connection_cancelSubscription (Connection *self, PyObject *args)
4521 {
4522 int id;
4523 ipp_t *request, *answer;
4524
4525 if (!PyArg_ParseTuple (args, "i", &id))
4526 return NULL;
4527
4528 debugprintf ("-> Connection_cancelSubscription()\n");
4529 request = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
4530 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
4531 "printer-uri", NULL, "/");
4532 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
4533 "requesting-user-name", NULL, cupsUser ());
4534 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
4535 "notify-subscription-id", id);
4536
4537 Connection_begin_allow_threads (self);
4538 answer = cupsDoRequest (self->http, request, "/");
4539 Connection_end_allow_threads (self);
4540 if (!answer || ippGetStatusCode (answer) > IPP_OK_CONFLICT) {
4541 set_ipp_error (answer ? ippGetStatusCode (answer) : cupsLastError (),
4542 answer ? NULL : cupsLastErrorString ());
4543 if (answer)
4544 ippDelete (answer);
4545 debugprintf ("<- Connection_cancelSubscription() EXCEPTION\n");
4546 return NULL;
4547 }
4548
4549 ippDelete (answer);
4550 debugprintf ("<- Connection_cancelSubscription()\n");
4551 Py_RETURN_NONE;
4552 }
4553
4554 static PyObject *
Connection_printFile(Connection * self,PyObject * args,PyObject * kwds)4555 Connection_printFile (Connection *self, PyObject *args, PyObject *kwds)
4556 {
4557 static char *kwlist[] = { "printer", "filename", "title", "options", NULL };
4558 PyObject *printer_obj;
4559 char *printer;
4560 PyObject *filename_obj;
4561 char *filename;
4562 PyObject *title_obj;
4563 char *title;
4564 PyObject *options_obj, *key, *val;
4565 int num_settings = 0;
4566 DICT_POS_TYPE pos = 0;
4567 cups_option_t *settings = NULL;
4568 int jobid;
4569
4570 if (!PyArg_ParseTupleAndKeywords (args, kwds, "OOOO", kwlist,
4571 &printer_obj, &filename_obj, &title_obj,
4572 &options_obj))
4573 return NULL;
4574
4575 if (UTF8_from_PyObj (&printer, printer_obj) == NULL)
4576 return NULL;
4577 if (UTF8_from_PyObj (&filename, filename_obj) == NULL) {
4578 free (printer);
4579 return NULL;
4580 }
4581 if (UTF8_from_PyObj (&title, title_obj) == NULL) {
4582 free (filename);
4583 free (printer);
4584 return NULL;
4585 }
4586
4587 if (!PyDict_Check (options_obj)) {
4588 free (title);
4589 free (filename);
4590 free (printer);
4591 PyErr_SetString (PyExc_TypeError, "options must be a dict");
4592 return NULL;
4593 }
4594 while (PyDict_Next (options_obj, &pos, &key, &val)) {
4595 char *name, *value;
4596 if ((!PyUnicode_Check (key) && !PyBytes_Check (key)) ||
4597 (!PyUnicode_Check (val) && !PyBytes_Check (val))) {
4598 cupsFreeOptions (num_settings, settings);
4599 free (title);
4600 free (filename);
4601 free (printer);
4602 PyErr_SetString (PyExc_TypeError, "Keys and values must be strings");
4603 return NULL;
4604 }
4605
4606 num_settings = cupsAddOption (UTF8_from_PyObj (&name, key),
4607 UTF8_from_PyObj (&value, val),
4608 num_settings,
4609 &settings);
4610 free (name);
4611 free (value);
4612 }
4613
4614 Connection_begin_allow_threads (self);
4615 jobid = cupsPrintFile2 (self->http, printer, filename, title, num_settings,
4616 settings);
4617 Connection_end_allow_threads (self);
4618
4619 if (jobid == 0) {
4620 cupsFreeOptions (num_settings, settings);
4621 free (title);
4622 free (filename);
4623 free (printer);
4624 set_ipp_error (cupsLastError (), cupsLastErrorString ());
4625 return NULL;
4626 }
4627
4628 cupsFreeOptions (num_settings, settings);
4629 free (title);
4630 free (filename);
4631 free (printer);
4632 return PyLong_FromLong (jobid);
4633 }
4634
4635 static void
free_string_list(int num_string,char ** strings)4636 free_string_list (int num_string, char **strings)
4637 {
4638 int i;
4639 for (i = 0; i < num_string; ++i) {
4640 free (strings[i]);
4641 }
4642 free (strings);
4643 }
4644
4645 static PyObject *
Connection_printFiles(Connection * self,PyObject * args,PyObject * kwds)4646 Connection_printFiles (Connection *self, PyObject *args, PyObject *kwds)
4647 {
4648 static char *kwlist[] = { "printer", "filenames", "title", "options", NULL };
4649 PyObject *printer_obj;
4650 char *printer;
4651 PyObject *filenames_obj;
4652 int num_filenames;
4653 char **filenames;
4654 PyObject *title_obj;
4655 char *title;
4656 PyObject *options_obj, *key, *val;
4657 int num_settings = 0;
4658 DICT_POS_TYPE pos = 0;
4659 cups_option_t *settings = NULL;
4660 int jobid;
4661
4662 if (!PyArg_ParseTupleAndKeywords (args, kwds, "OOOO", kwlist,
4663 &printer_obj, &filenames_obj, &title_obj,
4664 &options_obj))
4665 return NULL;
4666
4667 if (UTF8_from_PyObj (&printer, printer_obj) == NULL)
4668 return NULL;
4669
4670 if (!PyList_Check (filenames_obj)) {
4671 free (printer);
4672 PyErr_SetString (PyExc_TypeError, "filenames must be a list");
4673 return NULL;
4674 }
4675 num_filenames = PyList_Size (filenames_obj);
4676 if (num_filenames == 0) {
4677 free (printer);
4678 PyErr_SetString (PyExc_RuntimeError, "filenames list is empty");
4679 return NULL;
4680 }
4681 filenames = malloc (num_filenames * sizeof(char*));
4682 for (pos = 0; pos < num_filenames; ++pos) {
4683 PyObject *filename_obj = PyList_GetItem(filenames_obj, pos);
4684 if (UTF8_from_PyObj (&filenames[pos], filename_obj) == NULL) {
4685 free_string_list (pos, filenames);
4686 free (printer);
4687 return NULL;
4688 }
4689 }
4690 if (UTF8_from_PyObj (&title, title_obj) == NULL) {
4691 free_string_list (num_filenames, filenames);
4692 free (printer);
4693 return NULL;
4694 }
4695
4696 if (!PyDict_Check (options_obj)) {
4697 free (title);
4698 free_string_list (num_filenames, filenames);
4699 free (printer);
4700 PyErr_SetString (PyExc_TypeError, "options must be a dict");
4701 return NULL;
4702 }
4703 while (PyDict_Next (options_obj, &pos, &key, &val)) {
4704 char *name, *value;
4705 if ((!PyUnicode_Check (key) && !PyBytes_Check (key)) ||
4706 (!PyUnicode_Check (val) && !PyBytes_Check (val))) {
4707 cupsFreeOptions (num_settings, settings);
4708 free (title);
4709 free_string_list (num_filenames, filenames);
4710 free (printer);
4711 PyErr_SetString (PyExc_TypeError, "Keys and values must be strings");
4712 return NULL;
4713 }
4714
4715 num_settings = cupsAddOption (UTF8_from_PyObj (&name, key),
4716 UTF8_from_PyObj (&value, key),
4717 num_settings,
4718 &settings);
4719 free (name);
4720 free (value);
4721 }
4722
4723 Connection_begin_allow_threads (self);
4724 jobid = cupsPrintFiles2 (self->http, printer, num_filenames,
4725 (const char **) filenames, title, num_settings,
4726 settings);
4727 Connection_end_allow_threads (self);
4728
4729 if (jobid == 0) {
4730 cupsFreeOptions (num_settings, settings);
4731 free (title);
4732 free_string_list (num_filenames, filenames);
4733 free (printer);
4734 set_ipp_error (cupsLastError (), cupsLastErrorString ());
4735 return NULL;
4736 }
4737
4738 cupsFreeOptions (num_settings, settings);
4739 free (title);
4740 free_string_list (num_filenames, filenames);
4741 free (printer);
4742 return PyLong_FromLong (jobid);
4743 }
4744
4745 PyMethodDef Connection_methods[] =
4746 {
4747 { "getPrinters",
4748 (PyCFunction) Connection_getPrinters, METH_NOARGS,
4749 "getPrinters() -> dict\n\n"
4750 "@return: a dict, indexed by name, of dicts representing\n"
4751 "queues, indexed by attribute.\n"
4752 "@raise IPPError: IPP problem" },
4753
4754 { "getDests",
4755 (PyCFunction) Connection_getDests, METH_NOARGS,
4756 "getDests() -> dict\n\n"
4757 "@return: a dict representing available destinations. Each \n"
4758 "dictionary key is a pair of (queue, instance) strings, and the \n"
4759 "dictionary value is a L{cups.Dest} object. In addition to the \n"
4760 "available destinations, a special dictionary key (None,None) is \n"
4761 "provided for looking up the default destination; this destination \n"
4762 "will also be available under its own key.\n"
4763 "@raise IPPError: IPP problem" },
4764
4765 { "getClasses",
4766 (PyCFunction) Connection_getClasses, METH_NOARGS,
4767 "getClasses() -> dict\n\n"
4768 "@return: a dict, indexed by name, of objects representing\n"
4769 "classes. Each class object is either a string, in which case it\n"
4770 "is for the remote class; or a list, in which case it is a list of\n"
4771 "queue names.\n"
4772 "@raise IPPError: IPP problem" },
4773
4774 { "getPPDs",
4775 (PyCFunction) Connection_getPPDs, METH_VARARGS | METH_KEYWORDS,
4776 "getPPDs(limit=0, exclude_schemes=None, include_schemes=None, \n"
4777 "ppd_natural_language=None, ppd_device_id=None, ppd_make=None, \n"
4778 "ppd_make_and_model=None, ppd_model_number=-1, ppd_product=None, \n"
4779 "ppd_psversion=None, ppd_type=None) -> dict\n\n"
4780 "@type limit: integer\n"
4781 "@param limit: maximum number of PPDs to return\n"
4782 "@type exclude_schemes: string list\n"
4783 "@param exclude_schemes: list of PPD schemes to exclude\n"
4784 "@type include_schemes: string list\n"
4785 "@param include_schemes: list of PPD schemes to include\n"
4786 "@type ppd_natural_language: string\n"
4787 "@param ppd_natural_language: required language\n"
4788 "@type ppd_device_id: string\n"
4789 "@param ppd_device_id: IEEE 1284 Device ID to match against\n"
4790 "@type ppd_make: string\n"
4791 "@param ppd_make: required printer manufacturer\n"
4792 "@type ppd_make_and_model: string\n"
4793 "@param ppd_make_and_model: required make and model\n"
4794 "@type ppd_model_number: integer\n"
4795 "@param ppd_model_number: model number required (from cupsModelNumber \n"
4796 "in PPD file)\n"
4797 "@type ppd_product: string\n"
4798 "@param ppd_product: required PostScript product string (Product)\n"
4799 "@type ppd_psversion: string\n"
4800 "@param ppd_psversion: required PostScript version (PSVersion)\n"
4801 "@type ppd_type: string\n"
4802 "@param ppd_type: required type of PPD. Valid values are fax; pdf; \n"
4803 "postscript; raster; unknown.\n"
4804 "@return: a dict, indexed by PPD name, of dicts representing\n"
4805 "PPDs, indexed by attribute.\n"
4806 "@raise IPPError: IPP problem" },
4807
4808 { "getPPDs2",
4809 (PyCFunction) Connection_getPPDs2, METH_VARARGS | METH_KEYWORDS,
4810 "getPPDs2(limit=0, exclude_schemes=None, include_schemes=None, \n"
4811 "ppd_natural_language=None, ppd_device_id=None, ppd_make=None, \n"
4812 "ppd_make_and_model=None, ppd_model_number=-1, ppd_product=None, \n"
4813 "ppd_psversion=None, ppd_type=None) -> dict\n\n"
4814 "@type limit: integer\n"
4815 "@param limit: maximum number of PPDs to return\n"
4816 "@type exclude_schemes: string list\n"
4817 "@param exclude_schemes: list of PPD schemes to exclude\n"
4818 "@type include_schemes: string list\n"
4819 "@param include_schemes: list of PPD schemes to include\n"
4820 "@type ppd_natural_language: string\n"
4821 "@param ppd_natural_language: required language\n"
4822 "@type ppd_device_id: string\n"
4823 "@param ppd_device_id: IEEE 1284 Device ID to match against\n"
4824 "@type ppd_make: string\n"
4825 "@param ppd_make: required printer manufacturer\n"
4826 "@type ppd_make_and_model: string\n"
4827 "@param ppd_make_and_model: required make and model\n"
4828 "@type ppd_model_number: integer\n"
4829 "@param ppd_model_number: model number required (from cupsModelNumber \n"
4830 "in PPD file)\n"
4831 "@type ppd_product: string\n"
4832 "@param ppd_product: required PostScript product string (Product)\n"
4833 "@type ppd_psversion: string\n"
4834 "@param ppd_psversion: required PostScript version (PSVersion)\n"
4835 "@type ppd_type: string\n"
4836 "@param ppd_type: required type of PPD. Valid values are fax; pdf; \n"
4837 "postscript; raster; unknown.\n"
4838 "@return: a dict, indexed by PPD name, of dicts representing\n"
4839 "PPDs, indexed by attribute. All attribute values are lists.\n"
4840 "@raise IPPError: IPP problem" },
4841
4842 { "getServerPPD",
4843 (PyCFunction) Connection_getServerPPD, METH_VARARGS,
4844 "getServerPPD(ppd_name) -> string\n\n"
4845 "Fetches the named PPD and stores it in a temporary file.\n\n"
4846 "@type ppd_name: string\n"
4847 "@param ppd_name: the ppd-name of a PPD\n"
4848 "@return: temporary filename holding the PPD\n"
4849 "@raise RuntimeError: Not supported in libcups until 1.3\n"
4850 "@raise IPPError: IPP problem" },
4851
4852 { "getDocument",
4853 (PyCFunction) Connection_getDocument, METH_VARARGS,
4854 "getDocument(printer_uri, job_id, document_number) -> dict\n\n"
4855 "Fetches the job document and stores it in a temporary file.\n\n"
4856 "@type printer_uri: string\n"
4857 "@param printer_uri: the printer-uri for the printer\n"
4858 "@type job_id: integer\n"
4859 "@param job_id: the job ID\n"
4860 "@type document_number: integer\n"
4861 "@param document_number: the document number to retrieve\n"
4862 "@return: a dict with the following keys: "
4863 " 'file' (string), temporary filename holding the job document; "
4864 " 'document-format' (string), its MIME type. There may also be a "
4865 " 'document-name' key, in which case this is for the document name.\n"
4866 "@raise RuntimeError: Not supported in libcups until 1.4\n"
4867 "@raise IPPError: IPP problem" },
4868
4869 { "getDevices",
4870 (PyCFunction) Connection_getDevices, METH_VARARGS | METH_KEYWORDS,
4871 "getDevices(limit=0, exclude_schemes=None, include_schemes=None) -> dict\n\n"
4872 "@type limit: integer\n"
4873 "@param limit: maximum number of devices to return\n"
4874 "@type exclude_schemes: string list\n"
4875 "@param exclude_schemes: URI schemes to exclude\n"
4876 "@type include_schemes: string list\n"
4877 "@param include_schemes: URI schemes to include\n"
4878 "@return: a dict, indexed by device URI, of dicts representing\n"
4879 "devices, indexed by attribute.\n"
4880 "@raise IPPError: IPP problem" },
4881
4882 { "getJobs",
4883 (PyCFunction) Connection_getJobs, METH_VARARGS | METH_KEYWORDS,
4884 "getJobs(which_jobs='not-completed', my_jobs=False, limit=-1, first_job_id=-1, requested_attributes=None) -> dict\n"
4885 "Fetch a list of jobs.\n"
4886 "@type which_jobs: string\n"
4887 "@param which_jobs: which jobs to fetch; possible values: \n"
4888 "'completed', 'not-completed', 'all'\n"
4889 "@type my_jobs: boolean\n"
4890 "@param my_jobs: whether to restrict the returned jobs to those \n"
4891 "owned by the current CUPS user (as set by L{cups.setUser}).\n"
4892 "@return: a dict, indexed by job ID, of dicts representing job\n"
4893 "attributes.\n"
4894 "@type limit: integer\n"
4895 "@param limit: maximum number of jobs to return\n"
4896 "@type first_job_id: integer\n"
4897 "@param first_job_id: lowest job ID to return\n"
4898 "@type requested_attributes: string list\n"
4899 "@param requested_attributes: list of requested attribute names\n"
4900 "@raise IPPError: IPP problem" },
4901
4902 { "getJobAttributes",
4903 (PyCFunction) Connection_getJobAttributes, METH_VARARGS | METH_KEYWORDS,
4904 "getJobAttributes(jobid, requested_attributes=None) -> dict\n\n"
4905 "Fetch job attributes.\n"
4906 "@type jobid: integer\n"
4907 "@param jobid: job ID\n"
4908 "@type requested_attributes: string list\n"
4909 "@param requested_attributes: list of requested attribute names\n"
4910 "@return: a dict representing job attributes.\n"
4911 "@raise IPPError: IPP problem" },
4912
4913 { "cancelJob",
4914 (PyCFunction) Connection_cancelJob, METH_VARARGS | METH_KEYWORDS,
4915 "cancelJob(jobid, purge_job=False) -> None\n\n"
4916 "@type jobid: integer\n"
4917 "@param jobid: job ID to cancel\n"
4918 "@type purge_job: boolean\n"
4919 "@param purge_job: whether to remove data and control files\n"
4920 "@raise IPPError: IPP problem" },
4921
4922 { "cancelAllJobs",
4923 (PyCFunction) Connection_cancelAllJobs, METH_VARARGS | METH_KEYWORDS,
4924 "cancelAllJobs(name=None, uri=None, my_jobs=False, purge_jobs=True) -> None\n\n"
4925 "@type name: string\n"
4926 "@param name: queue name\n"
4927 "@type uri: string\n"
4928 "@param uri: printer URI\n"
4929 "@type my_jobs: boolean\n"
4930 "@param my_jobs: whether to restrict operation to jobs owned by \n"
4931 "the current CUPS user (as set by L{cups.setUser}).\n"
4932 "@type purge_jobs: boolean\n"
4933 "@param purge_jobs: whether to remove data and control files\n"
4934 "@raise IPPError: IPP problem" },
4935
4936 { "moveJob",
4937 (PyCFunction) Connection_moveJob, METH_VARARGS | METH_KEYWORDS,
4938 "moveJob(printer_uri=None, job_id=-1, job_printer_uri) -> None\n\n"
4939 "Move a job specified by printer_uri and jobid (only one need be given)\n"
4940 "to the printer specified by job_printer_uri.\n\n"
4941 "@type job_id: integer\n"
4942 "@param job_id: job ID to move\n"
4943 "@type printer_uri: string\n"
4944 "@param printer_uri: printer to move job(s) from\n"
4945 "@type job_printer_uri: string\n"
4946 "@param job_printer_uri: printer to move job(s) to\n"
4947 "@raise IPPError: IPP problem" },
4948
4949 { "authenticateJob",
4950 (PyCFunction) Connection_authenticateJob, METH_VARARGS,
4951 "authenticateJob(jobid, auth_info=None) -> None\n\n"
4952 "@type jobid: integer\n"
4953 "@param jobid: job ID to authenticate\n"
4954 "@type auth_info: optional string list\n"
4955 "@param auth_info: authentication details\n"
4956 "@raise IPPError: IPP problem" },
4957
4958 { "setJobHoldUntil",
4959 (PyCFunction) Connection_setJobHoldUntil, METH_VARARGS,
4960 "setJobHoldUntil(jobid, job_hold_until) -> None\n\n"
4961 "Specifies when a job should be printed.\n"
4962 "@type jobid: integer\n"
4963 "@param jobid: job ID to adjust\n"
4964 "@type job_hold_until: string\n"
4965 "@param job_hold_until: when to print the job; examples: 'hold', \n"
4966 "'immediate', 'restart', resume'\n"
4967 "@raise IPPError: IPP problem"},
4968
4969 { "restartJob",
4970 (PyCFunction) Connection_restartJob, METH_VARARGS | METH_KEYWORDS,
4971 "restartJob(job_id, job_hold_until=None) -> None\n\n"
4972 "Restart a job.\n\n"
4973 "@type job_id: integer\n"
4974 "@param job_id: job ID to restart\n"
4975 "@type job_hold_until: string\n"
4976 "@param job_hold_until: new job-hold-until value for job\n"
4977 "@raise IPPError: IPP problem" },
4978
4979 { "getFile",
4980 (PyCFunction) Connection_getFile, METH_VARARGS | METH_KEYWORDS,
4981 "getFile(resource, filename=None, fd=-1, file=None) -> None\n\n"
4982 "Fetch a CUPS server resource to a local file.\n\n"
4983 "This is for obtaining CUPS server configuration files and \n"
4984 "log files.\n\n"
4985 "@type resource: string\n"
4986 "@param resource: resource name\n"
4987 "@type filename: string\n"
4988 "@param filename: name of local file for storage\n"
4989 "@type fd: int\n"
4990 "@param fd: file descriptor of local file\n"
4991 "@type file: file\n"
4992 "@param file: Python file object for local file\n"
4993 "@raise HTTPError: HTTP problem" },
4994
4995 { "putFile",
4996 (PyCFunction) Connection_putFile, METH_VARARGS | METH_KEYWORDS,
4997 "putFile(resource, filename=None, fd=-1, file=None) -> None\n\n"
4998 "This is for uploading new configuration files for the CUPS \n"
4999 "server. Note: L{adminSetServerSettings} is a way of \n"
5000 "adjusting server settings without needing to parse the \n"
5001 "configuration file.\n"
5002 "@type resource: string\n"
5003 "@param resource: resource name\n"
5004 "@type filename: string\n"
5005 "@param filename: name of local file to upload\n"
5006 "@type fd: int\n"
5007 "@param fd: file descriptor of local file\n"
5008 "@type file: file\n"
5009 "@param file: Python file object for local file\n"
5010 "@raise HTTPError: HTTP problem"},
5011
5012 { "addPrinter",
5013 (PyCFunction) Connection_addPrinter, METH_VARARGS | METH_KEYWORDS,
5014 "addPrinter(name) -> None\n\n"
5015 "Add or adjust a print queue. Several parameters can select which\n"
5016 "PPD to use (filename, ppdname, and ppd) but only one may be\n"
5017 "given.\n\n"
5018 "@type filename: string\n"
5019 "@keyword filename: local filename of PPD file\n"
5020 "@type ppdname: string\n"
5021 "@keyword ppdname: filename from L{getPPDs}\n"
5022 "@type info: string\n"
5023 "@keyword info: human-readable information about the printer\n"
5024 "@type location: string\n"
5025 "@keyword location: human-readable printer location\n"
5026 "@type device: string\n"
5027 "@keyword device: device URI string\n"
5028 "@type ppd: L{cups.PPD} instance\n"
5029 "@keyword ppd: PPD object\n"
5030 "@raise IPPError: IPP problem" },
5031
5032 { "setPrinterDevice",
5033 (PyCFunction) Connection_setPrinterDevice, METH_VARARGS,
5034 "setPrinterDevice(name, device_uri) -> None\n\n"
5035 "Set the device URI for a printer.\n\n"
5036 "@type name: string\n"
5037 "@param name: queue name\n"
5038 "@type device_uri: string\n"
5039 "@param device_uri: device URI\n"
5040 "@raise IPPError: IPP problem" },
5041
5042 { "setPrinterInfo",
5043 (PyCFunction) Connection_setPrinterInfo, METH_VARARGS,
5044 "setPrinterInfo(name, info) -> None\n\n"
5045 "Set the human-readable information about a printer.\n\n"
5046 "@type name: string\n"
5047 "@param name: queue name\n"
5048 "@type info: string\n"
5049 "@param info: human-readable information about the printer\n"
5050 "@raise IPPError: IPP problem" },
5051
5052 { "setPrinterLocation",
5053 (PyCFunction) Connection_setPrinterLocation, METH_VARARGS,
5054 "setPrinterLocation(name, location) -> None\n\n"
5055 "Set the human-readable printer location\n\n"
5056 "@type name: string\n"
5057 "@param name: queue name\n"
5058 "@type location: string\n"
5059 "@param location: human-readable printer location\n"
5060 "@raise IPPError: IPP problem" },
5061
5062 { "setPrinterShared",
5063 (PyCFunction) Connection_setPrinterShared, METH_VARARGS,
5064 "setPrinterShared(name, shared) -> None\n\n"
5065 "Set whether a printer is shared with other people. This works \n"
5066 "with CUPS servers of at least version 1.2, by setting the \n"
5067 "printer-is-shared printer attribute.\n\n"
5068 "@type name: string\n"
5069 "@param name: queue name\n"
5070 "@type shared: boolean\n"
5071 "@param shared: whether printer should be shared\n"
5072 "@raise IPPError: IPP problem" },
5073
5074 { "setPrinterJobSheets",
5075 (PyCFunction) Connection_setPrinterJobSheets, METH_VARARGS,
5076 "setPrinterJobSheets(name, start, end) -> None\n\n"
5077 "Specifies job sheets for a printer.\n\n"
5078 "@type name: string\n"
5079 "@param name: queue name\n"
5080 "@type start: string\n"
5081 "@param start: name of a sheet to print before each job\n"
5082 "@type end: string\n"
5083 "@param end: name of a sheet to print after each job\n"
5084 "@raise IPPError: IPP problem" },
5085
5086 { "setPrinterErrorPolicy",
5087 (PyCFunction) Connection_setPrinterErrorPolicy, METH_VARARGS,
5088 "setPrinterErrorPolicy(name, policy) -> None\n\n"
5089 "Set the printer's error policy.\n\n"
5090 "@type name: string\n"
5091 "@param name: queue name\n"
5092 "@type policy: string\n"
5093 "@param policy: policy name; supported policy names can be found \n"
5094 "by using the L{getPrinterAttributes} function and looking for the \n"
5095 "'printer-error-policy-supported' attribute\n"
5096 "@raise IPPError: IPP problem" },
5097
5098 { "setPrinterOpPolicy",
5099 (PyCFunction) Connection_setPrinterOpPolicy, METH_VARARGS,
5100 "setPrinterOpPolicy(name, policy) -> None\n\n"
5101 "Set the printer's operation policy.\n\n"
5102 "@type name: string\n"
5103 "@param name: queue name\n"
5104 "@type policy: string\n"
5105 "@param policy: policy name; supported policy names can be found \n"
5106 "by using the L{getPrinterAttributes} function and looking for the \n"
5107 "'printer-op-policy-supported' attribute\n"
5108 "@raise IPPError: IPP problem" },
5109
5110 { "setPrinterUsersAllowed",
5111 (PyCFunction) Connection_setPrinterUsersAllowed, METH_VARARGS,
5112 "setPrinterUsersAllowed(name, allowed) -> None\n\n"
5113 "Set the list of users allowed to use a printer. This works \n"
5114 "with CUPS server of at least version 1.2, by setting the \n"
5115 "requesting-user-name-allowed printer attribute.\n\n"
5116 "@type name: string\n"
5117 "@param name: queue name\n"
5118 "@type allowed: string list\n"
5119 "@param allowed: list of allowed users; ['all'] \n"
5120 "means there will be no user-name restriction.\n"
5121 "@raise IPPError: IPP problem" },
5122
5123 { "setPrinterUsersDenied",
5124 (PyCFunction) Connection_setPrinterUsersDenied, METH_VARARGS,
5125 "setPrinterUsersDenied(name, denied) -> None\n\n"
5126 "Set the list of users denied the use of a printer. This works \n"
5127 "with CUPS servers of at least version 1.2, by setting the \n"
5128 "requesting-user-name-denied printer attribute.\n\n"
5129 "@type name: string\n"
5130 "@param name: queue name\n"
5131 "@type denied: string list\n"
5132 "@param denied: list of denied users; ['none'] \n"
5133 "means there will be no user-name restriction.\n"
5134 "@raise IPPError: IPP problem" },
5135
5136 { "addPrinterOptionDefault",
5137 (PyCFunction) Connection_addPrinterOptionDefault, METH_VARARGS,
5138 "addPrinterOptionDefault(name, option, value) -> None\n\n"
5139 "Set a network default option. Jobs submitted to the named queue \n"
5140 "will have the job option added if it is not already present in the \n"
5141 "job. This works with CUPS servers of at least version 1.2.\n\n"
5142 "@type name: string\n"
5143 "@param name: queue name\n"
5144 "@type option: string\n"
5145 "@param option: option name, for example 'job-priority'\n"
5146 "@type value: string\n"
5147 "@param value: option value as a string\n"
5148 "@raise IPPError: IPP problem" },
5149
5150 { "deletePrinterOptionDefault",
5151 (PyCFunction) Connection_deletePrinterOptionDefault, METH_VARARGS,
5152 "deletePrinterOptionDefault(name, option) -> None\n\n"
5153 "Removes a network default option. See L{addPrinterOptionDefault}.\n\n"
5154 "@type name: string\n"
5155 "@param name: queue name\n"
5156 "@type option: string\n"
5157 "@param option: option name, for example 'job-priority'\n"
5158 "@raise IPPError: IPP problem" },
5159
5160 { "deletePrinter",
5161 (PyCFunction) Connection_deletePrinter, METH_VARARGS | METH_KEYWORDS,
5162 "deletePrinter(name) -> None\n\n"
5163 "Delete a printer.\n\n"
5164 "@type name: string\n"
5165 "@param name: queue name\n"
5166 "@raise IPPError: IPP problem" },
5167
5168 { "getPrinterAttributes",
5169 (PyCFunction) Connection_getPrinterAttributes,
5170 METH_VARARGS | METH_KEYWORDS,
5171 "getPrinterAttributes(name=None, uri=None, requested_attributes=None) -> dict\n"
5172 "Fetch the attributes for a printer, specified either by name or by \n"
5173 "uri but not both.\n\n"
5174 "@type name: string\n"
5175 "@param name: queue name\n"
5176 "@type uri: string\n"
5177 "@param uri: queue URI\n"
5178 "@type requested_attributes: string list\n"
5179 "@param requested_attributes: list of requested attribute names\n"
5180 "@return: a dict, indexed by attribute, of printer attributes\n"
5181 "for the specified printer.\n\n"
5182 "Attributes:\n"
5183 " - 'job-sheets-supported': list of strings\n"
5184 " - 'job-sheets-default': tuple of strings (start, end)\n"
5185 " - 'printer-error-policy-supported': if present, list of strings\n"
5186 " - 'printer-error-policy': if present, string\n"
5187 " - 'printer-op-policy-supported': if present, list of strings\n"
5188 " - 'printer-op-policy': if present, string\n\n"
5189 "There are other attributes; the exact list of attributes returned \n"
5190 "will depend on the IPP server.\n"
5191 "@raise IPPError: IPP problem"},
5192
5193 { "addPrinterToClass",
5194 (PyCFunction) Connection_addPrinterToClass, METH_VARARGS,
5195 "addPrinterToClass(name, class) -> None\n\n"
5196 "Add a printer to a class. If the class does not yet exist, \n"
5197 "it is created.\n\n"
5198 "@type name: string\n"
5199 "@param name: queue name\n"
5200 "@type class: string\n"
5201 "@param class: class name\n"
5202 "@raise IPPError: IPP problem" },
5203
5204 { "deletePrinterFromClass",
5205 (PyCFunction) Connection_deletePrinterFromClass, METH_VARARGS,
5206 "deletePrinterFromClass(name, class) -> None\n\n"
5207 "Remove a printer from a class. If the class would be left empty, \n"
5208 "it is removed.\n\n"
5209 "@type name: string\n"
5210 "@param name: queue name\n"
5211 "@type class: string\n"
5212 "@param class: class name\n"
5213 "@raise IPPError: IPP problem" },
5214
5215 { "deleteClass",
5216 (PyCFunction) Connection_deleteClass, METH_VARARGS,
5217 "deleteClass(class) -> None\n\n"
5218 "Delete a class.\n\n"
5219 "@type class: string\n"
5220 "@param class: class name\n"
5221 "@raise IPPError: IPP problem" },
5222
5223 { "getDefault",
5224 (PyCFunction) Connection_getDefault, METH_NOARGS,
5225 "getDefault() -> string or None\n\n"
5226 "Get the system default printer.\n\n"
5227 "@return: default printer name or None" },
5228
5229 { "setDefault",
5230 (PyCFunction) Connection_setDefault, METH_VARARGS | METH_KEYWORDS,
5231 "setDefault(name) -> None\n\n"
5232 "Set the system default printer. Note that this can be over-ridden \n"
5233 "on a per-user basis using the lpoptions command.\n\n"
5234 "@type name: string\n"
5235 "@param name: queue name\n"
5236 "@raise IPPError: IPP problem" },
5237
5238 { "getPPD",
5239 (PyCFunction) Connection_getPPD, METH_VARARGS,
5240 "getPPD(name) -> string\n\n"
5241 "Fetch a printer's PPD.\n\n"
5242 "@type name: string\n"
5243 "@param name: queue name\n"
5244 "@return: temporary PPD file name\n"
5245 "@raise IPPError: IPP problem" },
5246
5247 { "getPPD3",
5248 (PyCFunction) Connection_getPPD3, METH_VARARGS | METH_KEYWORDS,
5249 "getPPD3(name[, modtime, filename]) -> (status,modtime,filename)\n\n"
5250 "Fetch a printer's PPD if it is newer.\n\n"
5251 "@type name: string\n"
5252 "@param name: queue name\n"
5253 "@type modtime: float\n"
5254 "@param modtime: modification time of existing file\n"
5255 "@type filename: string\n"
5256 "@param filename: filename of existing file\n"
5257 "@return: tuple of HTTP status, modification time, and filename\n" },
5258
5259 { "enablePrinter",
5260 (PyCFunction) Connection_enablePrinter, METH_VARARGS | METH_KEYWORDS,
5261 "enablePrinter(name) -> None\n\n"
5262 "Enable printer. This allows the printer to process its job queue.\n\n"
5263 "@type name: string\n"
5264 "@param name: queue name\n"
5265 "@raise IPPError: IPP problem" },
5266
5267 { "disablePrinter",
5268 (PyCFunction) Connection_disablePrinter, METH_VARARGS | METH_KEYWORDS,
5269 "disablePrinter(name) -> None\n\n"
5270 "Disable printer. This prevents the printer from processing its \n"
5271 "job queue.\n\n"
5272 "@type name: string\n"
5273 "@param name: queue name\n"
5274 "@type reason: string\n"
5275 "@keyword reason: optional human-readable reason for disabling the \n"
5276 "printer\n"
5277 "@raise IPPError: IPP problem" },
5278
5279 { "acceptJobs",
5280 (PyCFunction) Connection_acceptJobs, METH_VARARGS | METH_KEYWORDS,
5281 "acceptJobs(name) -> None\n\n"
5282 "Cause printer to accept jobs.\n"
5283 "@type name: string\n"
5284 "@param name: queue name\n"
5285 "@raise IPPError: IPP problem" },
5286
5287 { "rejectJobs",
5288 (PyCFunction) Connection_rejectJobs, METH_VARARGS | METH_KEYWORDS,
5289 "rejectJobs(name)\n\n"
5290 "Cause printer to reject jobs.\n\n"
5291 "@type name: string\n"
5292 "@param name: queue name\n"
5293 "@type reason: string\n"
5294 "@keyword reason: optional human-readable reason for rejecting jobs\n"
5295 "@raise IPPError: IPP problem" },
5296
5297 { "printTestPage",
5298 (PyCFunction) Connection_printTestPage, METH_VARARGS | METH_KEYWORDS,
5299 "printTestPage(name) -> job ID\n\n"
5300 "Print a test page.\n\n"
5301 "@type name: string\n"
5302 "@param name: queue name\n"
5303 "@type file: string\n"
5304 "@keyword file: input file (default is CUPS test page)\n"
5305 "@type title: string\n"
5306 "@keyword title: job title (default 'Test Page')\n"
5307 "@type format: string\n"
5308 "@keyword format: document format (default 'application/postscript')\n"
5309 "@type user: string\n"
5310 "@keyword user: user to submit the job as\n"
5311 "@raise IPPError: IPP problem" },
5312
5313 { "adminExportSamba",
5314 (PyCFunction) Connection_adminExportSamba, METH_VARARGS,
5315 "adminExportSamba(name, samba_server, samba_username,\n"
5316 " samba_password) -> None\n\n"
5317 "Export a printer to Samba.\n\n"
5318 "@type name: string\n"
5319 "@param name: queue name\n"
5320 "@type samba_server: string\n"
5321 "@param samba_server: samba server\n"
5322 "@type samba_username: string\n"
5323 "@param samba_username: samba username\n"
5324 "@type samba_password: string\n"
5325 "@param samba_password: samba password\n"
5326 "@raise IPPError: IPP problem" },
5327
5328 { "adminGetServerSettings",
5329 (PyCFunction) Connection_adminGetServerSettings, METH_NOARGS,
5330 "adminGetServerSettings() -> dict\n\n"
5331 "Get server settings.\n\n"
5332 "@return: dict representing server settings; keywords include \n"
5333 "L{CUPS_SERVER_DEBUG_LOGGING}, L{CUPS_SERVER_REMOTE_ADMIN}, \n"
5334 "L{CUPS_SERVER_REMOTE_PRINTERS}, L{CUPS_SERVER_SHARE_PRINTERS}, \n"
5335 "L{CUPS_SERVER_USER_CANCEL_ANY}\n"
5336 "@see: L{adminSetServerSettings}\n"
5337 "@raise IPPError: IPP problem" },
5338
5339 { "adminSetServerSettings",
5340 (PyCFunction) Connection_adminSetServerSettings, METH_VARARGS,
5341 "adminSetServerSettings(settings) -> None\n\n"
5342 "Set server settings.\n\n"
5343 "@type settings: dict\n"
5344 "@param settings: dict of server settings\n"
5345 "@see: L{adminGetServerSettings}\n"
5346 "@raise IPPError: IPP problem" },
5347
5348 { "getSubscriptions",
5349 (PyCFunction) Connection_getSubscriptions, METH_VARARGS | METH_KEYWORDS,
5350 "getSubscriptions(uri) -> integer list\n\n"
5351 "Get subscriptions.\n\n"
5352 "@type uri: string\n"
5353 "@param uri: URI for object\n"
5354 "@type my_subscriptions: boolean\n"
5355 "@keyword my_subscriptions: only return subscriptions belonging to \n"
5356 "the current user (default False)\n"
5357 "@type job_id: integer\n"
5358 "@keyword job_id: only return subscriptions relating to this job\n"
5359 "@return: list of subscriptions\n"
5360 "@raise IPPError: IPP problem" },
5361
5362 { "createSubscription",
5363 (PyCFunction) Connection_createSubscription,
5364 METH_VARARGS | METH_KEYWORDS,
5365 "createSubscription(uri, events=[], job_id=-1, recipient_uri="",\n"
5366 " lease_duration=-1, time_interval=-1,\n"
5367 " user_data="") -> integer\n\n"
5368 "Create a subscription.\n\n"
5369 "@type uri: string\n"
5370 "@param uri: URI for object\n"
5371 "@type events: string list\n"
5372 "@keyword events: events to receive notifications for\n"
5373 "@type job_id: integer\n"
5374 "@keyword job_id: job ID to receive notifications for\n"
5375 "@type recipient_uri: string\n"
5376 "@keyword recipient_uri: URI for notifications recipient\n"
5377 "@type lease_duration: integer\n"
5378 "@keyword lease_duration: lease duration in seconds\n"
5379 "@type time_interval: integer\n"
5380 "@keyword time_interval: time interval\n"
5381 "@type user_data: string\n"
5382 "@keyword user_data: user data to receive with notifications\n"
5383 "@return: subscription ID\n"
5384 "@raise IPPError: IPP problem" },
5385
5386 { "getNotifications",
5387 (PyCFunction) Connection_getNotifications, METH_VARARGS | METH_KEYWORDS,
5388 "getNotifications(subscription_ids) -> list\n\n"
5389 "Get notifications for subscribed events.\n\n"
5390 "@type subscription_ids: integer list\n"
5391 "@param subscription_ids: list of subscription IDs to receive \n"
5392 "notifications for\n"
5393 "@return: list of dicts, each representing an event\n"
5394 "@raise IPPError: IPP problem" },
5395
5396 { "cancelSubscription",
5397 (PyCFunction) Connection_cancelSubscription, METH_VARARGS,
5398 "cancelSubscription(id) -> None\n\n"
5399 "Cancel a subscription.\n\n"
5400 "@type id: integer\n"
5401 "@param id: subscription ID\n"
5402 "@raise IPPError: IPP problem" },
5403
5404 { "renewSubscription",
5405 (PyCFunction) Connection_renewSubscription, METH_VARARGS | METH_KEYWORDS,
5406 "renewSubscription(id, lease_duration=-1) -> None\n\n"
5407 "Renew a subscription.\n\n"
5408 "@type id: integer\n"
5409 "@param id: subscription ID\n"
5410 "@type lease_duration: integer\n"
5411 "@param lease_duration: lease duration in seconds\n"
5412 "@raise IPPError: IPP problem" },
5413
5414 { "createJob",
5415 (PyCFunction) Connection_createJob, METH_VARARGS | METH_KEYWORDS,
5416 "createJob(printer, title, options) -> integer\n\n"
5417 "Create an empty job for streaming.\n\n"
5418 "@type printer: string\n"
5419 "@param printer: queue name\n"
5420 "@type title: string\n"
5421 "@param title: title of the print job\n"
5422 "@type options: dict\n"
5423 "@param options: dict of options\n"
5424 "@return: job ID\n"
5425 "@raise IPPError: IPP problem" },
5426
5427 { "startDocument",
5428 (PyCFunction) Connection_startDocument, METH_VARARGS | METH_KEYWORDS,
5429 "startDocument(printer, job_id, doc_name, format, last_document) -> integer\n\n"
5430 "Add a document to a job created with createJob.\n\n"
5431 "@type printer: string\n"
5432 "@param printer: queue name\n"
5433 "@type job_id: integer\n"
5434 "@param job_id: job ID to create document\n"
5435 "@type doc_name: string\n"
5436 "@param doc_name: name of the document\n"
5437 "@type format: string\n"
5438 "@param format: MIME type\n"
5439 "@type last_document: integer\n"
5440 "@param last_document: 1 for last document of job, 0 otherwise\n"
5441 "@return: HTTP status\n"
5442 "@raise IPPError: IPP problem" },
5443
5444 { "writeRequestData",
5445 (PyCFunction) Connection_writeRequestData, METH_VARARGS | METH_KEYWORDS,
5446 "writeRequestData(buffer, length) -> integer\n\n"
5447 "Write data after an IPP request.\n\n"
5448 "@type buffer: string\n"
5449 "@param buffer: bytes to write\n"
5450 "@type length: integer\n"
5451 "@param length: number of bytes to write\n"
5452 "@return: HTTP status\n"
5453 "@raise IPPError: IPP problem" },
5454
5455 { "finishDocument",
5456 (PyCFunction) Connection_finishDocument, METH_VARARGS | METH_KEYWORDS,
5457 "finishDocument(printer) -> integer\n\n"
5458 "Finish sending a document.\n\n"
5459 "@type printer: string\n"
5460 "@param printer: queue name\n"
5461 "@return: HTTP status\n"
5462 "@raise IPPError: IPP problem" },
5463
5464 { "printFile",
5465 (PyCFunction) Connection_printFile, METH_VARARGS | METH_KEYWORDS,
5466 "printFile(printer, filename, title, options) -> integer\n\n"
5467 "Print a file.\n\n"
5468 "@type printer: string\n"
5469 "@param printer: queue name\n"
5470 "@type filename: string\n"
5471 "@param filename: local file path to the document\n"
5472 "@type title: string\n"
5473 "@param title: title of the print job\n"
5474 "@type options: dict\n"
5475 "@param options: dict of options\n"
5476 "@return: job ID\n"
5477 "@raise IPPError: IPP problem" },
5478
5479 { "printFiles",
5480 (PyCFunction) Connection_printFiles, METH_VARARGS | METH_KEYWORDS,
5481 "printFiles(printer, filenames, title, options) -> integer\n\n"
5482 "Print a list of files.\n\n"
5483 "@type printer: string\n"
5484 "@param printer: queue name\n"
5485 "@type filenames: list\n"
5486 "@param filenames: list of local file paths to the documents\n"
5487 "@type title: string\n"
5488 "@param title: title of the print job\n"
5489 "@type options: dict\n"
5490 "@param options: dict of options\n"
5491 "@return: job ID\n"
5492 "@raise IPPError: IPP problem" },
5493
5494 { NULL } /* Sentinel */
5495 };
5496
5497 PyTypeObject cups_ConnectionType =
5498 {
5499 PyVarObject_HEAD_INIT(NULL, 0)
5500 "cups.Connection", /*tp_name*/
5501 sizeof(Connection), /*tp_basicsize*/
5502 0, /*tp_itemsize*/
5503 (destructor)Connection_dealloc, /*tp_dealloc*/
5504 0, /*tp_print*/
5505 0, /*tp_getattr*/
5506 0, /*tp_setattr*/
5507 0, /*tp_compare*/
5508 (reprfunc)Connection_repr, /*tp_repr*/
5509 0, /*tp_as_number*/
5510 0, /*tp_as_sequence*/
5511 0, /*tp_as_mapping*/
5512 0, /*tp_hash */
5513 0, /*tp_call*/
5514 0, /*tp_str*/
5515 0, /*tp_getattro*/
5516 0, /*tp_setattro*/
5517 0, /*tp_as_buffer*/
5518 Py_TPFLAGS_DEFAULT |
5519 Py_TPFLAGS_BASETYPE, /*tp_flags*/
5520 "CUPS connection\n"
5521 "===============\n\n"
5522
5523 " A connection to the CUPS server. Before it is created the \n"
5524 " connection server and username should be set using \n"
5525 " L{cups.setServer} and L{cups.setUser}; otherwise the defaults will \n"
5526 " be used. When a Connection object is instantiated it results in a \n"
5527 " call to the libcups function httpConnectEncrypt().\n\n"
5528 " The constructor takes optional arguments host, port, and encryption, \n"
5529 " which default to the values of L{cups.getServer}(), \n"
5530 " L{cups.getPort}(), and L{cups.getEncryption}().\n"
5531 "", /* tp_doc */
5532 0, /* tp_traverse */
5533 0, /* tp_clear */
5534 0, /* tp_richcompare */
5535 0, /* tp_weaklistoffset */
5536 0, /* tp_iter */
5537 0, /* tp_iternext */
5538 Connection_methods, /* tp_methods */
5539 0, /* tp_members */
5540 0, /* tp_getset */
5541 0, /* tp_base */
5542 0, /* tp_dict */
5543 0, /* tp_descr_get */
5544 0, /* tp_descr_set */
5545 0, /* tp_dictoffset */
5546 (initproc)Connection_init, /* tp_init */
5547 0, /* tp_alloc */
5548 Connection_new, /* tp_new */
5549 };
5550
5551 //////////
5552 // Dest //
5553 //////////
5554
5555 static PyObject *
Dest_new(PyTypeObject * type,PyObject * args,PyObject * kwds)5556 Dest_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
5557 {
5558 Dest *self;
5559 self = (Dest *) type->tp_alloc (type, 0);
5560 return (PyObject *) self;
5561 }
5562
5563 static int
Dest_init(Dest * self,PyObject * args,PyObject * kwds)5564 Dest_init (Dest *self, PyObject *args, PyObject *kwds)
5565 {
5566 self->num_options = 0;
5567 return 0;
5568 }
5569
5570 static void
Dest_dealloc(Dest * self)5571 Dest_dealloc (Dest *self)
5572 {
5573 if (self->num_options) {
5574 int i;
5575 for (i = 0; i < self->num_options; i++) {
5576 free (self->name[i]);
5577 free (self->value[i]);
5578 }
5579
5580 free (self->name);
5581 free (self->value);
5582 self->num_options = 0;
5583
5584 free (self->destname);
5585 free (self->instance);
5586 }
5587 ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
5588 }
5589
5590 static PyObject *
Dest_repr(Dest * self)5591 Dest_repr (Dest *self)
5592 {
5593 char buffer[256];
5594 snprintf (buffer, 256, "<cups.Dest %s%s%s%s>",
5595 self->destname,
5596 self->instance ? "/" : "",
5597 self->instance ? self->instance : "",
5598 self->is_default ? " (default)" : "");
5599 return PyUnicode_FromString (buffer);
5600 }
5601
5602 //////////
5603 // Dest // ATTRIBUTES
5604 //////////
5605
5606 static PyObject *
Dest_getName(Dest * self,void * closure)5607 Dest_getName (Dest *self, void *closure)
5608 {
5609 return PyUnicode_FromString (self->destname);
5610 }
5611
5612 static PyObject *
Dest_getInstance(Dest * self,void * closure)5613 Dest_getInstance (Dest *self, void *closure)
5614 {
5615 if (self->instance)
5616 return PyUnicode_FromString (self->instance);
5617
5618 Py_RETURN_NONE;
5619 }
5620
5621 static PyObject *
Dest_getIsDefault(Dest * self,void * closure)5622 Dest_getIsDefault (Dest *self, void *closure)
5623 {
5624 return PyBool_FromLong (self->is_default);
5625 }
5626
5627 static PyObject *
Dest_getOptions(Dest * self,void * closure)5628 Dest_getOptions (Dest *self, void *closure)
5629 {
5630 PyObject *pyoptions = PyDict_New ();
5631 int i;
5632 for (i = 0; i < self->num_options; i++) {
5633 PyObject *string = PyUnicode_FromString (self->value[i]);
5634 PyDict_SetItemString (pyoptions, self->name[i], string);
5635 Py_DECREF (string);
5636 }
5637
5638 return pyoptions;
5639 }
5640
5641 PyGetSetDef Dest_getseters[] =
5642 {
5643 { "name",
5644 (getter) Dest_getName, (setter) NULL,
5645 "name", NULL },
5646
5647 { "instance",
5648 (getter) Dest_getInstance, (setter) NULL,
5649 "instance", NULL },
5650
5651 { "is_default",
5652 (getter) Dest_getIsDefault, (setter) NULL,
5653 "is_default", NULL },
5654
5655 { "options",
5656 (getter) Dest_getOptions, (setter) NULL,
5657 "options", NULL },
5658
5659 { NULL }
5660 };
5661
5662 PyTypeObject cups_DestType =
5663 {
5664 PyVarObject_HEAD_INIT(NULL, 0)
5665 "cups.Dest", /*tp_name*/
5666 sizeof(Dest), /*tp_basicsize*/
5667 0, /*tp_itemsize*/
5668 (destructor)Dest_dealloc, /*tp_dealloc*/
5669 0, /*tp_print*/
5670 0, /*tp_getattr*/
5671 0, /*tp_setattr*/
5672 0, /*tp_compare*/
5673 (reprfunc)Dest_repr, /*tp_repr*/
5674 0, /*tp_as_number*/
5675 0, /*tp_as_sequence*/
5676 0, /*tp_as_mapping*/
5677 0, /*tp_hash */
5678 0, /*tp_call*/
5679 0, /*tp_str*/
5680 0, /*tp_getattro*/
5681 0, /*tp_setattro*/
5682 0, /*tp_as_buffer*/
5683 Py_TPFLAGS_DEFAULT, /*tp_flags*/
5684 "CUPS destination\n"
5685 "================\n\n"
5686 " A destination print queue, as returned by L{Connection.getDests}.\n\n"
5687 "@type name: string\n"
5688 "@ivar name: destination queue name\n"
5689 "@type instance: string\n"
5690 "@ivar instance: destination instance name\n"
5691 "@type is_default: boolean\n"
5692 "@ivar is_default: whether this is the default destination\n"
5693 "@type options: dict\n"
5694 "@ivar options: string:string dict of default options for this \n"
5695 "destination, indexed by option name\n"
5696 "", /* tp_doc */
5697 0, /* tp_traverse */
5698 0, /* tp_clear */
5699 0, /* tp_richcompare */
5700 0, /* tp_weaklistoffset */
5701 0, /* tp_iter */
5702 0, /* tp_iternext */
5703 0, /* tp_methods */
5704 0, /* tp_members */
5705 Dest_getseters, /* tp_getset */
5706 0, /* tp_base */
5707 0, /* tp_dict */
5708 0, /* tp_descr_get */
5709 0, /* tp_descr_set */
5710 0, /* tp_dictoffset */
5711 (initproc)Dest_init, /* tp_init */
5712 0, /* tp_alloc */
5713 Dest_new, /* tp_new */
5714 };
5715