1
2 /******************************************************************************\
3
4 Copyright 2015 HP Development Company, L.P.
5
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of version 2 of the GNU General Public License as published by the
8 Free Software Foundation.
9 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
11 See the GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License along with
13 this program;
14 if not, write to:
15 Free Software Foundation, Inc.
16 51 Franklin Street, Fifth Floor
17 Boston, MA 02110-1301, USA.
18
19 \******************************************************************************/
20
21
22 #include <cups/cups.h>
23 #include <cups/language.h>
24 #include <cups/ppd.h>
25 #include <syslog.h>
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #include <pwd.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <string.h>
32
33 #include <sys/time.h>
34 #include <time.h>
35 #include <unistd.h>
36
37 #include "hp_ipp.h"
38 #include "hp_ipp_i.h"
39
40 #define _STRINGIZE(x) #x
41 #define STRINGIZE(x) _STRINGIZE(x)
42
43
acquireCupsInstance()44 http_t* acquireCupsInstance()
45 {
46 if ( http == NULL)
47 {
48 http = httpConnectEncrypt( cupsServer(), ippPort(), cupsEncryption() );
49 }
50
51 return http;
52 }
53
54
_releaseCupsInstance()55 void _releaseCupsInstance()
56 {
57 if (http)
58 {
59 httpClose(http);
60 }
61
62 http = NULL;
63 }
64
65
66 /*
67 * 'validate_name()' - Make sure the printer name only contains valid chars.
68 */
69 static int /* O - 0 if name is no good, 1 if name is good */
validate_name(const char * name)70 validate_name( const char *name ) /* I - Name to check */
71 {
72 return 1; // TODO: Make it work with utf-8 encoding
73 }
74
getCupsErrorString(int status)75 const char *getCupsErrorString(int status)
76 {
77 return ippErrorString(status);
78 }
79
freePrinterList(printer_t * list)80 void freePrinterList(printer_t *list)
81 {
82 printer_t *temp;
83 printer_t *elem = list;
84
85 while (elem != NULL) {
86 temp = elem;
87 elem = elem->next;
88 free(temp);
89 }
90 }
91
addCupsPrinter(char * name,char * device_uri,char * location,char * ppd_file,char * model,char * info)92 int addCupsPrinter(char *name, char *device_uri, char *location, char *ppd_file, char *model, char *info)
93 {
94 char printer_uri[ HTTP_MAX_URI ];
95 cups_lang_t *language; /* Default language */
96 int ret_status = 0;
97 ipp_status_t status = IPP_BAD_REQUEST;
98 ipp_t * request = NULL, /* IPP Request */
99 *response = NULL; /* IPP Response */
100
101 if ( ( strlen( ppd_file ) > 0 && strlen( model ) > 0 ) ||
102 ( strlen( ppd_file ) == 0 && strlen( model ) == 0) ) {
103 // status_str = "Invalid arguments: specify only ppd_file or model, not both or neither";
104 goto abort;
105 }
106
107 if ( !validate_name(name) ) {
108 //status_str = "Invalid printer name";
109 goto abort;
110 }
111
112 if ( info == NULL )
113 strcpy( info, name );
114
115 sprintf( printer_uri, "ipp://localhost/printers/%s", name );
116
117 cupsSetUser ("root");
118 /* Connect to the HTTP server */
119 if (acquireCupsInstance() == NULL)
120 {
121 //status_str = "Unable to connect to CUPS server";
122 goto abort;
123 }
124
125 /* Assemble the IPP request */
126 request = ippNew();
127 language = cupsLangDefault();
128
129 ippSetOperation( request, CUPS_ADD_PRINTER );
130 ippSetRequestId ( request, 1 );
131
132 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
133 "attributes-charset", NULL, cupsLangEncoding( language ) );
134
135 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
136 "attributes-natural-language", NULL, language->language );
137
138 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_URI,
139 "printer-uri", NULL, printer_uri );
140
141 ippAddInteger( request, IPP_TAG_PRINTER, IPP_TAG_ENUM,
142 "printer-state", IPP_PRINTER_IDLE );
143
144 ippAddBoolean( request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1 );
145
146 ippAddString( request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
147 device_uri );
148
149 ippAddString( request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL,
150 info );
151
152 ippAddString( request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL,
153 location );
154
155 if ( strlen( model ) > 0 ) {
156 ippAddString( request, IPP_TAG_PRINTER, IPP_TAG_NAME, "ppd-name", NULL, model );
157
158 /* Send the request and get a response. */
159 response = cupsDoRequest( http, request, "/admin/" );
160 } else {
161 /* Send the request and get a response. */
162 response = cupsDoFileRequest( http, request, "/admin/", ppd_file );
163 }
164
165 if (response == NULL)
166 status = cupsLastError();
167 else
168 status = ippGetStatusCode( response );
169
170 // If user cancels the authentication pop-up, changing error code to IPP_NOT_AUTHENTICATED from IPP_FORBIDDEN
171 if (status == IPP_FORBIDDEN && auth_cancel_req) {
172 status = IPP_NOT_AUTHENTICATED;
173 auth_cancel_req = 0; // Reseting cancel request.
174 }
175
176 if ( status <= IPP_OK_CONFLICT ) {
177 status =IPP_OK;
178 ret_status = 0;
179 } else {
180 ret_status = 1;
181 }
182
183 abort:
184 if ( response != NULL )
185 ippDelete( response );
186
187 return ret_status;
188 }
189
delCupsPrinter(char * pr_name)190 int delCupsPrinter(char *pr_name)
191 {
192 int ret_status;
193 cups_lang_t *language; /* Default language */
194 char uri[ HTTP_MAX_URI ];
195 const char *username = NULL;
196 ipp_t * request = NULL, /* IPP Request */
197 *response = NULL; /* IPP Response */
198
199 if ( !validate_name(pr_name) ) {
200 goto abort;
201 }
202
203 username = cupsUser();
204
205 cupsSetUser ("root");
206 /* Connect to the HTTP server */
207 if (acquireCupsInstance() == NULL)
208 {
209 goto abort;
210 }
211 snprintf( uri, sizeof( uri ), "ipp://localhost/printers/%s", pr_name );
212
213 /*
214 * Build a CUPS_DELETE_PRINTER request, which requires the following
215 * attributes:
216 *
217 * attributes-charset
218 * attributes-natural-language
219 * printer-uri
220 */
221 request = ippNew();
222
223 ippSetOperation( request, CUPS_DELETE_PRINTER );
224 ippSetRequestId ( request, 1 );
225
226 language = cupsLangDefault();
227
228 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
229 "attributes-charset", NULL, cupsLangEncoding( language ) );
230
231 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
232 "attributes-natural-language", NULL, language->language );
233
234 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_URI,
235 "printer-uri", NULL, uri );
236
237 /*
238 * Do the request and get back a response...
239 */
240 response = cupsDoRequest( http, request, "/admin/" );
241
242 if (response == NULL)
243 ret_status = cupsLastError();
244 else
245 ret_status = ippGetStatusCode( response );
246
247 // If user cancels the authentication pop-up, changing error code to IPP_NOT_AUTHENTICATED from IPP_FORBIDDEN
248 if (ret_status == IPP_FORBIDDEN && auth_cancel_req)
249 {
250 ret_status = IPP_NOT_AUTHENTICATED;
251 auth_cancel_req = 0; // Reseting cancel request.
252 }
253
254 if ( ret_status <= IPP_OK_CONFLICT )
255 ret_status = IPP_OK;
256
257 abort:
258 if (username)
259 cupsSetUser(username);
260
261 if ( response != NULL )
262 ippDelete( response );
263
264 return ret_status;
265 }
266
267
setDefaultCupsPrinter(char * pr_name)268 int setDefaultCupsPrinter(char *pr_name)
269 {
270 int ret_status = 0;
271 char uri[ HTTP_MAX_URI ]; /* URI for printer/class */
272 ipp_t *request = NULL, /* IPP Request */
273 *response = NULL; /* IPP Response */
274 cups_lang_t *language; /* Default language */
275 const char *username = NULL;
276
277 if ( !validate_name(pr_name) ) {
278 goto abort;
279 }
280
281 username = cupsUser();
282
283 cupsSetUser ("root");
284 /* Connect to the HTTP server */
285 if ( acquireCupsInstance () == NULL) {
286 goto abort;
287 }
288
289 /*
290 * Build a CUPS_SET_DEFAULT request, which requires the following
291 * attributes:
292 *
293 * attributes-charset
294 * attributes-natural-language
295 * printer-uri
296 */
297
298 snprintf( uri, sizeof( uri ), "ipp://localhost/printers/%s", pr_name );
299
300 request = ippNew();
301
302 ippSetOperation( request, CUPS_SET_DEFAULT );
303 ippSetRequestId ( request, 1 );
304
305 language = cupsLangDefault();
306
307 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
308 "attributes-charset", NULL, "utf-8" ); //cupsLangEncoding( language ) );
309
310 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
311 "attributes-natural-language",
312 //NULL, language != NULL ? language->language : "en");
313 NULL, language->language );
314
315 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_URI,
316 "printer-uri", NULL, uri );
317
318 /*
319 * Do the request and get back a response...
320 */
321
322 response = cupsDoRequest( http, request, "/admin/" );
323 if (response == NULL)
324 ret_status = cupsLastError();
325 else
326 ret_status = ippGetStatusCode(response );
327
328 // If user cancels the authentication pop-up, changing error code to IPP_NOT_AUTHENTICATED from IPP_FORBIDDEN
329 if (ret_status == IPP_FORBIDDEN && auth_cancel_req)
330 {
331 ret_status = IPP_NOT_AUTHENTICATED;
332 auth_cancel_req = 0; // Reseting cancel request.
333 }
334
335 // ToDo: When you assign ret_status with IPP_OK, the caller will lose getting the actual status
336 if ( ret_status <= IPP_OK_CONFLICT )
337 ret_status = IPP_OK;
338
339 abort:
340 if (username)
341 cupsSetUser(username);
342
343 if ( response != NULL )
344 ippDelete( response );
345
346 return ret_status;
347
348 }
349
controlCupsPrinter(char * pr_name,int op)350 int controlCupsPrinter(char *pr_name, int op)
351 {
352 ipp_t *request = NULL, /* IPP Request */
353 *response = NULL; /* IPP Response */
354 char uri[ HTTP_MAX_URI ]; /* URI for printer/class */
355 cups_lang_t *language;
356 const char *username = NULL;
357 int ret_status = 0; //ToDo: Make use of IPP_BAD_REQUEST while changing to proper status codes
358
359 if ( !validate_name(pr_name) ) {
360 goto abort;
361 }
362
363 username = cupsUser();
364 cupsSetUser ("root");
365 /* Connect to the HTTP server */
366 if (acquireCupsInstance () == NULL)
367 {
368 goto abort;
369 }
370
371 request = ippNew();
372
373 ippSetOperation( request, op );
374 ippSetRequestId ( request, 1 );
375
376 language = cupsLangDefault();
377
378 snprintf( uri, sizeof( uri ), "ipp://localhost/printers/%s", pr_name );
379
380 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
381 "attributes-charset", NULL, cupsLangEncoding( language ) );
382
383 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
384 "attributes-natural-language", NULL, language->language );
385
386 ippAddString( request, IPP_TAG_OPERATION, IPP_TAG_URI,
387 "printer-uri", NULL, uri );
388
389
390 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
391 "requesting-user-name", NULL, cupsUser());
392
393 if (op == IPP_PURGE_JOBS)
394 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 1);
395
396 response = cupsDoRequest(http, request, "/admin/");
397
398 if (response == NULL)
399 ret_status = cupsLastError();
400 else
401 ret_status = ippGetStatusCode( response );
402
403 // If user cancels the authentication pop-up, changing error code to IPP_NOT_AUTHENTICATED from IPP_FORBIDDEN
404 if (ret_status == IPP_FORBIDDEN && auth_cancel_req)
405 {
406 ret_status = IPP_NOT_AUTHENTICATED;
407 auth_cancel_req = 0; // Reseting cancel request.
408 }
409
410 abort:
411 if (username)
412 cupsSetUser(username);
413
414 if ( response != NULL )
415 ippDelete( response );
416
417 return ret_status;
418 }
419
420
421 /*
422 * 'getCupsPrinters()' - Get installed cups printers.
423 *
424 * This function sends a CUPS_GET_PRINTERS request and return IPP response.
425 * It also updates the number of cups printers found in count variable.
426 *
427 */
__getCupsPrinters()428 ipp_t * __getCupsPrinters()
429 {
430 ipp_t *request = NULL; /* IPP request object */
431 ipp_t *response = NULL; /* IPP response object */
432 ipp_attribute_t *attr; /* Current IPP attribute */
433
434
435 static const char * attrs[] = /* Requested attributes */
436 {
437 "printer-info",
438 "printer-location",
439 "printer-make-and-model",
440 "printer-state",
441 "printer-name",
442 "device-uri",
443 "printer-uri-supported",
444 "printer-is-accepting-jobs",
445 };
446
447 /* Connect to the HTTP server */
448 if (acquireCupsInstance() == NULL)
449 {
450 goto abort;
451 }
452
453 /* Assemble the IPP request */
454 request = ippNewRequest(CUPS_GET_PRINTERS);
455
456 if (request == NULL)
457 goto abort;
458
459
460 ippAddStrings( request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
461 "requested-attributes", sizeof( attrs ) / sizeof( attrs[ 0 ] ),
462 NULL, attrs );
463
464 /* Send the request and get a response. */
465 response = cupsDoRequest( http, request, "/" );
466
467 abort:
468
469 return response;
470 }
471
472
473 /*
474 * 'parsePrinterAttributes()'
475 *
476 * This function parses the IPP response and updates the printer_list buffer.
477 *
478 */
__parsePrinterAttributes(ipp_t * response,printer_t ** printer_list)479 int __parsePrinterAttributes(ipp_t *response, printer_t **printer_list)
480 {
481 ipp_attribute_t *attr;
482 ipp_tag_t grp_tag;
483 ipp_tag_t val_tag;
484 char *attr_name;
485
486 printer_t *t_printer = NULL;
487 printer_t *t_printer_list = NULL;
488
489 for ( attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response) )
490 {
491 if ( ippGetGroupTag(attr) != IPP_TAG_PRINTER )
492 continue;
493
494 t_printer = (printer_t *) calloc(1, sizeof(printer_t));
495 if (t_printer == NULL) {
496 BUG("Memory allocation for printer struct failed!\n");
497 goto abort;
498 }
499
500 if (t_printer_list == NULL) {
501 t_printer_list = t_printer;
502 *printer_list = t_printer_list;
503 } else {
504 t_printer_list->next = t_printer;
505 t_printer_list = t_printer;
506 }
507
508 while ( attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER ) {
509 attr_name = ippGetName(attr);
510 val_tag = ippGetValueTag(attr);
511
512 if ( strcmp(attr_name, "printer-name") == 0 &&
513 val_tag == IPP_TAG_NAME ) {
514 strcpy(t_printer->name, ippGetString(attr, 0, NULL) );
515 }
516 else if ( strcmp(attr_name, "device-uri") == 0 &&
517 val_tag == IPP_TAG_URI ) {
518 strcpy(t_printer->device_uri, ippGetString(attr, 0, NULL) );
519 }
520 else if ( strcmp(attr_name, "printer-uri-supported") == 0 &&
521 val_tag == IPP_TAG_URI ) {
522 strcpy(t_printer->printer_uri, ippGetString(attr, 0, NULL) );
523 }
524 else if ( strcmp(attr_name, "printer-info") == 0 &&
525 val_tag == IPP_TAG_TEXT ) {
526 strcpy(t_printer->info, ippGetString(attr, 0, NULL) );
527 }
528 else if ( strcmp(attr_name, "printer-location") == 0 &&
529 val_tag == IPP_TAG_TEXT ) {
530 strcpy(t_printer->location, ippGetString(attr, 0, NULL) );
531 }
532 else if ( strcmp(attr_name, "printer-make-and-model") == 0 &&
533 val_tag == IPP_TAG_TEXT ) {
534 strcpy(t_printer->make_model, ippGetString(attr, 0, NULL) );
535 }
536 else if ( strcmp(attr_name, "printer-state") == 0 &&
537 val_tag == IPP_TAG_ENUM ) {
538 t_printer->state = ( ipp_state_t ) ippGetInteger(attr, 0);
539 }
540 else if (!strcmp(attr_name, "printer-is-accepting-jobs") &&
541 val_tag == IPP_TAG_BOOLEAN) {
542 t_printer->accepting = ippGetBoolean(attr, 0);
543 }
544
545 attr = ippNextAttribute(response);
546 }
547 }
548
549 abort:
550 return 0;
551 }
552
553
getCupsPrinters(printer_t ** printer_list)554 int getCupsPrinters(printer_t **printer_list)
555 {
556 ipp_t *response = NULL;
557 int ret_status = 0;
558
559 response = __getCupsPrinters();
560
561 if (response) {
562 ret_status = __parsePrinterAttributes(response, printer_list);
563 ippDelete(response);
564 }
565
566 return ret_status;
567 }
568
569
570 /*
571 * 'initializeIPPRequest()' - Initialize request with those attributes which
572 * are common for all requests .
573 */
initializeIPPRequest(ipp_t * request)574 void initializeIPPRequest(ipp_t *request)
575 {
576
577 if (request)
578 {
579 //Set common request fields
580 ippSetVersion ( request, 2, 0 );
581 ippSetRequestId ( request, 100 );
582 }
583 }
584
585
586 /*
587 * 'createDeviceStatusRequest()' - Create IPP request and update the same with values needed for getting device status attributes.
588 */
createDeviceStatusRequest()589 ipp_t * createDeviceStatusRequest()
590 {
591
592 ipp_t *request = NULL; /* IPP request object */
593 static const char * attrs[] = /* Requested attributes */
594 {
595 "marker-names",
596 "marker-types",
597 "marker-levels",
598 "marker-low-levels",
599 "printer-state",
600 "printer-state-reasons",
601 };
602
603 /* Assemble the IPP request */
604 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
605 initializeIPPRequest(request);
606 if (request)
607 {
608 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "");
609 ippAddStrings( request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof( attrs ) / sizeof( attrs[ 0 ] ), NULL, attrs );
610 }
611
612 return request;
613 }
614
615
616 /*
617 * 'ExtractIPPData()' - Extract IPP Data from IPP raw response. It will have HTTP header and may have chunk data.
618 */
ExtractIPPData(char * data,int * length)619 HPIPP_RESULT ExtractIPPData(char* data, int *length)
620 {
621 char * ptr = data;
622 int chunked = 0;
623 int content_length = 0;
624 HPIPP_RESULT status = HPIPP_OK;
625
626 status = parseResponseHeader(data, &content_length, &chunked, NULL);
627 if(HPIPP_OK != status )
628 return status;
629
630 DBG("Transfer-Encoding (chunked = %d), DataLength(with http header) = [%d]\n", chunked, *length);
631
632 //Remove http header.
633 ptr = strstr(data, CRLFCRLF);
634 if(ptr)
635 {
636 ptr += CRLFCRLF_LENGTH;
637 *length = *length - (ptr - data);
638 memmove(data, ptr, *length);
639 }
640
641 if (chunked)
642 {
643 status = removeChunkInfo(data, length);
644 }
645
646 return status;
647 }
648
649
650 /*
651 * 'parseResponseHeader()' - Parse HTTP Header and update content_length and chunk flag.
652 *
653 */
parseResponseHeader(char * header,int * content_length,int * chunked,int * header_size)654 HPIPP_RESULT parseResponseHeader(char *header, int *content_length, int *chunked, int *header_size)
655 {
656 HPIPP_RESULT hr = HPIPP_OK;
657 char *ptr = NULL;
658
659 if(!header || !content_length || !chunked)
660 return HPIPP_ERROR;
661
662 if(header_size)
663 {
664 ptr = strstr(header, CRLFCRLF);
665 *header_size = (ptr)? (ptr - header + CRLFCRLF_LENGTH): 0;
666 }
667
668 if (strcasestr(header, "Transfer-Encoding:") && strcasestr(header, "chunked"))
669 {
670 *chunked = 1;
671 *content_length = 0;
672 }
673 else if (ptr = strstr(header,"Content-Length:"))
674 {
675 *content_length = strtol(ptr + strlen("Content-Length:"), NULL, BASE_DECIMAL);
676 *chunked = 0;
677 }
678 else
679 {
680 DBG("parseResponseHeader: Could not find Transfer-Encoding: chunked or Content-Length: \n");
681 hr = HPIPP_ERROR;
682 }
683
684 DBG("chunked = [%d] , Content_length = [%d]\n", *chunked, *content_length);
685 return hr;
686 }
687
688
689 /*
690 * 'getDeviceStatusAttributes()' - Get Device Status Attributes.
691 *
692 * This function sends a IPP_GET_PRINTER_ATTRIBUTES request and request for
693 * marker-names, marker-types, marker-levels, marker-low-levels, printer-state, printer-state-reasons.
694 * In addition to this it also updates the attribute count in the response.
695 */
getDeviceStatusAttributes(char * device_uri,int * count)696 ipp_t * getDeviceStatusAttributes(char* device_uri, int *count)
697 {
698 ipp_t *request = NULL; /* IPP request object */
699 ipp_t *response = NULL; /* IPP response object */
700 ipp_attribute_t *attr; /* Current IPP attribute */
701 int max_count = 0;
702
703 //Create Device Status Request
704 request = createDeviceStatusRequest();
705 if (request == NULL)
706 goto abort;
707
708 //Send request to the server based on connection
709 if(strcasestr(device_uri, ":/usb") != NULL )
710 {
711 response = usbDoRequest(request, device_uri);
712 }
713 else if (strcasestr(device_uri, ":/net") != NULL )
714 {
715 response = networkDoRequest(request, device_uri);
716 }
717 else
718 {
719 BUG("Invalid device URI (%s)\n", device_uri);
720 goto abort;
721 }
722
723 //Find out the attribute count.
724 if (response)
725 for ( attr = ippFirstAttribute( response ); attr != NULL; attr = ippNextAttribute( response ) )
726 max_count++;
727
728 abort:
729 *count = max_count;
730 return response;
731 }
732
733 /*************************** UTILS ***************************/
734
735 /*
736 * 'usbDoRequest()' - Send ipp request to a usb device and read back the response.
737 *
738 * This function sends ipp request to a device correponding to caller provided device_uri and reads back the response.
739 * This function then parses and stores the raw response into ipp_t (response) and sends back the same.
740 */
usbDoRequest(ipp_t * request,char * device_uri)741 ipp_t * usbDoRequest(ipp_t *request, char* device_uri)
742 {
743 ipp_t *response = NULL; /* IPP response object */
744 ipp_state_t state;
745 raw_ipp raw_request;
746 raw_ipp raw_response;
747
748 memset(&raw_request, 0, sizeof(raw_request));
749 memset(&raw_response, 0, sizeof(raw_response));
750
751 //Convert ipp_t request structure into raw ipp format
752 state = ippWriteIO(&raw_request, (ipp_iocb_t)raw_ipp_request_callback, 1, NULL, request);
753 if(IPP_ERROR == state)
754 {
755 BUG("ippWriteIO Failed...\n");
756 goto abort;
757 }
758
759 //Prepend http header to the raw request buffer
760 if (prepend_http_header(&raw_request) != HPIPP_OK)
761 goto abort;
762
763 //Send request to the device and get back the response
764 if (HPMUD_R_OK != sendUSBRequest(raw_request.data, raw_request.data_length, &raw_response, device_uri))
765 {
766 goto abort;
767 }
768
769 //Convert Raw data into ipp_t response structure format
770 response = ippNew();
771 state = ippReadIO(&raw_response, (ipp_iocb_t)raw_ipp_response_read_callback, 1, NULL, response);
772 if(response && (IPP_ERROR == state))
773 {
774 BUG("ippWriteIO Failed...\n");
775 ippDelete( response );
776 response = NULL;
777 }
778
779 abort:
780 return response;
781 }
782
783
784 /*
785 * 'networkDoRequest()' - Send ipp request to a network device and read back the response.
786 *
787 * This function makes use of cups API (cupsDoRequest) to send ipp request and get back the ipp response.
788 */
networkDoRequest(ipp_t * request,char * device_uri)789 ipp_t * networkDoRequest(ipp_t *request, char* device_uri)
790 {
791 ipp_t *response = NULL; /* IPP response object */
792 char ip[HPMUD_LINE_SIZE] = {0};
793 http_t * http = NULL; /* HTTP object */
794
795 //Get IP address
796 hpmud_get_uri_datalink(device_uri, ip, HPMUD_LINE_SIZE);
797
798 //Create http connection
799 http = httpConnectEncrypt( ip, ippPort(), cupsEncryption() );
800 if (http == NULL)
801 goto abort;
802
803 //Send the request and get a response.
804 if ( ( response = cupsDoRequest( http, request, "/ipp/printer" ) ) == NULL )
805 {
806 goto abort;
807 }
808
809 abort:
810
811 return response;
812 }
813
814
815 /*
816 * 'sendUSBRequest()' - Send request and read response.
817 *
818 */
sendUSBRequest(char * buf,int size,raw_ipp * responseptr,char * device_uri)819 enum HPMUD_RESULT sendUSBRequest(char *buf, int size, raw_ipp *responseptr, char * device_uri)
820 {
821 HPMUD_DEVICE hd = 0;
822 HPMUD_CHANNEL cd;
823 enum HPMUD_RESULT stat = HPMUD_R_OK;
824 int device_already_open = 0;
825
826 DBG("sendUSBRequest: buf = %p, size = %d, responseptr = %p, device_uri = %s\n", buf, size, responseptr, device_uri);
827
828 /* Open hp device. */
829 if ((stat = hpmud_open_device(device_uri, HPMUD_RAW_MODE, &hd)) != HPMUD_R_OK)
830 {
831 if(stat == HPMUD_R_INVALID_STATE)
832 {
833 //hpmud reports HPMUD_R_INVALID_STATE error when we try to open the device again which was already opened by tools like hp-levels
834 /// or hp-toolbox. We can use this already opened device but we need to make sure we do not close the device after use.
835 hd = 1;
836 device_already_open = 1;
837 }
838 else
839 {
840 BUG("Device open failed with status code = %d\n", stat);
841 goto abort;
842 }
843 }
844
845 /* Open ipp channel. */
846 if ((stat = hpmud_open_channel(hd, HPMUD_S_IPP_CHANNEL, &cd)) != HPMUD_R_OK)
847 {
848 if ((stat = hpmud_open_channel(hd, HPMUD_S_IPP_CHANNEL2, &cd)) != HPMUD_R_OK)
849 {
850 BUG("Channel open failed with status code = %d\n", stat);
851 goto abort;
852 }
853 }
854
855 //Write request on the channel
856 if ((stat = writeChannel(buf, size, hd, cd)) != HPMUD_R_OK)
857 {
858 BUG("Channel write failed with status code = %d\n", stat);
859 goto abort;
860 }
861
862 //Read the response from the channel
863 if ((stat = readChannel(responseptr, hd, cd)) != HPMUD_R_OK)
864 {
865 BUG("Channel read failed with status code = %d\n", stat);
866 //goto abort;
867 }
868
869
870 //Remove header and chunking information from the raw IPP response
871 ExtractIPPData(responseptr->data, &responseptr->data_length);
872 stat = HPMUD_R_OK;
873
874 abort:
875
876 if (cd > 0)
877 hpmud_close_channel(hd, cd);
878 if (hd > 0 && !device_already_open)
879 hpmud_close_device(hd);
880
881 return stat;
882 }
883
884
885 /*
886 * 'readChannel()' - Read response from the channel.
887 *
888 */
readChannel(raw_ipp * responseptr,HPMUD_DEVICE hd,HPMUD_CHANNEL cd)889 enum HPMUD_RESULT readChannel(raw_ipp *responseptr, HPMUD_DEVICE hd, HPMUD_CHANNEL cd)
890 {
891 int bytes_read = 0;
892 int bytes_remaining = 0;
893 int content_length = 0;
894 int chunked = 0;
895 int header_size = 0;
896 enum HPMUD_RESULT stat = HPMUD_R_OK;
897 char* data = responseptr->data;
898 int* size = &responseptr->data_length;
899
900 if(!responseptr)
901 {
902 DBG("NULL responseptr passed.\n");
903 return HPMUD_R_INVALID_LENGTH;
904 }
905
906 memset(responseptr, 0, MAX_IPP_DATA_LENGTH);
907
908 //Read http header and figure out Transfer-Encoding of the response
909 stat = hpmud_read_channel(hd, cd, data, USB_BULK_TRANSFER_LENGTH, TIMEOUT, &bytes_read);
910 if(HPMUD_R_OK != stat && HPMUD_R_IO_TIMEOUT != stat)
911 {
912 return stat;
913 }
914
915 DBG("Header bytes read from the channel = %d, status = [%d] \n", bytes_read, stat);
916 *size += bytes_read;
917
918 if (HPIPP_OK != parseResponseHeader(data, &content_length, &chunked, &header_size))
919 return HPMUD_R_IO_ERROR;
920
921 bytes_remaining = content_length - (bytes_read - header_size); //Update bytes remaining
922
923 //Read remaining response
924 while(bytes_read)
925 {
926 if (*size + USB_BULK_TRANSFER_LENGTH > MAX_IPP_DATA_LENGTH) //Check for the overflow condition
927 {
928 stat = HPMUD_R_INVALID_LENGTH;
929 break;
930 }
931
932 stat = hpmud_read_channel(hd, cd, &data[*size], USB_BULK_TRANSFER_LENGTH, TIMEOUT, &bytes_read);
933
934 DBG("Bytes read from the channel = %d , status = [%d], bytes_remaining = [%d]\n", bytes_read, stat, bytes_remaining);
935 if(HPMUD_R_OK != stat && HPMUD_R_IO_TIMEOUT != stat)
936 break;
937
938 *size += bytes_read;
939 if(chunked)
940 {
941 if(memcmp(&data[*size - CHUNK_DELIMITER_LENGTH], CHUNK_DELIMITER, CHUNK_DELIMITER_LENGTH) == 0)
942 {
943 DBG("Chunk end recieved....\n");
944 break;
945 }
946 }
947 else
948 {
949 bytes_remaining -= bytes_read;
950 if(bytes_remaining == 0)
951 {
952 DBG("Complete unchunked data recieved....\n");
953 break;
954 }
955 }
956 }
957
958 DBG("Total bytes read from the channel = %d\n", *size);
959 return stat;
960 }
961
962
963 /*
964 * 'writeChannel()' - Write request on the channel.
965 */
writeChannel(char * buf,int size,HPMUD_DEVICE hd,HPMUD_CHANNEL cd)966 enum HPMUD_RESULT writeChannel(char *buf, int size, HPMUD_DEVICE hd, HPMUD_CHANNEL cd)
967 {
968 int timeout = 1;
969 int total = 0;
970 int len = 0;
971 int transfer_size = 0;
972 enum HPMUD_RESULT stat = HPMUD_R_OK;
973
974 while(size > 0)
975 {
976 transfer_size = (size > USB_BULK_TRANSFER_LENGTH)? USB_BULK_TRANSFER_LENGTH : size;
977 stat = hpmud_write_channel(hd, cd, buf+total, transfer_size, timeout, &len);
978 DBG("Bytes written on the channel = %d Size = %d, transfer_size = %d\n\n\n", len, size, transfer_size);
979 total = len;
980 size -= len;
981 }
982
983 DBG("Total bytes written on the channel = %d\n", total);
984
985 return stat;
986 }
987
988
989 /*
990 * 'raw_ipp_response_read_callback()' - Read IPP data from a raw IPP response buffer.
991 *
992 * This callback function is called from cups API ippReadIO. ippReadIO function reads data from file,
993 * buffer or socket and then parses and converts raw ipp data into ipp_t format.
994 */
995 static ssize_t /* O - Number of bytes read */
raw_ipp_response_read_callback(raw_ipp * src,ipp_uchar_t * buffer,size_t length)996 raw_ipp_response_read_callback(raw_ipp *src, /* I - Source raw ipp data buffer */
997 ipp_uchar_t *buffer, /* O - Read buffer */
998 size_t length) /* O - Number of bytes to read */
999 {
1000
1001 //Check if remaining data has length bytes left. If not then return only remaining bytes.
1002 if (src->data_length < length )
1003 {
1004 DBG("Requested Length(%d) is more than source buffer length(%d).\n", length, src->data_length);
1005 length = src->data_length;
1006 }
1007
1008 //Copy data to the requested buffer
1009 memcpy(buffer, src->data, length);
1010
1011 //Remove copied data from the src, so that next time same data is not copied.
1012 memmove(src->data, &src->data[length], src->data_length - length);
1013
1014 //Update src with new length.
1015 src->data_length = src->data_length - length;
1016
1017 return (length);
1018
1019 }
1020
1021 /*
1022 * 'raw_ipp_request_callback()' - Write RAW IPP data to a buffer.
1023 *
1024 * This callback function (called from cupsSendRequest) recieves the converted data from
1025 * ipp_t request structure to * a binary format in iterations. This function keeps on
1026 * updating raw_buffer which is passed by the caller. Once all iterations are over,
1027 * raw_buffer will contain complete IPP request data in a binary form which can later
1028 * be sent to the IPP server.
1029 */
1030 static ssize_t /* O - Number of bytes written */
raw_ipp_request_callback(volatile raw_ipp * raw_buffer,ipp_uchar_t * buffer,size_t length)1031 raw_ipp_request_callback(volatile raw_ipp *raw_buffer, /* O - RAW IPP buffer*/
1032 ipp_uchar_t *buffer, /* I - Data to write */
1033 size_t length) /* I - Number of bytes to write */
1034 {
1035
1036 if (raw_buffer->data_length + length < MAX_IPP_DATA_LENGTH)
1037 {
1038 memcpy((char *)&raw_buffer->data[raw_buffer->data_length], buffer, length);
1039 raw_buffer->data_length = raw_buffer->data_length + length;
1040 }
1041 else
1042 {
1043 BUG("Insufficient raw_ipp->data size. %d bytes needed.\n", raw_buffer->data_length + length);
1044 }
1045
1046 return (length);
1047 }
1048
1049
1050 /*
1051 * 'prepend_http_header()' - Adds http header in the beginning of the ipp raw data and shifts
1052 * raw data accordingly.
1053 */
prepend_http_header(raw_ipp * raw_request)1054 HPIPP_RESULT prepend_http_header(raw_ipp *raw_request)
1055 {
1056
1057 char *http_header_tamplate = "POST /ipp/printer HTTP/1.1\r\nContent-Length: %d\r\nContent-Type: application/ipp\r\nHOST: Localhost\r\n\r\n";
1058 char http_header[1024] = {0};
1059 int http_header_size = 0;
1060
1061 //Create transport header for the request
1062 http_header_size = sprintf(http_header, http_header_tamplate, raw_request->data_length);
1063
1064 //Shift raw_request->data by http_header_size bytes and copy http_header in the beginning
1065 if (raw_request->data_length + http_header_size >= MAX_IPP_DATA_LENGTH)
1066 return HPIPP_INVALID_LENGTH;
1067
1068 memmove(raw_request->data + http_header_size, raw_request->data , raw_request->data_length);
1069 memcpy(raw_request->data, http_header, http_header_size);
1070 raw_request->data_length = raw_request->data_length + http_header_size;
1071
1072 return HPIPP_OK;
1073
1074 }
1075
1076 /*
1077 * 'removeChunkInfo()' - Remove Chunk length information from the response.
1078 */
removeChunkInfo(char * data,int * length)1079 HPIPP_RESULT removeChunkInfo(char* data, int *length)
1080 {
1081 char* chunksize_start = data;
1082 char* chunksize_end = NULL;
1083 int chunklen = 0;
1084 int remaining_bytes = *length;
1085 HPIPP_RESULT hr = HPIPP_OK;
1086 int newlength = *length;
1087
1088 do
1089 {
1090 chunksize_end = strstr(chunksize_start, CRLF);
1091 if(chunksize_end == NULL)
1092 {
1093 BUG("removeChunkInfo failed.\n");
1094 return HPIPP_ERROR;
1095 }
1096
1097 chunksize_end += CRLF_LENGTH;
1098 chunklen = strtol(chunksize_start, NULL, BASE_HEX); //Convert ChunkLength from ASCII hex format to an integer.
1099 remaining_bytes -= (chunksize_end -chunksize_start); //Update remmaining length
1100
1101 DBG("chunklen = [%d], remaining_bytes= [%d], newlength = [%d]\n", chunklen, remaining_bytes, newlength);
1102 if(chunklen > remaining_bytes)
1103 {
1104 BUG("RemoveChunkInfo failed.\n");
1105 return HPIPP_ERROR;
1106 }
1107
1108 memmove(chunksize_start, chunksize_end, remaining_bytes); //Delete chunklengthinfo from buffer by moving remaining_bytes data
1109 newlength -= (chunksize_end -chunksize_start); //Subtract size of chunklengthinfo from ipp data length
1110 chunksize_start += chunklen; //Move to next chunk
1111 remaining_bytes -= chunklen; //Update remmaining length
1112
1113 //Check if chunk data ends with CRLF. If so then remove that also.
1114 if(memcmp(chunksize_start, CRLF, CRLF_LENGTH) == 0)
1115 {
1116 remaining_bytes -= CRLF_LENGTH;
1117 newlength -= CRLF_LENGTH;
1118 memmove(chunksize_start, chunksize_start + CRLF_LENGTH, remaining_bytes);
1119 }
1120
1121 } while(chunklen);
1122
1123 //Set remaining_bytes buffer to 0 and update length of the buffer to newlength before returning.
1124 memset(data+newlength, 0, MAX_IPP_DATA_LENGTH - newlength);
1125 *length = newlength;
1126
1127 return hr;
1128 }
1129
1130
1131