2
* "$Id: ipp.c 11221 2013-08-06 16:16:01Z msweet $"
4
* IPP backend for CUPS.
6
* Copyright 2007-2013 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
9
* These coded instructions, statements, and computer programs are the
10
* property of Apple Inc. and are protected by Federal copyright
11
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
12
* "LICENSE" which should have been included with this file. If this
13
* file is missing or damaged, see the license at "http://www.cups.org/".
15
* This file is subject to the Apple OS-Developed Software exception.
19
* main() - Send a file to the printer or server.
20
* cancel_job() - Cancel a print job.
21
* check_printer_state() - Check the printer state.
22
* compress_files() - Compress print files.
23
* monitor_printer() - Monitor the printer state.
24
* new_request() - Create a new print creation or validation request.
25
* password_cb() - Disable the password prompt for
26
* cupsDoFileRequest().
27
* report_attr() - Report an IPP attribute value.
28
* report_printer_state() - Report the printer state.
29
* run_as_user() - Run the IPP backend as the printing user.
30
* timeout_cb() - Handle HTTP timeouts.
31
* sigterm_handler() - Handle 'terminate' signals that stop the backend.
2
* "$Id: ipp.c 11353 2013-10-23 19:53:08Z msweet $"
4
* IPP backend for CUPS.
6
* Copyright 2007-2013 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
9
* These coded instructions, statements, and computer programs are the
10
* property of Apple Inc. and are protected by Federal copyright
11
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
12
* "LICENSE" which should have been included with this file. If this
13
* file is missing or damaged, see the license at "http://www.cups.org/".
15
* This file is subject to the Apple OS-Developed Software exception.
153
154
static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
154
155
const char *resource,
155
156
const char *user, int version);
157
static void compress_files(int num_files, char **files);
158
#endif /* HAVE_LIBZ */
159
157
static void *monitor_printer(_cups_monitor_t *monitor);
160
158
static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
161
159
const char *user, const char *title,
162
160
int num_options, cups_option_t *options,
163
161
const char *compression, int copies,
164
162
const char *format, _ppd_cache_t *pc,
165
164
ipp_attribute_t *media_col_sup,
166
ipp_attribute_t *doc_handling_sup);
165
ipp_attribute_t *doc_handling_sup,
166
int print_color_mode);
167
167
static const char *password_cb(const char *prompt, http_t *http,
168
168
const char *method, const char *resource,
170
static const char *quote_string(const char *s, char *q, size_t qsize);
170
171
static void report_attr(ipp_attribute_t *attr);
171
172
static void report_printer_state(ipp_t *ipp);
172
173
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
230
231
prev_delay; /* Previous delay */
231
232
const char *compression; /* Compression mode */
232
233
int waitjob, /* Wait for job complete? */
234
waitjob_tries = 0, /* Number of times we've waited */
233
235
waitprinter; /* Wait for printer ready? */
234
236
_cups_monitor_t monitor; /* Monitoring data */
235
237
ipp_attribute_t *job_id_attr; /* job-id attribute */
236
238
int job_id; /* job-id value */
237
239
ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
238
240
ipp_attribute_t *job_state; /* job-state */
242
ipp_attribute_t *compression_sup; /* compression-supported */
243
#endif /* HAVE_LIBZ */
239
244
ipp_attribute_t *copies_sup; /* copies-supported */
240
245
ipp_attribute_t *cups_version; /* cups-version */
241
246
ipp_attribute_t *format_sup; /* document-format-supported */
247
ipp_attribute_t *job_auth; /* job-authorization-uri */
242
248
ipp_attribute_t *media_col_sup; /* media-col-supported */
243
249
ipp_attribute_t *operations_sup; /* operations-supported */
244
250
ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
905
919
if (version >= 20)
907
_cupsLangPrintFilter(stderr, "INFO",
908
_("The printer does not support IPP/%d.%d, trying "
909
"IPP/%s."), version / 10, version % 10, "1.1");
921
_cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
923
"DEBUG: The printer does not support IPP/%d.%d, trying "
924
"IPP/1.1.\n", version / 10, version % 10);
914
_cupsLangPrintFilter(stderr, "INFO",
915
_("The printer does not support IPP/%d.%d, trying "
916
"IPP/%s."), version / 10, version % 10, "1.0");
929
_cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
931
"DEBUG: The printer does not support IPP/%d.%d, trying "
932
"IPP/1.0.\n", version / 10, version % 10);
1017
1033
* Check for supported attributes...
1037
if ((compression_sup = ippFindAttribute(supported, "compression-supported",
1038
IPP_TAG_KEYWORD)) != NULL)
1041
* Check whether the requested compression is supported and/or default to
1042
* compression if supported...
1045
if (compression && !ippContainsString(compression_sup, compression))
1047
fprintf(stderr, "DEBUG: Printer does not support the requested "
1048
"compression value \"%s\".\n", compression);
1051
else if (!compression)
1053
if (ippContainsString(compression_sup, "gzip"))
1054
compression = "gzip";
1055
else if (ippContainsString(compression_sup, "deflate"))
1056
compression = "deflate";
1059
fprintf(stderr, "DEBUG: Automatically using \"%s\" compression.\n",
1063
#endif /* HAVE_LIBZ */
1020
1065
if ((copies_sup = ippFindAttribute(supported, "copies-supported",
1021
1066
IPP_TAG_RANGE)) != NULL)
1335
1384
request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
1336
1385
monitor.job_name, num_options, options, compression,
1337
copies_sup ? copies : 1, document_format, pc,
1338
media_col_sup, doc_handling_sup);
1386
copies_sup ? copies : 1, document_format, pc, ppd,
1387
media_col_sup, doc_handling_sup, print_color_mode);
1340
ippDelete(cupsDoRequest(http, request, resource));
1389
response = cupsDoRequest(http, request, resource);
1342
1391
ipp_status = cupsLastError();
1344
1393
fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1345
1394
ippErrorString(ipp_status), cupsLastErrorString());
1396
if ((job_auth = ippFindAttribute(response, "job-authorization-uri",
1397
IPP_TAG_URI)) != NULL)
1398
num_options = cupsAddOption("job-authorization-uri",
1399
ippGetString(job_auth, 0, NULL), num_options,
1402
ippDelete(response);
1347
1404
if (job_canceled)
1350
if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1407
if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
1408
ipp_status == IPP_STATUS_ERROR_BUSY)
1352
1410
_cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
1355
else if (ipp_status == IPP_DOCUMENT_FORMAT)
1413
else if (ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED ||
1414
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1415
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1416
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1417
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
1357
else if (ipp_status == IPP_FORBIDDEN ||
1358
ipp_status == IPP_AUTHENTICATION_CANCELED)
1419
else if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
1420
ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
1360
1422
const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1361
1423
/* WWW-Authenticate field value */
1985
2079
ipp_status <= IPP_OK_CONFLICT)
1986
2080
fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
2082
if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED)
2083
fputs("JOBSTATE: account-info-needed\n", stderr);
2084
else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED)
2085
fputs("JOBSTATE: account-closed\n", stderr);
2086
else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED)
2087
fputs("JOBSTATE: account-limit-reached\n", stderr);
2088
else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2089
fputs("JOBSTATE: account-authorization-failed\n", stderr);
1988
2091
if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1989
2092
ipp_status == IPP_AUTHENTICATION_CANCELED)
1990
2093
return (CUPS_BACKEND_AUTH_REQUIRED);
2094
else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
2095
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
2096
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
2097
ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2098
return (CUPS_BACKEND_HOLD);
1991
2099
else if (ipp_status == IPP_INTERNAL_ERROR)
1992
2100
return (CUPS_BACKEND_STOP);
1993
2101
else if (ipp_status == IPP_CONFLICT)
1994
2102
return (CUPS_BACKEND_FAILED);
1995
2103
else if (ipp_status == IPP_REQUEST_VALUE ||
2104
ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
1996
2105
ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
1998
2107
if (ipp_status == IPP_REQUEST_VALUE)
2117
* 'compress_files()' - Compress print files.
2121
compress_files(int num_files, /* I - Number of files */
2122
char **files) /* I - Files */
2124
int i, /* Looping var */
2125
fd; /* Temporary file descriptor */
2126
ssize_t bytes; /* Bytes read/written */
2127
size_t total; /* Total bytes read */
2128
cups_file_t *in, /* Input file */
2129
*out; /* Output file */
2130
struct stat outinfo; /* Output file information */
2131
char filename[1024], /* Temporary filename */
2132
buffer[32768]; /* Copy buffer */
2135
fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
2136
for (i = 0; i < num_files; i ++)
2138
if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
2140
_cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
2141
exit(CUPS_BACKEND_FAILED);
2144
if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
2146
_cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
2147
exit(CUPS_BACKEND_FAILED);
2150
if ((in = cupsFileOpen(files[i], "r")) == NULL)
2152
_cupsLangPrintError("ERROR", _("Unable to open print file"));
2154
exit(CUPS_BACKEND_FAILED);
2158
while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2159
if (cupsFileWrite(out, buffer, bytes) < bytes)
2161
_cupsLangPrintError("ERROR",
2162
_("Unable to generate compressed print file"));
2165
exit(CUPS_BACKEND_FAILED);
2173
files[i] = strdup(filename);
2175
if (!stat(filename, &outinfo))
2177
"DEBUG: File %d compressed to %.1f%% of original size, "
2178
CUPS_LLFMT " bytes...\n",
2179
i + 1, 100.0 * outinfo.st_size / total,
2180
CUPS_LLCAST outinfo.st_size);
2183
#endif /* HAVE_LIBZ */
2187
2228
* 'monitor_printer()' - Monitor the printer state.
2380
if ((attr = ippFindAttribute(response, "job-state-reasons",
2381
IPP_TAG_KEYWORD)) != NULL)
2383
int i, new_reasons = 0; /* Looping var, new reasons */
2385
for (i = 0; i < attr->num_values; i ++)
2387
if (!strcmp(attr->values[i].string.text,
2388
"account-authorization-failed"))
2389
new_reasons |= _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED;
2390
else if (!strcmp(attr->values[i].string.text, "account-closed"))
2391
new_reasons |= _CUPS_JSR_ACCOUNT_CLOSED;
2392
else if (!strcmp(attr->values[i].string.text, "account-info-needed"))
2393
new_reasons |= _CUPS_JSR_ACCOUNT_INFO_NEEDED;
2394
else if (!strcmp(attr->values[i].string.text,
2395
"account-limit-reached"))
2396
new_reasons |= _CUPS_JSR_ACCOUNT_LIMIT_REACHED;
2397
else if (!strcmp(attr->values[i].string.text, "job-password-wait"))
2398
new_reasons |= _CUPS_JSR_JOB_PASSWORD_WAIT;
2399
else if (!strcmp(attr->values[i].string.text, "job-release-wait"))
2400
new_reasons |= _CUPS_JSR_JOB_RELEASE_WAIT;
2403
if (new_reasons != monitor->job_reasons)
2405
if (new_reasons & _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED)
2406
fputs("JOBSTATE: account-authorization-failed\n", stderr);
2407
else if (new_reasons & _CUPS_JSR_ACCOUNT_CLOSED)
2408
fputs("JOBSTATE: account-closed\n", stderr);
2409
else if (new_reasons & _CUPS_JSR_ACCOUNT_INFO_NEEDED)
2410
fputs("JOBSTATE: account-info-needed\n", stderr);
2411
else if (new_reasons & _CUPS_JSR_ACCOUNT_LIMIT_REACHED)
2412
fputs("JOBSTATE: account-limit-reached\n", stderr);
2413
else if (new_reasons & _CUPS_JSR_JOB_PASSWORD_WAIT)
2414
fputs("JOBSTATE: job-password-wait\n", stderr);
2415
else if (new_reasons & _CUPS_JSR_JOB_RELEASE_WAIT)
2416
fputs("JOBSTATE: job-release-wait\n", stderr);
2418
fputs("JOBSTATE: job-printing\n", stderr);
2420
monitor->job_reasons = new_reasons;
2336
2424
ippDelete(response);
2338
2426
fprintf(stderr, "DEBUG: (monitor) job-state=%s\n",
2471
2566
int num_finishings = 0, /* Number of finishing values */
2472
2567
finishings[10]; /* Finishing enum values */
2568
ppd_choice_t *choice; /* Marked choice */
2475
2571
* Send standard IPP attributes...
2574
fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr);
2577
(keyword = cupsGetOption("job-password", num_options,
2580
ippAddOctetString(request, IPP_TAG_OPERATION, "job-password",
2581
keyword, strlen(keyword));
2583
if ((keyword = cupsGetOption("job-password-encryption", num_options,
2587
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2588
"job-password-encryption", NULL, keyword);
2593
if ((keyword = cupsGetOption("job-account-id", num_options,
2595
keyword = cupsGetOption("job-billing", num_options, options);
2598
ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id",
2602
if (pc->accounting_user_id)
2604
if ((keyword = cupsGetOption("job-accounting-user-id", num_options,
2609
ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME,
2610
"job-accounting-user-id", NULL, keyword);
2613
for (mandatory = (char *)cupsArrayFirst(pc->mandatory);
2615
mandatory = (char *)cupsArrayNext(pc->mandatory))
2617
if (strcmp(mandatory, "copies") &&
2618
strcmp(mandatory, "destination-uris") &&
2619
strcmp(mandatory, "finishings") &&
2620
strcmp(mandatory, "job-account-id") &&
2621
strcmp(mandatory, "job-accounting-user-id") &&
2622
strcmp(mandatory, "job-password") &&
2623
strcmp(mandatory, "job-password-encryption") &&
2624
strcmp(mandatory, "media") &&
2625
strncmp(mandatory, "media-col", 9) &&
2626
strcmp(mandatory, "multiple-document-handling") &&
2627
strcmp(mandatory, "output-bin") &&
2628
strcmp(mandatory, "print-color-mode") &&
2629
strcmp(mandatory, "print-quality") &&
2630
strcmp(mandatory, "sides") &&
2631
(keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
2633
_ipp_option_t *opt = _ippFindOption(mandatory);
2635
ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
2640
case IPP_TAG_INTEGER :
2642
ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory,
2645
case IPP_TAG_BOOLEAN :
2646
ippAddBoolean(request, IPP_TAG_JOB, mandatory,
2647
!_cups_strcasecmp(keyword, "true"));
2649
case IPP_TAG_RANGE :
2651
int lower, upper; /* Range */
2653
if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
2654
lower = upper = atoi(keyword);
2656
ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
2659
case IPP_TAG_STRING :
2660
ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword,
2664
ippAddString(request, IPP_TAG_JOB, value_tag, mandatory,
2478
2671
if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2479
2672
keyword = cupsGetOption("media", num_options, options);
2534
2727
if ((keyword = cupsGetOption("output-bin", num_options,
2535
2728
options)) == NULL)
2536
keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2730
if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
2731
keyword = _ppdCacheGetBin(pc, choice->choice);
2540
2735
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2541
2736
NULL, keyword);
2543
if ((keyword = cupsGetOption("output-mode", num_options,
2738
color_attr_name = print_color_mode ? "print-color-mode" : "output-mode";
2740
if ((keyword = cupsGetOption("print-color-mode", num_options,
2544
2741
options)) != NULL)
2545
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2742
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name,
2546
2743
NULL, keyword);
2547
else if ((keyword = cupsGetOption("ColorModel", num_options,
2744
else if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
2550
if (!_cups_strcasecmp(keyword, "Gray"))
2551
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2552
NULL, "monochrome");
2746
if (!_cups_strcasecmp(choice->choice, "Gray"))
2747
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2748
color_attr_name, NULL, "monochrome");
2554
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2750
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2751
color_attr_name, NULL, "color");
2558
2754
if ((keyword = cupsGetOption("print-quality", num_options,
2559
2755
options)) != NULL)
2560
2756
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2561
2757
atoi(keyword));
2562
else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2758
else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
2565
if (!_cups_strcasecmp(keyword, "draft"))
2760
if (!_cups_strcasecmp(choice->choice, "draft"))
2566
2761
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2567
2762
IPP_QUALITY_DRAFT);
2568
else if (!_cups_strcasecmp(keyword, "normal"))
2763
else if (!_cups_strcasecmp(choice->choice, "normal"))
2569
2764
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2570
2765
IPP_QUALITY_NORMAL);
2571
else if (!_cups_strcasecmp(keyword, "high"))
2766
else if (!_cups_strcasecmp(choice->choice, "high"))
2572
2767
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2573
2768
IPP_QUALITY_HIGH);
2577
2772
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2578
2773
NULL, keyword);
2579
2774
else if (pc->sides_option &&
2580
(keyword = cupsGetOption(pc->sides_option, num_options,
2775
(choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
2583
if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2777
if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
2584
2778
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2585
2779
NULL, "one-sided");
2586
else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2780
else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
2587
2781
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2588
2782
NULL, "two-sided-long-edge");
2589
if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2783
if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
2590
2784
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2591
2785
NULL, "two-sided-short-edge");
2699
2895
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2898
fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10,
2899
ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2900
for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(request);
2902
attr = ippNextAttribute(request))
2904
const char *name = ippGetName(attr);
2908
group = IPP_TAG_ZERO;
2912
if (group != ippGetGroupTag(attr))
2914
group = ippGetGroupTag(attr);
2915
fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
2918
ippAttributeString(attr, buffer, sizeof(buffer));
2919
fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
2920
ippGetCount(attr) > 1 ? "1setOf " : "",
2921
ippTagString(ippGetValueTag(attr)), buffer);
2924
fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
2702
2926
return (request);