1*2264Sjacobs /*
2*2264Sjacobs  * CDDL HEADER START
3*2264Sjacobs  *
4*2264Sjacobs  * The contents of this file are subject to the terms of the
5*2264Sjacobs  * Common Development and Distribution License (the "License").
6*2264Sjacobs  * You may not use this file except in compliance with the License.
7*2264Sjacobs  *
8*2264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2264Sjacobs  * or http://www.opensolaris.org/os/licensing.
10*2264Sjacobs  * See the License for the specific language governing permissions
11*2264Sjacobs  * and limitations under the License.
12*2264Sjacobs  *
13*2264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
14*2264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
16*2264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
17*2264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2264Sjacobs  *
19*2264Sjacobs  * CDDL HEADER END
20*2264Sjacobs  */
21*2264Sjacobs 
22*2264Sjacobs /*
23*2264Sjacobs  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*2264Sjacobs  * Use is subject to license terms.
25*2264Sjacobs  *
26*2264Sjacobs  */
27*2264Sjacobs 
28*2264Sjacobs /* $Id: ipp-support.c 148 2006-04-25 16:54:17Z njacobs $ */
29*2264Sjacobs 
30*2264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*2264Sjacobs 
32*2264Sjacobs #include <papi_impl.h>
33*2264Sjacobs #include <stdlib.h>
34*2264Sjacobs #include <pwd.h>
35*2264Sjacobs #include <locale.h>
36*2264Sjacobs #include <errno.h>
37*2264Sjacobs #include <fcntl.h>
38*2264Sjacobs #include <sys/stat.h>
39*2264Sjacobs #include <md5.h>
40*2264Sjacobs 
41*2264Sjacobs #include <config-site.h>
42*2264Sjacobs 
43*2264Sjacobs papi_status_t
44*2264Sjacobs http_to_papi_status(http_status_t status)
45*2264Sjacobs {
46*2264Sjacobs 	switch (status) {
47*2264Sjacobs 	case HTTP_OK:
48*2264Sjacobs 		return (PAPI_OK);
49*2264Sjacobs 	case HTTP_BAD_REQUEST:
50*2264Sjacobs 		return (PAPI_BAD_REQUEST);
51*2264Sjacobs 	case HTTP_UNAUTHORIZED:
52*2264Sjacobs 	case HTTP_FORBIDDEN:
53*2264Sjacobs 		return (PAPI_NOT_AUTHORIZED);
54*2264Sjacobs 	case HTTP_NOT_FOUND:
55*2264Sjacobs 		return (PAPI_NOT_FOUND);
56*2264Sjacobs 	case HTTP_GONE:
57*2264Sjacobs 		return (PAPI_GONE);
58*2264Sjacobs 	case HTTP_SERVICE_UNAVAILABLE:
59*2264Sjacobs 		return (PAPI_SERVICE_UNAVAILABLE);
60*2264Sjacobs 	default:
61*2264Sjacobs 		return ((papi_status_t)status);
62*2264Sjacobs 	}
63*2264Sjacobs }
64*2264Sjacobs 
65*2264Sjacobs papi_status_t
66*2264Sjacobs ipp_to_papi_status(uint16_t status)
67*2264Sjacobs {
68*2264Sjacobs 	switch (status) {
69*2264Sjacobs 	case IPP_OK:
70*2264Sjacobs 		return (PAPI_OK);
71*2264Sjacobs 	case IPP_OK_IGNORED_ATTRIBUTES:
72*2264Sjacobs 		return (PAPI_OK);
73*2264Sjacobs 	case IPP_OK_CONFLICTING_ATTRIBUTES:
74*2264Sjacobs 		return (PAPI_OK);
75*2264Sjacobs 	case IPP_OK_IGNORED_SUBSCRIPTIONS:
76*2264Sjacobs 		return (PAPI_OK_IGNORED_SUBSCRIPTIONS);
77*2264Sjacobs 	case IPP_OK_IGNORED_NOTIFICATIONS:
78*2264Sjacobs 		return (PAPI_OK_IGNORED_NOTIFICATIONS);
79*2264Sjacobs 	case IPP_CERR_BAD_REQUEST:
80*2264Sjacobs 		return (PAPI_BAD_REQUEST);
81*2264Sjacobs 	case IPP_CERR_FORBIDDEN:
82*2264Sjacobs 		return (PAPI_FORBIDDEN);
83*2264Sjacobs 	case IPP_CERR_NOT_AUTHENTICATED:
84*2264Sjacobs 		return (PAPI_NOT_AUTHENTICATED);
85*2264Sjacobs 	case IPP_CERR_NOT_AUTHORIZED:
86*2264Sjacobs 		return (PAPI_NOT_AUTHORIZED);
87*2264Sjacobs 	case IPP_CERR_NOT_POSSIBLE:
88*2264Sjacobs 		return (PAPI_NOT_POSSIBLE);
89*2264Sjacobs 	case IPP_CERR_TIMEOUT:
90*2264Sjacobs 		return (PAPI_TIMEOUT);
91*2264Sjacobs 	case IPP_CERR_NOT_FOUND:
92*2264Sjacobs 		return (PAPI_NOT_FOUND);
93*2264Sjacobs 	case IPP_CERR_GONE:
94*2264Sjacobs 		return (PAPI_GONE);
95*2264Sjacobs 	case IPP_CERR_REQUEST_ENTITY:
96*2264Sjacobs 		return (PAPI_REQUEST_ENTITY);
97*2264Sjacobs 	case IPP_CERR_REQUEST_VALUE:
98*2264Sjacobs 		return (PAPI_REQUEST_VALUE);
99*2264Sjacobs 	case IPP_CERR_DOCUMENT_FORMAT:
100*2264Sjacobs 		return (PAPI_DOCUMENT_FORMAT);
101*2264Sjacobs 	case IPP_CERR_ATTRIBUTES:
102*2264Sjacobs 		return (PAPI_ATTRIBUTES);
103*2264Sjacobs 	case IPP_CERR_URI_SCHEME:
104*2264Sjacobs 		return (PAPI_URI_SCHEME);
105*2264Sjacobs 	case IPP_CERR_CHARSET:
106*2264Sjacobs 		return (PAPI_CHARSET);
107*2264Sjacobs 	case IPP_CERR_CONFLICT:
108*2264Sjacobs 		return (PAPI_CONFLICT);
109*2264Sjacobs 	case IPP_CERR_COMPRESSION_NOT_SUPPORTED:
110*2264Sjacobs 		return (PAPI_COMPRESSION_NOT_SUPPORTED);
111*2264Sjacobs 	case IPP_CERR_COMPRESSION_ERROR:
112*2264Sjacobs 		return (PAPI_COMPRESSION_ERROR);
113*2264Sjacobs 	case IPP_CERR_DOCUMENT_FORMAT_ERROR:
114*2264Sjacobs 		return (PAPI_DOCUMENT_FORMAT_ERROR);
115*2264Sjacobs 	case IPP_CERR_DOCUMENT_ACCESS_ERROR:
116*2264Sjacobs 		return (PAPI_DOCUMENT_ACCESS_ERROR);
117*2264Sjacobs 	case IPP_CERR_ATTRIBUTES_NOT_SETTABLE:
118*2264Sjacobs 		return (PAPI_ATTRIBUTES_NOT_SETTABLE);
119*2264Sjacobs 	case IPP_CERR_IGNORED_ALL_SUBSCRIPTIONS:
120*2264Sjacobs 		return (PAPI_IGNORED_ALL_SUBSCRIPTIONS);
121*2264Sjacobs 	case IPP_CERR_TOO_MANY_SUBSCRIPTIONS:
122*2264Sjacobs 		return (PAPI_TOO_MANY_SUBSCRIPTIONS);
123*2264Sjacobs 	case IPP_CERR_IGNORED_ALL_NOTIFICATIONS:
124*2264Sjacobs 		return (PAPI_IGNORED_ALL_NOTIFICATIONS);
125*2264Sjacobs 	case IPP_CERR_PRINT_SUPPORT_FILE_NOT_FOUND:
126*2264Sjacobs 		return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND);
127*2264Sjacobs 	case IPP_SERR_INTERNAL:
128*2264Sjacobs 		return (PAPI_INTERNAL_ERROR);
129*2264Sjacobs 	case IPP_SERR_OPERATION_NOT_SUPPORTED:
130*2264Sjacobs 		return (PAPI_OPERATION_NOT_SUPPORTED);
131*2264Sjacobs 	case IPP_SERR_SERVICE_UNAVAILABLE:
132*2264Sjacobs 		return (PAPI_SERVICE_UNAVAILABLE);
133*2264Sjacobs 	case IPP_SERR_VERSION_NOT_SUPPORTED:
134*2264Sjacobs 		return (PAPI_VERSION_NOT_SUPPORTED);
135*2264Sjacobs 	case IPP_SERR_DEVICE_ERROR:
136*2264Sjacobs 		return (PAPI_DEVICE_ERROR);
137*2264Sjacobs 	case IPP_SERR_TEMPORARY_ERROR:
138*2264Sjacobs 		return (PAPI_TEMPORARY_ERROR);
139*2264Sjacobs 	case IPP_SERR_NOT_ACCEPTING:
140*2264Sjacobs 		return (PAPI_NOT_ACCEPTING);
141*2264Sjacobs 	case IPP_SERR_BUSY:
142*2264Sjacobs 	case IPP_SERR_CANCELLED:
143*2264Sjacobs 	default:
144*2264Sjacobs 		return (PAPI_TEMPORARY_ERROR);
145*2264Sjacobs 	}
146*2264Sjacobs }
147*2264Sjacobs 
148*2264Sjacobs void
149*2264Sjacobs ipp_initialize_request(service_t *svc, papi_attribute_t ***request,
150*2264Sjacobs 		uint16_t operation)
151*2264Sjacobs {
152*2264Sjacobs 	papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
153*2264Sjacobs 			"version-major", 1);
154*2264Sjacobs 	papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
155*2264Sjacobs 			"version-minor", 1);
156*2264Sjacobs 	papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
157*2264Sjacobs 			"request-id", (short)lrand48());
158*2264Sjacobs 	papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
159*2264Sjacobs 			"operation-id", operation);
160*2264Sjacobs }
161*2264Sjacobs 
162*2264Sjacobs void
163*2264Sjacobs ipp_initialize_operational_attributes(service_t *svc, papi_attribute_t ***op,
164*2264Sjacobs 		papi_attribute_t **attributes)
165*2264Sjacobs {
166*2264Sjacobs 	char *charset = "utf-8"; /* default to UTF-8 encoding */
167*2264Sjacobs 	char *language = setlocale(LC_ALL, "");
168*2264Sjacobs 	char *user = "nobody";
169*2264Sjacobs 	struct passwd *pw = NULL;
170*2264Sjacobs 
171*2264Sjacobs 	/*
172*2264Sjacobs 	 * All IPP requests must contain the following:
173*2264Sjacobs 	 * 	attributes-charset		(UTF-8)
174*2264Sjacobs 	 *	attributes-natural-language	(our current locale)
175*2264Sjacobs 	 *	requesting-user-name		(process user)
176*2264Sjacobs 	 */
177*2264Sjacobs 	papiAttributeListGetString(attributes, NULL,
178*2264Sjacobs 			"attributes-charset", &charset);
179*2264Sjacobs 	papiAttributeListAddString(op, PAPI_ATTR_EXCL,
180*2264Sjacobs 			"attributes-charset", charset);
181*2264Sjacobs 
182*2264Sjacobs 	papiAttributeListGetString(attributes, NULL,
183*2264Sjacobs 			"attributes-natural-language", &language);
184*2264Sjacobs 	papiAttributeListAddString(op, PAPI_ATTR_EXCL,
185*2264Sjacobs 			"attributes-natural-language", language);
186*2264Sjacobs 
187*2264Sjacobs 	if ((pw = getpwuid(getuid())) != NULL)
188*2264Sjacobs 		user = pw->pw_name;
189*2264Sjacobs 	/*
190*2264Sjacobs 	 * if our euid is 0 "super user", we will allow the system supplied
191*2264Sjacobs 	 * user name to be overridden, if the requestor wants to.
192*2264Sjacobs 	 */
193*2264Sjacobs 	if (geteuid() == 0) {
194*2264Sjacobs 		if (svc->user != NULL)
195*2264Sjacobs 			user = svc->user;
196*2264Sjacobs 		papiAttributeListGetString(attributes, NULL,
197*2264Sjacobs 				"requesting-user-name", &user);
198*2264Sjacobs 	}
199*2264Sjacobs 	papiAttributeListAddString(op, PAPI_ATTR_REPLACE,
200*2264Sjacobs 			"requesting-user-name", user);
201*2264Sjacobs }
202*2264Sjacobs 
203*2264Sjacobs #ifndef OPID_CUPS_GET_DEFAULT	   /* for servers that will enumerate */
204*2264Sjacobs #define	OPID_CUPS_GET_DEFAULT	   0x4001
205*2264Sjacobs #endif  /* OPID_CUPS_GET_DEFAULT */
206*2264Sjacobs 
207*2264Sjacobs static papi_status_t
208*2264Sjacobs _default_destination(service_t *svc, char **uri)
209*2264Sjacobs {
210*2264Sjacobs 	papi_status_t result = PAPI_INTERNAL_ERROR;
211*2264Sjacobs 	printer_t *p = NULL;
212*2264Sjacobs 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
213*2264Sjacobs 	char *tmp = NULL;
214*2264Sjacobs 
215*2264Sjacobs 	if ((svc == NULL) || (uri == NULL))
216*2264Sjacobs 		return (PAPI_BAD_ARGUMENT);
217*2264Sjacobs 
218*2264Sjacobs 	/* we must be connected to find the default destination */
219*2264Sjacobs 	if (svc->connection == NULL)
220*2264Sjacobs 		return (PAPI_NOT_POSSIBLE);
221*2264Sjacobs 
222*2264Sjacobs 	if ((p = calloc(1, sizeof (*p))) == NULL)
223*2264Sjacobs 		return (PAPI_TEMPORARY_ERROR);
224*2264Sjacobs 
225*2264Sjacobs 	ipp_initialize_request(svc, &request, OPID_CUPS_GET_DEFAULT);
226*2264Sjacobs 	ipp_initialize_operational_attributes(svc, &op, NULL);
227*2264Sjacobs 	papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
228*2264Sjacobs 			"requested-attributes", "printer-uri-supported");
229*2264Sjacobs 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
230*2264Sjacobs 			"operational-attributes-group", op);
231*2264Sjacobs 	papiAttributeListFree(op);
232*2264Sjacobs 	result = ipp_send_request(svc, request, &response);
233*2264Sjacobs 	papiAttributeListFree(request);
234*2264Sjacobs 
235*2264Sjacobs 	op = NULL;
236*2264Sjacobs 	papiAttributeListGetCollection(response, NULL,
237*2264Sjacobs 			"printer-attributes-group", &op);
238*2264Sjacobs 
239*2264Sjacobs 	if (uri != NULL) {
240*2264Sjacobs 		char *tmp = NULL;
241*2264Sjacobs 
242*2264Sjacobs 		papiAttributeListGetString(op, NULL, "printer-uri", &tmp);
243*2264Sjacobs 		papiAttributeListGetString(op, NULL,
244*2264Sjacobs 					"printer-uri-supported", &tmp);
245*2264Sjacobs 		if (tmp != NULL)
246*2264Sjacobs 			*uri = strdup(tmp);
247*2264Sjacobs 	}
248*2264Sjacobs 
249*2264Sjacobs 	papiAttributeListFree(response);
250*2264Sjacobs 
251*2264Sjacobs 	return (result);
252*2264Sjacobs }
253*2264Sjacobs 
254*2264Sjacobs void
255*2264Sjacobs ipp_add_printer_uri(service_t *svc, char *name, papi_attribute_t ***op)
256*2264Sjacobs {
257*2264Sjacobs 	char *uri = name;
258*2264Sjacobs 	char buf[BUFSIZ];
259*2264Sjacobs 	uri_t *tmp = NULL;
260*2264Sjacobs 
261*2264Sjacobs 	if (strstr(name, "://") == NULL) { /* not in URI form */
262*2264Sjacobs 		if (strcmp(name, DEFAULT_DEST) != 0) {
263*2264Sjacobs 			/* not the "default" */
264*2264Sjacobs 			snprintf(buf, sizeof (buf), "%s/%s", svc->name, name);
265*2264Sjacobs 			uri = buf;
266*2264Sjacobs 		} else
267*2264Sjacobs 			_default_destination(svc, &uri);
268*2264Sjacobs 	}
269*2264Sjacobs 
270*2264Sjacobs 	papiAttributeListAddString(op, PAPI_ATTR_EXCL, "printer-uri", uri);
271*2264Sjacobs 
272*2264Sjacobs 	/* save the printer-uri's path to be used by http POST request */
273*2264Sjacobs 	if ((uri_from_string(uri, &tmp) == 0) && (tmp != NULL)) {
274*2264Sjacobs 		if (svc->post != NULL)
275*2264Sjacobs 			free(svc->post);
276*2264Sjacobs 		svc->post = strdup(tmp->path);
277*2264Sjacobs 		uri_free(tmp);
278*2264Sjacobs 	}
279*2264Sjacobs }
280*2264Sjacobs 
281*2264Sjacobs 
282*2264Sjacobs /*
283*2264Sjacobs  * don't actually write anything, just add to the total size and return the
284*2264Sjacobs  * size of what would be written, so we can figure out how big the request
285*2264Sjacobs  * is going to be.
286*2264Sjacobs  */
287*2264Sjacobs static ssize_t
288*2264Sjacobs size_calculate(void *fd, void *buffer, size_t length)
289*2264Sjacobs {
290*2264Sjacobs 	ssize_t *size = (ssize_t *)fd;
291*2264Sjacobs 
292*2264Sjacobs 	*size += length;
293*2264Sjacobs 	return (length);
294*2264Sjacobs }
295*2264Sjacobs 
296*2264Sjacobs 
297*2264Sjacobs static ssize_t
298*2264Sjacobs build_chunk(void *fd, void *buffer, size_t length)
299*2264Sjacobs {
300*2264Sjacobs 	char **s1 = fd;
301*2264Sjacobs 
302*2264Sjacobs 	memcpy(*s1, buffer, length);
303*2264Sjacobs 	*s1 = *s1 + length;
304*2264Sjacobs 
305*2264Sjacobs 	return (length);
306*2264Sjacobs }
307*2264Sjacobs 
308*2264Sjacobs ssize_t
309*2264Sjacobs ipp_request_write(void *fd, void *buffer, size_t length)
310*2264Sjacobs {
311*2264Sjacobs 	service_t *svc = (service_t *)fd;
312*2264Sjacobs 
313*2264Sjacobs #ifdef DEBUG
314*2264Sjacobs 	printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd, buffer, length);
315*2264Sjacobs 	httpDumpData(stdout, "ipp_request_write:", buffer, length);
316*2264Sjacobs #endif
317*2264Sjacobs 	return (httpWrite(svc->connection, buffer, length));
318*2264Sjacobs }
319*2264Sjacobs 
320*2264Sjacobs ssize_t
321*2264Sjacobs ipp_request_read(void *fd, void *buffer, size_t length)
322*2264Sjacobs {
323*2264Sjacobs 	service_t *svc = (service_t *)fd;
324*2264Sjacobs 	ssize_t rc, i = length;
325*2264Sjacobs 	char *p = buffer;
326*2264Sjacobs 
327*2264Sjacobs 	while ((rc = httpRead(svc->connection, p, i)) != i) {
328*2264Sjacobs 		if (rc == 0)
329*2264Sjacobs 			return (rc);
330*2264Sjacobs 		if (rc < 0)
331*2264Sjacobs 			return (rc);
332*2264Sjacobs 		i -= rc;
333*2264Sjacobs 		p += rc;
334*2264Sjacobs 	}
335*2264Sjacobs #ifdef DEBUG
336*2264Sjacobs 	printf("ipp_request_read(0x%8.8x, 0x%8.8x, %d) = %d\n",
337*2264Sjacobs 			fd, buffer, length, rc);
338*2264Sjacobs 	httpDumpData(stdout, "ipp_request_read:", buffer, length);
339*2264Sjacobs #endif
340*2264Sjacobs 
341*2264Sjacobs 	return (length);
342*2264Sjacobs }
343*2264Sjacobs 
344*2264Sjacobs papi_status_t
345*2264Sjacobs ipp_send_initial_request_block(service_t *svc, papi_attribute_t **request,
346*2264Sjacobs 		ssize_t file_size)
347*2264Sjacobs {
348*2264Sjacobs 	papi_status_t result = PAPI_OK;
349*2264Sjacobs 	ssize_t chunk_size = 0;
350*2264Sjacobs 	char length[32];
351*2264Sjacobs 	void *chunk, *ptr;
352*2264Sjacobs 	http_status_t status;
353*2264Sjacobs 
354*2264Sjacobs 	/* calculate the request size */
355*2264Sjacobs 	(void) ipp_write_message(&size_calculate, &chunk_size, request);
356*2264Sjacobs 
357*2264Sjacobs 	/* Fill in the HTTP Header information */
358*2264Sjacobs 	httpClearFields(svc->connection);
359*2264Sjacobs 	if (svc->transfer_encoding == TRANSFER_ENCODING_CHUNKED)
360*2264Sjacobs 		httpSetField(svc->connection, HTTP_FIELD_TRANSFER_ENCODING,
361*2264Sjacobs 			"chunked");
362*2264Sjacobs 	else {
363*2264Sjacobs 		sprintf(length, "%lu", (unsigned long)(file_size + chunk_size));
364*2264Sjacobs 		httpSetField(svc->connection, HTTP_FIELD_CONTENT_LENGTH,
365*2264Sjacobs 			length);
366*2264Sjacobs 	}
367*2264Sjacobs 	httpSetField(svc->connection, HTTP_FIELD_CONTENT_TYPE,
368*2264Sjacobs 			"application/ipp");
369*2264Sjacobs 	httpSetField(svc->connection, HTTP_FIELD_AUTHORIZATION,
370*2264Sjacobs 			svc->connection->authstring);
371*2264Sjacobs 
372*2264Sjacobs 	/* flush any state information about this connection */
373*2264Sjacobs 	httpFlush(svc->connection);
374*2264Sjacobs 
375*2264Sjacobs 	/* if we have don't have a POST path, use the service uri path */
376*2264Sjacobs 	if (svc->post == NULL)
377*2264Sjacobs 		svc->post = strdup(svc->uri->path);
378*2264Sjacobs 	/* send the HTTP POST message for the IPP request */
379*2264Sjacobs 	/* if the POST fails, return the error */
380*2264Sjacobs 	status = httpPost(svc->connection, svc->post);
381*2264Sjacobs 	if (status != 0)
382*2264Sjacobs 		return (http_to_papi_status(status));
383*2264Sjacobs 
384*2264Sjacobs 	if (httpCheck(svc->connection) != 0) {
385*2264Sjacobs 		status = httpUpdate(svc->connection);
386*2264Sjacobs 		if (status != HTTP_OK)
387*2264Sjacobs 			return (http_to_papi_status(status));
388*2264Sjacobs 	}
389*2264Sjacobs 
390*2264Sjacobs 	/* build the request chunk */
391*2264Sjacobs 	chunk = ptr = calloc(1, chunk_size);
392*2264Sjacobs 	result = ipp_write_message(&build_chunk, &ptr, request);
393*2264Sjacobs #ifdef DEBUG
394*2264Sjacobs 	printf("request: %d (0x%x) bytes\n", chunk_size, chunk_size);
395*2264Sjacobs 	httpDumpData(stdout, "request:", chunk, chunk_size);
396*2264Sjacobs #endif
397*2264Sjacobs 
398*2264Sjacobs 	/* send the actual IPP request */
399*2264Sjacobs 	if (ipp_request_write(svc, chunk, chunk_size) != chunk_size)
400*2264Sjacobs 		result = PAPI_TEMPORARY_ERROR;
401*2264Sjacobs 	free(chunk);
402*2264Sjacobs 
403*2264Sjacobs 	if (httpCheck(svc->connection) != 0) {
404*2264Sjacobs 		status = httpUpdate(svc->connection);
405*2264Sjacobs 		if (status != HTTP_OK)
406*2264Sjacobs 			return (http_to_papi_status(status));
407*2264Sjacobs 	}
408*2264Sjacobs 
409*2264Sjacobs 	return (result);
410*2264Sjacobs }
411*2264Sjacobs 
412*2264Sjacobs static int
413*2264Sjacobs setAuthString(service_t *svc)
414*2264Sjacobs {
415*2264Sjacobs 	http_t *http;
416*2264Sjacobs 	char *user, *passphrase;
417*2264Sjacobs 	char encoded[BUFSIZ];
418*2264Sjacobs 
419*2264Sjacobs 	if ((svc == NULL) || (svc->connection == NULL) || (svc->name == NULL))
420*2264Sjacobs 		return (-1);
421*2264Sjacobs 
422*2264Sjacobs 	http = svc->connection;
423*2264Sjacobs 
424*2264Sjacobs 	if (svc->user == NULL) {
425*2264Sjacobs 		struct passwd *p;
426*2264Sjacobs 
427*2264Sjacobs 		if ((p = getpwuid(getuid())) != NULL) {
428*2264Sjacobs 			user = p->pw_name;
429*2264Sjacobs 		} else if ((user = getenv("LOGNAME")) == NULL)
430*2264Sjacobs 			user = getenv("USER");
431*2264Sjacobs 		if (user == NULL)
432*2264Sjacobs 			user = "nobody";
433*2264Sjacobs 	} else
434*2264Sjacobs 		user = svc->user;
435*2264Sjacobs 
436*2264Sjacobs 	/* if the passphrase is not set, use the Authentication Callback */
437*2264Sjacobs 	if (((svc->password == NULL) || (svc->password[0] == '\0')) &&
438*2264Sjacobs 	    (svc->authCB != NULL))
439*2264Sjacobs 		(svc->authCB)(svc, svc->app_data);
440*2264Sjacobs 	passphrase = svc->password;
441*2264Sjacobs 
442*2264Sjacobs 	/* if there is still no passphrase, we have to fail */
443*2264Sjacobs 	if ((passphrase == NULL) || (passphrase[0] == '\0'))
444*2264Sjacobs 		return (-1);
445*2264Sjacobs 
446*2264Sjacobs 	if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
447*2264Sjacobs 			"Basic", 5) == 0) {
448*2264Sjacobs 		char plain[BUFSIZ];
449*2264Sjacobs 
450*2264Sjacobs 		snprintf(plain, sizeof (plain), "%s:%s", user, passphrase);
451*2264Sjacobs 		httpEncode64(encoded, plain);
452*2264Sjacobs 		snprintf(http->authstring, sizeof (http->authstring),
453*2264Sjacobs 			"Basic %s", encoded);
454*2264Sjacobs 	} else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
455*2264Sjacobs 			"Digest", 6) == 0) {
456*2264Sjacobs 		char realm[HTTP_MAX_VALUE];
457*2264Sjacobs 		char nonce[HTTP_MAX_VALUE];
458*2264Sjacobs 		char line [BUFSIZ];
459*2264Sjacobs 		char urp[128];
460*2264Sjacobs 		char mr[128];
461*2264Sjacobs 		char *uri = svc->post;
462*2264Sjacobs 
463*2264Sjacobs 		httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
464*2264Sjacobs 				"realm", realm);
465*2264Sjacobs 		httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
466*2264Sjacobs 				"nonce", nonce);
467*2264Sjacobs 
468*2264Sjacobs 		snprintf(line, sizeof (line), "%s:%s:%s", user, realm,
469*2264Sjacobs 				passphrase);
470*2264Sjacobs 		md5_calc(urp, line, strlen(line));
471*2264Sjacobs 
472*2264Sjacobs 		snprintf(line, sizeof (line), "POST:%s", uri);
473*2264Sjacobs 		md5_calc(mr, line, strlen(line));
474*2264Sjacobs 
475*2264Sjacobs 		snprintf(line, sizeof (line), "%s:%s:%s", urp, mr, nonce);
476*2264Sjacobs 		md5_calc(encoded, line, strlen(line));
477*2264Sjacobs 
478*2264Sjacobs 		snprintf(http->authstring, sizeof (http->authstring),
479*2264Sjacobs 			"Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
480*2264Sjacobs 			"uri=\"%s\", response=\"%s\"", user, realm, nonce, uri,
481*2264Sjacobs 			encoded);
482*2264Sjacobs 	}
483*2264Sjacobs 
484*2264Sjacobs 	return (0);
485*2264Sjacobs }
486*2264Sjacobs 
487*2264Sjacobs papi_status_t
488*2264Sjacobs ipp_status_info(service_t *svc, papi_attribute_t **response)
489*2264Sjacobs {
490*2264Sjacobs 	papi_attribute_t **operational = NULL;
491*2264Sjacobs 	int32_t status = 0;
492*2264Sjacobs 
493*2264Sjacobs 	papiAttributeListGetCollection(response, NULL,
494*2264Sjacobs 			"operational-attributes-group", &operational);
495*2264Sjacobs 	if (operational != NULL) {
496*2264Sjacobs 		char *message = NULL;
497*2264Sjacobs 
498*2264Sjacobs 		papiAttributeListGetString(response, NULL,
499*2264Sjacobs 					"status-message", &message);
500*2264Sjacobs 		papiAttributeListAddString(&svc->attributes, PAPI_ATTR_REPLACE,
501*2264Sjacobs 					"detailed-status-message", message);
502*2264Sjacobs 	}
503*2264Sjacobs 	papiAttributeListGetInteger(response, NULL, "status-code", &status);
504*2264Sjacobs 
505*2264Sjacobs 	return (ipp_to_papi_status(status));
506*2264Sjacobs }
507*2264Sjacobs 
508*2264Sjacobs papi_status_t
509*2264Sjacobs ipp_send_request_with_file(service_t *svc, papi_attribute_t **request,
510*2264Sjacobs 		papi_attribute_t ***response, char *file)
511*2264Sjacobs {
512*2264Sjacobs 	papi_status_t result = PAPI_OK;
513*2264Sjacobs 	ssize_t size = 0;
514*2264Sjacobs 	int fd;
515*2264Sjacobs 
516*2264Sjacobs #ifdef DEBUG
517*2264Sjacobs 	fprintf(stderr, "\nIPP-REQUEST: (%s)", (file ? file : ""));
518*2264Sjacobs 	papiAttributeListPrint(stderr, request, "    ");
519*2264Sjacobs 	putc('\n', stderr);
520*2264Sjacobs 	fflush(stderr);
521*2264Sjacobs #endif
522*2264Sjacobs 
523*2264Sjacobs 	/*
524*2264Sjacobs 	 * if we are sending a file, open it and include it's size in the
525*2264Sjacobs 	 * message size.
526*2264Sjacobs 	 */
527*2264Sjacobs 	if (file != NULL) {
528*2264Sjacobs 		if ((fd = open(file, O_RDONLY)) < 0) {
529*2264Sjacobs 			detailed_error(svc, "%s: %s", file, strerror(errno));
530*2264Sjacobs 			return (PAPI_DOCUMENT_ACCESS_ERROR);
531*2264Sjacobs 		} else if (svc->transfer_encoding !=
532*2264Sjacobs 						TRANSFER_ENCODING_CHUNKED) {
533*2264Sjacobs 			struct stat st;
534*2264Sjacobs 
535*2264Sjacobs 			if (fstat(fd, &st) >= 0)
536*2264Sjacobs 				size = st.st_size;
537*2264Sjacobs 		}
538*2264Sjacobs 	}
539*2264Sjacobs 
540*2264Sjacobs 	*response = NULL;
541*2264Sjacobs 	while (*response == NULL) {
542*2264Sjacobs 		http_status_t status = HTTP_CONTINUE;
543*2264Sjacobs 
544*2264Sjacobs 		result = ipp_send_initial_request_block(svc, request, size);
545*2264Sjacobs 
546*2264Sjacobs 		if (result == PAPI_OK) {
547*2264Sjacobs 			if (file != NULL) {
548*2264Sjacobs 				/* send the file contents if we have it */
549*2264Sjacobs 				int rc;
550*2264Sjacobs 				char buf[BUFSIZ];
551*2264Sjacobs 
552*2264Sjacobs 				lseek(fd, 0L, SEEK_SET);
553*2264Sjacobs 				while ((rc = read(fd, buf, sizeof (buf))) > 0) {
554*2264Sjacobs 					if (ipp_request_write(svc, buf, rc)
555*2264Sjacobs 							< rc) {
556*2264Sjacobs 						break;
557*2264Sjacobs 					}
558*2264Sjacobs 				}
559*2264Sjacobs 			}
560*2264Sjacobs 
561*2264Sjacobs 			(void) ipp_request_write(svc, "", 0);
562*2264Sjacobs 		}
563*2264Sjacobs 
564*2264Sjacobs 		/* update our connection info */
565*2264Sjacobs 		while (status == HTTP_CONTINUE)
566*2264Sjacobs 			status = httpUpdate(svc->connection);
567*2264Sjacobs 
568*2264Sjacobs 		if (status == HTTP_UNAUTHORIZED) {
569*2264Sjacobs 			httpFlush(svc->connection);
570*2264Sjacobs 			if ((svc->connection->authstring[0] == '\0') &&
571*2264Sjacobs 			    (setAuthString(svc) == 0)) {
572*2264Sjacobs 				httpReconnect(svc->connection);
573*2264Sjacobs 				continue;
574*2264Sjacobs 			}
575*2264Sjacobs 		} else if (status == HTTP_UPGRADE_REQUIRED) {
576*2264Sjacobs 			/*
577*2264Sjacobs 			 * If the transport was built with TLS support, we can
578*2264Sjacobs 			 * try to use it.
579*2264Sjacobs 			 */
580*2264Sjacobs 			httpFlush(svc->connection);
581*2264Sjacobs 			httpReconnect(svc->connection);
582*2264Sjacobs 			httpEncryption(svc->connection, HTTP_ENCRYPT_REQUIRED);
583*2264Sjacobs 			continue;
584*2264Sjacobs 		}
585*2264Sjacobs 
586*2264Sjacobs 		if (status != HTTP_OK)
587*2264Sjacobs 			return (http_to_papi_status(status));
588*2264Sjacobs 
589*2264Sjacobs 		/* read the IPP response */
590*2264Sjacobs 		result = ipp_read_message(&ipp_request_read, svc, response,
591*2264Sjacobs 				IPP_TYPE_RESPONSE);
592*2264Sjacobs 
593*2264Sjacobs 		if (result == PAPI_OK)
594*2264Sjacobs 			result = ipp_status_info(svc, *response);
595*2264Sjacobs #ifdef DEBUG
596*2264Sjacobs 		fprintf(stderr, "\nIPP-RESPONSE: (%s) (%s)", (file ? file : ""),
597*2264Sjacobs 				papiStatusString(result));
598*2264Sjacobs 		papiAttributeListPrint(stderr, *response, "    ");
599*2264Sjacobs 		putc('\n', stderr);
600*2264Sjacobs 		fflush(stderr);
601*2264Sjacobs #endif
602*2264Sjacobs 	}
603*2264Sjacobs 
604*2264Sjacobs 	return (result);
605*2264Sjacobs }
606*2264Sjacobs 
607*2264Sjacobs papi_status_t
608*2264Sjacobs ipp_send_request(service_t *svc, papi_attribute_t **request,
609*2264Sjacobs 		papi_attribute_t ***response)
610*2264Sjacobs {
611*2264Sjacobs 	return (ipp_send_request_with_file(svc, request, response, NULL));
612*2264Sjacobs }
613