Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/notifier/rss.c
1090 views
1
/*
2
* RSS notifier for CUPS.
3
*
4
* Copyright 2007-2015 by Apple Inc.
5
* Copyright 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 <cups/cups.h>
15
#include <sys/stat.h>
16
#include <cups/language.h>
17
#include <cups/string-private.h>
18
#include <cups/array.h>
19
#include <sys/select.h>
20
#include <cups/ipp-private.h> /* TODO: Update so we don't need this */
21
22
23
/*
24
* Structures...
25
*/
26
27
typedef struct _cups_rss_s /**** RSS message data ****/
28
{
29
int sequence_number; /* notify-sequence-number */
30
char *subject, /* Message subject/summary */
31
*text, /* Message text */
32
*link_url; /* Link to printer */
33
time_t event_time; /* When the event occurred */
34
} _cups_rss_t;
35
36
37
/*
38
* Local globals...
39
*/
40
41
static char *rss_password; /* Password for remote RSS */
42
43
44
/*
45
* Local functions...
46
*/
47
48
static int compare_rss(_cups_rss_t *a, _cups_rss_t *b);
49
static void delete_message(_cups_rss_t *msg);
50
static void load_rss(cups_array_t *rss, const char *filename);
51
static _cups_rss_t *new_message(int sequence_number, char *subject,
52
char *text, char *link_url,
53
time_t event_time);
54
static const char *password_cb(const char *prompt);
55
static int save_rss(cups_array_t *rss, const char *filename,
56
const char *baseurl);
57
static char *xml_escape(const char *s);
58
59
60
/*
61
* 'main()' - Main entry for the test notifier.
62
*/
63
64
int /* O - Exit status */
65
main(int argc, /* I - Number of command-line arguments */
66
char *argv[]) /* I - Command-line arguments */
67
{
68
int i; /* Looping var */
69
ipp_t *event; /* Event from scheduler */
70
ipp_state_t state; /* IPP event state */
71
char scheme[32], /* URI scheme ("rss") */
72
username[256], /* Username for remote RSS */
73
host[1024], /* Hostname for remote RSS */
74
resource[1024], /* RSS file */
75
*options; /* Options */
76
int port, /* Port number for remote RSS */
77
max_events; /* Maximum number of events */
78
http_t *http; /* Connection to remote server */
79
http_status_t status; /* HTTP GET/PUT status code */
80
char filename[1024], /* Local filename */
81
newname[1024]; /* filename.N */
82
cups_lang_t *language; /* Language information */
83
ipp_attribute_t *printer_up_time, /* Timestamp on event */
84
*notify_sequence_number,/* Sequence number */
85
*notify_printer_uri; /* Printer URI */
86
char *subject, /* Subject for notification message */
87
*text, /* Text for notification message */
88
link_url[1024], /* Link to printer */
89
link_scheme[32], /* Scheme for link */
90
link_username[256], /* Username for link */
91
link_host[1024], /* Host for link */
92
link_resource[1024]; /* Resource for link */
93
int link_port; /* Link port */
94
cups_array_t *rss; /* RSS message array */
95
_cups_rss_t *msg; /* RSS message */
96
char baseurl[1024]; /* Base URL */
97
fd_set input; /* Input set for select() */
98
struct timeval timeout; /* Timeout for select() */
99
int changed; /* Has the RSS data changed? */
100
int exit_status; /* Exit status */
101
102
103
fprintf(stderr, "DEBUG: argc=%d\n", argc);
104
for (i = 0; i < argc; i ++)
105
fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
106
107
/*
108
* See whether we are publishing this RSS feed locally or remotely...
109
*/
110
111
if (httpSeparateURI(HTTP_URI_CODING_ALL, argv[1], scheme, sizeof(scheme),
112
username, sizeof(username), host, sizeof(host), &port,
113
resource, sizeof(resource)) < HTTP_URI_OK)
114
{
115
fprintf(stderr, "ERROR: Bad RSS URI \"%s\"!\n", argv[1]);
116
return (1);
117
}
118
119
max_events = 20;
120
121
if ((options = strchr(resource, '?')) != NULL)
122
{
123
*options++ = '\0';
124
125
if (!strncmp(options, "max_events=", 11))
126
{
127
max_events = atoi(options + 11);
128
129
if (max_events <= 0)
130
max_events = 20;
131
}
132
}
133
134
rss = cupsArrayNew((cups_array_func_t)compare_rss, NULL);
135
136
if (host[0])
137
{
138
/*
139
* Remote feed, see if we can get the current file...
140
*/
141
142
int fd; /* Temporary file */
143
144
145
if ((rss_password = strchr(username, ':')) != NULL)
146
*rss_password++ = '\0';
147
148
cupsSetPasswordCB(password_cb);
149
cupsSetUser(username);
150
151
if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
152
{
153
fprintf(stderr, "ERROR: Unable to create temporary file: %s\n",
154
strerror(errno));
155
156
return (1);
157
}
158
159
if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL)) == NULL)
160
{
161
fprintf(stderr, "ERROR: Unable to connect to %s on port %d: %s\n",
162
host, port, strerror(errno));
163
164
close(fd);
165
unlink(filename);
166
167
return (1);
168
}
169
170
status = cupsGetFd(http, resource, fd);
171
172
close(fd);
173
174
if (status != HTTP_OK && status != HTTP_NOT_FOUND)
175
{
176
fprintf(stderr, "ERROR: Unable to GET %s from %s on port %d: %d %s\n",
177
resource, host, port, status, httpStatus(status));
178
179
httpClose(http);
180
unlink(filename);
181
182
return (1);
183
}
184
185
strlcpy(newname, filename, sizeof(newname));
186
187
httpAssembleURI(HTTP_URI_CODING_ALL, baseurl, sizeof(baseurl), "http",
188
NULL, host, port, resource);
189
}
190
else
191
{
192
const char *cachedir, /* CUPS_CACHEDIR */
193
*server_name, /* SERVER_NAME */
194
*server_port; /* SERVER_PORT */
195
196
197
http = NULL;
198
199
if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL)
200
cachedir = CUPS_CACHEDIR;
201
202
if ((server_name = getenv("SERVER_NAME")) == NULL)
203
server_name = "localhost";
204
205
if ((server_port = getenv("SERVER_PORT")) == NULL)
206
server_port = "631";
207
208
snprintf(filename, sizeof(filename), "%s/rss%s", cachedir, resource);
209
snprintf(newname, sizeof(newname), "%s.N", filename);
210
211
httpAssembleURIf(HTTP_URI_CODING_ALL, baseurl, sizeof(baseurl), "http",
212
NULL, server_name, atoi(server_port), "/rss%s", resource);
213
}
214
215
/*
216
* Load the previous RSS file, if any...
217
*/
218
219
load_rss(rss, filename);
220
221
changed = cupsArrayCount(rss) == 0;
222
223
/*
224
* Localize for the user's chosen language...
225
*/
226
227
language = cupsLangDefault();
228
229
/*
230
* Read events and update the RSS file until we are out of events.
231
*/
232
233
for (exit_status = 0, event = NULL;;)
234
{
235
if (changed)
236
{
237
/*
238
* Save the messages to the file again, uploading as needed...
239
*/
240
241
if (save_rss(rss, newname, baseurl))
242
{
243
if (http)
244
{
245
/*
246
* Upload the RSS file...
247
*/
248
249
if ((status = cupsPutFile(http, resource, filename)) != HTTP_CREATED)
250
fprintf(stderr, "ERROR: Unable to PUT %s from %s on port %d: %d %s\n",
251
resource, host, port, status, httpStatus(status));
252
}
253
else
254
{
255
/*
256
* Move the new RSS file over top the old one...
257
*/
258
259
if (rename(newname, filename))
260
fprintf(stderr, "ERROR: Unable to rename %s to %s: %s\n",
261
newname, filename, strerror(errno));
262
}
263
264
changed = 0;
265
}
266
}
267
268
/*
269
* Wait up to 30 seconds for an event...
270
*/
271
272
timeout.tv_sec = 30;
273
timeout.tv_usec = 0;
274
275
FD_ZERO(&input);
276
FD_SET(0, &input);
277
278
if (select(1, &input, NULL, NULL, &timeout) < 0)
279
continue;
280
else if (!FD_ISSET(0, &input))
281
{
282
fprintf(stderr, "DEBUG: %s is bored, exiting...\n", argv[1]);
283
break;
284
}
285
286
/*
287
* Read the next event...
288
*/
289
290
event = ippNew();
291
while ((state = ippReadFile(0, event)) != IPP_DATA)
292
{
293
if (state <= IPP_IDLE)
294
break;
295
}
296
297
if (state == IPP_ERROR)
298
fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
299
300
if (state <= IPP_IDLE)
301
break;
302
303
/*
304
* Collect the info from the event...
305
*/
306
307
printer_up_time = ippFindAttribute(event, "printer-up-time",
308
IPP_TAG_INTEGER);
309
notify_sequence_number = ippFindAttribute(event, "notify-sequence-number",
310
IPP_TAG_INTEGER);
311
notify_printer_uri = ippFindAttribute(event, "notify-printer-uri",
312
IPP_TAG_URI);
313
subject = cupsNotifySubject(language, event);
314
text = cupsNotifyText(language, event);
315
316
if (printer_up_time && notify_sequence_number && subject && text)
317
{
318
/*
319
* Create a new RSS message...
320
*/
321
322
if (notify_printer_uri)
323
{
324
httpSeparateURI(HTTP_URI_CODING_ALL,
325
notify_printer_uri->values[0].string.text,
326
link_scheme, sizeof(link_scheme),
327
link_username, sizeof(link_username),
328
link_host, sizeof(link_host), &link_port,
329
link_resource, sizeof(link_resource));
330
httpAssembleURI(HTTP_URI_CODING_ALL, link_url, sizeof(link_url),
331
"http", link_username, link_host, link_port,
332
link_resource);
333
}
334
335
msg = new_message(notify_sequence_number->values[0].integer,
336
xml_escape(subject), xml_escape(text),
337
notify_printer_uri ? xml_escape(link_url) : NULL,
338
printer_up_time->values[0].integer);
339
340
if (!msg)
341
{
342
fprintf(stderr, "ERROR: Unable to create message: %s\n",
343
strerror(errno));
344
exit_status = 1;
345
break;
346
}
347
348
/*
349
* Add it to the array...
350
*/
351
352
cupsArrayAdd(rss, msg);
353
354
changed = 1;
355
356
/*
357
* Trim the array as needed...
358
*/
359
360
while (cupsArrayCount(rss) > max_events)
361
{
362
msg = cupsArrayFirst(rss);
363
364
cupsArrayRemove(rss, msg);
365
366
delete_message(msg);
367
}
368
}
369
370
if (subject)
371
free(subject);
372
373
if (text)
374
free(text);
375
376
ippDelete(event);
377
event = NULL;
378
}
379
380
/*
381
* We only get here when idle or error...
382
*/
383
384
ippDelete(event);
385
386
if (http)
387
{
388
unlink(filename);
389
httpClose(http);
390
}
391
392
return (exit_status);
393
}
394
395
396
/*
397
* 'compare_rss()' - Compare two messages.
398
*/
399
400
static int /* O - Result of comparison */
401
compare_rss(_cups_rss_t *a, /* I - First message */
402
_cups_rss_t *b) /* I - Second message */
403
{
404
return (a->sequence_number - b->sequence_number);
405
}
406
407
408
/*
409
* 'delete_message()' - Free all memory used by a message.
410
*/
411
412
static void
413
delete_message(_cups_rss_t *msg) /* I - RSS message */
414
{
415
if (msg->subject)
416
free(msg->subject);
417
418
if (msg->text)
419
free(msg->text);
420
421
if (msg->link_url)
422
free(msg->link_url);
423
424
free(msg);
425
}
426
427
428
/*
429
* 'load_rss()' - Load an existing RSS feed file.
430
*/
431
432
static void
433
load_rss(cups_array_t *rss, /* I - RSS messages */
434
const char *filename) /* I - File to load */
435
{
436
FILE *fp; /* File pointer */
437
char line[4096], /* Line from file */
438
*subject, /* Subject */
439
*text, /* Text */
440
*link_url, /* Link URL */
441
*start, /* Start of element */
442
*end; /* End of element */
443
time_t event_time; /* Event time */
444
int sequence_number; /* Sequence number */
445
int in_item; /* In an item */
446
_cups_rss_t *msg; /* New message */
447
448
449
if ((fp = fopen(filename, "r")) == NULL)
450
{
451
if (errno != ENOENT)
452
fprintf(stderr, "ERROR: Unable to open %s: %s\n", filename,
453
strerror(errno));
454
455
return;
456
}
457
458
subject = NULL;
459
text = NULL;
460
link_url = NULL;
461
event_time = 0;
462
sequence_number = 0;
463
in_item = 0;
464
465
while (fgets(line, sizeof(line), fp))
466
{
467
if (strstr(line, "<item>"))
468
in_item = 1;
469
else if (strstr(line, "</item>") && in_item)
470
{
471
if (subject && text)
472
{
473
msg = new_message(sequence_number, subject, text, link_url,
474
event_time);
475
476
if (msg)
477
cupsArrayAdd(rss, msg);
478
479
}
480
else
481
{
482
if (subject)
483
free(subject);
484
485
if (text)
486
free(text);
487
488
if (link_url)
489
free(link_url);
490
}
491
492
subject = NULL;
493
text = NULL;
494
link_url = NULL;
495
event_time = 0;
496
sequence_number = 0;
497
in_item = 0;
498
}
499
else if (!in_item)
500
continue;
501
else if ((start = strstr(line, "<title>")) != NULL)
502
{
503
start += 7;
504
if ((end = strstr(start, "</title>")) != NULL)
505
{
506
*end = '\0';
507
subject = strdup(start);
508
}
509
}
510
else if ((start = strstr(line, "<description>")) != NULL)
511
{
512
start += 13;
513
if ((end = strstr(start, "</description>")) != NULL)
514
{
515
*end = '\0';
516
text = strdup(start);
517
}
518
}
519
else if ((start = strstr(line, "<link>")) != NULL)
520
{
521
start += 6;
522
if ((end = strstr(start, "</link>")) != NULL)
523
{
524
*end = '\0';
525
link_url = strdup(start);
526
}
527
}
528
else if ((start = strstr(line, "<pubDate>")) != NULL)
529
{
530
start += 9;
531
if ((end = strstr(start, "</pubDate>")) != NULL)
532
{
533
*end = '\0';
534
event_time = httpGetDateTime(start);
535
}
536
}
537
else if ((start = strstr(line, "<guid>")) != NULL)
538
sequence_number = atoi(start + 6);
539
}
540
541
if (subject)
542
free(subject);
543
544
if (text)
545
free(text);
546
547
if (link_url)
548
free(link_url);
549
550
fclose(fp);
551
}
552
553
554
/*
555
* 'new_message()' - Create a new RSS message.
556
*/
557
558
static _cups_rss_t * /* O - New message */
559
new_message(int sequence_number, /* I - notify-sequence-number */
560
char *subject, /* I - Subject/summary */
561
char *text, /* I - Text */
562
char *link_url, /* I - Link to printer */
563
time_t event_time) /* I - Date/time of event */
564
{
565
_cups_rss_t *msg; /* New message */
566
567
568
if ((msg = calloc(1, sizeof(_cups_rss_t))) == NULL)
569
{
570
#ifdef __clang_analyzer__
571
// These free calls are really unnecessary (a failure here ultimately causes
572
// an exit, which frees all memory much faster) but it makes Clang happy...
573
free(subject);
574
free(text);
575
free(link_url);
576
#endif // __clang_analyzer__
577
578
return (NULL);
579
}
580
581
msg->sequence_number = sequence_number;
582
msg->subject = subject;
583
msg->text = text;
584
msg->link_url = link_url;
585
msg->event_time = event_time;
586
587
return (msg);
588
}
589
590
591
/*
592
* 'password_cb()' - Return the cached password.
593
*/
594
595
static const char * /* O - Cached password */
596
password_cb(const char *prompt) /* I - Prompt string, unused */
597
{
598
(void)prompt;
599
600
return (rss_password);
601
}
602
603
604
/*
605
* 'save_rss()' - Save messages to a RSS file.
606
*/
607
608
static int /* O - 1 on success, 0 on failure */
609
save_rss(cups_array_t *rss, /* I - RSS messages */
610
const char *filename, /* I - File to save to */
611
const char *baseurl) /* I - Base URL */
612
{
613
FILE *fp; /* File pointer */
614
_cups_rss_t *msg; /* Current message */
615
char date[1024]; /* Current date */
616
char *href; /* Escaped base URL */
617
618
619
if ((fp = fopen(filename, "w")) == NULL)
620
{
621
fprintf(stderr, "ERROR: Unable to create %s: %s\n", filename,
622
strerror(errno));
623
return (0);
624
}
625
626
fchmod(fileno(fp), 0644);
627
628
fputs("<?xml version=\"1.0\"?>\n", fp);
629
fputs("<rss version=\"2.0\">\n", fp);
630
fputs(" <channel>\n", fp);
631
fputs(" <title>CUPS RSS Feed</title>\n", fp);
632
633
href = xml_escape(baseurl);
634
fprintf(fp, " <link>%s</link>\n", href);
635
free(href);
636
637
fputs(" <description>CUPS RSS Feed</description>\n", fp);
638
fputs(" <generator>" CUPS_SVERSION "</generator>\n", fp);
639
fputs(" <ttl>1</ttl>\n", fp);
640
641
fprintf(fp, " <pubDate>%s</pubDate>\n",
642
httpGetDateString2(time(NULL), date, sizeof(date)));
643
644
for (msg = (_cups_rss_t *)cupsArrayLast(rss);
645
msg;
646
msg = (_cups_rss_t *)cupsArrayPrev(rss))
647
{
648
char *subject = xml_escape(msg->subject);
649
char *text = xml_escape(msg->text);
650
651
fputs(" <item>\n", fp);
652
fprintf(fp, " <title>%s</title>\n", subject);
653
fprintf(fp, " <description>%s</description>\n", text);
654
if (msg->link_url)
655
fprintf(fp, " <link>%s</link>\n", msg->link_url);
656
fprintf(fp, " <pubDate>%s</pubDate>\n",
657
httpGetDateString2(msg->event_time, date, sizeof(date)));
658
fprintf(fp, " <guid>%d</guid>\n", msg->sequence_number);
659
fputs(" </item>\n", fp);
660
661
free(subject);
662
free(text);
663
}
664
665
fputs(" </channel>\n", fp);
666
fputs("</rss>\n", fp);
667
668
return (!fclose(fp));
669
}
670
671
672
/*
673
* 'xml_escape()' - Copy a string, escaping &, <, and > as needed.
674
*/
675
676
static char * /* O - Escaped string */
677
xml_escape(const char *s) /* I - String to escape */
678
{
679
char *e, /* Escaped string */
680
*eptr; /* Pointer into escaped string */
681
const char *sptr; /* Pointer into string */
682
size_t bytes; /* Bytes needed for string */
683
684
685
/*
686
* First figure out how many extra bytes we need...
687
*/
688
689
for (bytes = 0, sptr = s; *sptr; sptr ++)
690
if (*sptr == '&')
691
bytes += 4; /* &amp; */
692
else if (*sptr == '<' || *sptr == '>')
693
bytes += 3; /* &lt; and &gt; */
694
695
/*
696
* If there is nothing to escape, just strdup() it...
697
*/
698
699
if (bytes == 0)
700
return (strdup(s));
701
702
/*
703
* Otherwise allocate memory and copy...
704
*/
705
706
if ((e = malloc(bytes + 1 + strlen(s))) == NULL)
707
return (NULL);
708
709
for (eptr = e, sptr = s; *sptr; sptr ++)
710
if (*sptr == '&')
711
{
712
*eptr++ = '&';
713
*eptr++ = 'a';
714
*eptr++ = 'm';
715
*eptr++ = 'p';
716
*eptr++ = ';';
717
}
718
else if (*sptr == '<')
719
{
720
*eptr++ = '&';
721
*eptr++ = 'l';
722
*eptr++ = 't';
723
*eptr++ = ';';
724
}
725
else if (*sptr == '>')
726
{
727
*eptr++ = '&';
728
*eptr++ = 'g';
729
*eptr++ = 't';
730
*eptr++ = ';';
731
}
732
else
733
*eptr++ = *sptr;
734
735
*eptr = '\0';
736
737
return (e);
738
}
739
740