• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Quota routines for the CUPS scheduler.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright 2007-2011 by Apple Inc.
6  * Copyright 1997-2007 by Easy Software Products.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include "cupsd.h"
16 
17 
18 /*
19  * Local functions...
20  */
21 
22 static cupsd_quota_t	*add_quota(cupsd_printer_t *p, const char *username);
23 static int		compare_quotas(const cupsd_quota_t *q1,
24 			               const cupsd_quota_t *q2);
25 
26 
27 /*
28  * 'cupsdFindQuota()' - Find a quota record.
29  */
30 
31 cupsd_quota_t *				/* O - Quota data */
cupsdFindQuota(cupsd_printer_t * p,const char * username)32 cupsdFindQuota(
33     cupsd_printer_t *p,			/* I - Printer */
34     const char      *username)		/* I - User */
35 {
36   cupsd_quota_t	*q,			/* Quota data pointer */
37 		match;			/* Search data */
38   char		*ptr;			/* Pointer into username */
39 
40 
41   if (!p || !username)
42     return (NULL);
43 
44   strlcpy(match.username, username, sizeof(match.username));
45   if ((ptr = strchr(match.username, '@')) != NULL)
46     *ptr = '\0';			/* Strip @domain/@KDC */
47 
48   if ((q = (cupsd_quota_t *)cupsArrayFind(p->quotas, &match)) != NULL)
49     return (q);
50   else
51     return (add_quota(p, username));
52 }
53 
54 
55 /*
56  * 'cupsdFreeQuotas()' - Free quotas for a printer.
57  */
58 
59 void
cupsdFreeQuotas(cupsd_printer_t * p)60 cupsdFreeQuotas(cupsd_printer_t *p)	/* I - Printer */
61 {
62   cupsd_quota_t *q;			/* Current quota record */
63 
64 
65   if (!p)
66     return;
67 
68   for (q = (cupsd_quota_t *)cupsArrayFirst(p->quotas);
69        q;
70        q = (cupsd_quota_t *)cupsArrayNext(p->quotas))
71     free(q);
72 
73   cupsArrayDelete(p->quotas);
74 
75   p->quotas = NULL;
76 }
77 
78 
79 /*
80  * 'cupsdUpdateQuota()' - Update quota data for the specified printer and user.
81  */
82 
83 cupsd_quota_t *				/* O - Quota data */
cupsdUpdateQuota(cupsd_printer_t * p,const char * username,int pages,int k)84 cupsdUpdateQuota(
85     cupsd_printer_t *p,			/* I - Printer */
86     const char      *username,		/* I - User */
87     int             pages,		/* I - Number of pages */
88     int             k)			/* I - Number of kilobytes */
89 {
90   cupsd_quota_t		*q;		/* Quota data */
91   cupsd_job_t		*job;		/* Current job */
92   time_t		curtime;	/* Current time */
93   ipp_attribute_t	*attr;		/* Job attribute */
94 
95 
96   if (!p || !username)
97     return (NULL);
98 
99   if (!p->k_limit && !p->page_limit)
100     return (NULL);
101 
102   if ((q = cupsdFindQuota(p, username)) == NULL)
103     return (NULL);
104 
105   cupsdLogMessage(CUPSD_LOG_DEBUG,
106                   "cupsdUpdateQuota: p=%s username=%s pages=%d k=%d",
107                   p->name, username, pages, k);
108 
109   curtime = time(NULL);
110 
111   if (curtime < q->next_update)
112   {
113     q->page_count += pages;
114     q->k_count    += k;
115 
116     return (q);
117   }
118 
119   if (p->quota_period)
120     curtime -= p->quota_period;
121   else
122     curtime = 0;
123 
124   q->next_update = 0;
125   q->page_count  = 0;
126   q->k_count     = 0;
127 
128   for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
129        job;
130        job = (cupsd_job_t *)cupsArrayNext(Jobs))
131   {
132    /*
133     * We only care about the current printer/class and user...
134     */
135 
136     if (_cups_strcasecmp(job->dest, p->name) != 0 ||
137         _cups_strcasecmp(job->username, q->username) != 0)
138       continue;
139 
140    /*
141     * Make sure attributes are loaded; we always call cupsdLoadJob() to ensure
142     * the access_time member is updated so the job isn't unloaded right away...
143     */
144 
145     if (!cupsdLoadJob(job))
146       continue;
147 
148     if ((attr = ippFindAttribute(job->attrs, "time-at-completion",
149                                  IPP_TAG_INTEGER)) == NULL)
150       if ((attr = ippFindAttribute(job->attrs, "time-at-processing",
151                                    IPP_TAG_INTEGER)) == NULL)
152         attr = ippFindAttribute(job->attrs, "time-at-creation",
153                                 IPP_TAG_INTEGER);
154 
155     if (attr->values[0].integer < curtime)
156     {
157      /*
158       * This job is too old to count towards the quota, ignore it...
159       */
160 
161       if (JobAutoPurge && !job->printer && job->state_value > IPP_JOB_STOPPED)
162         cupsdDeleteJob(job, CUPSD_JOB_PURGE);
163 
164       continue;
165     }
166 
167     if (q->next_update == 0)
168       q->next_update = attr->values[0].integer + p->quota_period;
169 
170     if ((attr = ippFindAttribute(job->attrs, "job-media-sheets-completed",
171                                  IPP_TAG_INTEGER)) != NULL)
172       q->page_count += attr->values[0].integer;
173 
174     if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
175                                  IPP_TAG_INTEGER)) != NULL)
176       q->k_count += attr->values[0].integer;
177   }
178 
179   return (q);
180 }
181 
182 
183 /*
184  * 'add_quota()' - Add a quota record for this printer and user.
185  */
186 
187 static cupsd_quota_t *			/* O - Quota data */
add_quota(cupsd_printer_t * p,const char * username)188 add_quota(cupsd_printer_t *p,		/* I - Printer */
189           const char      *username)	/* I - User */
190 {
191   cupsd_quota_t	*q;			/* New quota data */
192   char		*ptr;			/* Pointer into username */
193 
194 
195   if (!p || !username)
196     return (NULL);
197 
198   if (!p->quotas)
199     p->quotas = cupsArrayNew((cups_array_func_t)compare_quotas, NULL);
200 
201   if (!p->quotas)
202     return (NULL);
203 
204   if ((q = calloc(1, sizeof(cupsd_quota_t))) == NULL)
205     return (NULL);
206 
207   strlcpy(q->username, username, sizeof(q->username));
208   if ((ptr = strchr(q->username, '@')) != NULL)
209     *ptr = '\0';			/* Strip @domain/@KDC */
210 
211   cupsArrayAdd(p->quotas, q);
212 
213   return (q);
214 }
215 
216 
217 /*
218  * 'compare_quotas()' - Compare two quota records...
219  */
220 
221 static int				/* O - Result of comparison */
compare_quotas(const cupsd_quota_t * q1,const cupsd_quota_t * q2)222 compare_quotas(const cupsd_quota_t *q1,	/* I - First quota record */
223                const cupsd_quota_t *q2)	/* I - Second quota record */
224 {
225   return (_cups_strcasecmp(q1->username, q2->username));
226 }
227