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