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