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