1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-07 23:32:53 +02:00
2010-01-24 09:19:39 +00:00

452 lines
11 KiB
C

/* rss2ical.c: Turn RSS into ical
*
* Copyright (C) 2008, David Beckett http://www.dajobe.org/
*
* This package is Free Software and part of Redland http://librdf.org/
*
* It is licensed under the following three licenses as alternatives:
* 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
* 2. GNU General Public License (GPL) V2 or any newer version
* 3. Apache License, V2.0 or any newer version
*
* You may not use this file except in compliance with at least one of
* the above three licenses.
*
* See LICENSE.html or LICENSE.txt at the top of this package for the
* complete terms and further detail along with the license texts for
* the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
*
*
* USAGE: rss2ical [URI of RSS/Atom Feed] [Calendar Title] > result.ics
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <redland.h>
#undef RSS2ICAL_DEBUG
#define DO_ESCAPE_NL 0
static const unsigned char* get_items_query=(const unsigned char*)
"PREFIX rss: <http://purl.org/rss/1.0/>\n\
PREFIX dc: <http://purl.org/dc/elements/1.1/>\n\
PREFIX content: <http://web.resource.org/rss/1.0/modules/content/>\n\
SELECT ?item ?date ?title ?description ?creator\n\
WHERE {\n\
?item a rss:item;\n\
dc:date ?date;\n\
rss:title ?title;\n\
rss:description ?description .\n\
OPTIONAL { ?item dc:creator ?creator . }\n\
}";
/*
Removed for now as OPTIONAL in Rasqal is fragile/broken:
OPTIONAL { ?item dc:source ?source } \n\
OPTIONAL { ?item content:encoded ?htmldesc } \n \
*/
/* %s-prod id %s-cal name id %s-rel cal id %s-tzone %s-tzone */
static const char *ical_header_format="\
BEGIN:VCALENDAR\r\n\
VERSION:2.0\r\n\
PRODID:%s\r\n\
X-WR-CALNAME:%s\r\n\
X-WR-RELCALID:%s\r\n\
X-WR-TIMEZONE:%s\r\n\
CALSCALE:GREGORIAN\r\n\
METHOD:PUBLISH\r\n\
BEGIN:VTIMEZONE\r\n\
TZID:%s\r\n\
BEGIN:DAYLIGHT\r\n\
DTSTART:20060326T020000\r\n\
TZOFFSETTO:+0100\r\n\
TZOFFSETFROM:+0000\r\n\
TZNAME:BST\r\n\
END:DAYLIGHT\r\n\
BEGIN:STANDARD\r\n\
DTSTART:20061029T020000\r\n\
TZOFFSETTO:+0000\r\n\
TZOFFSETFROM:+0100\r\n\
TZNAME:GMT\r\n\
END:STANDARD\r\n\
END:VTIMEZONE\r\n\
";
static const char *ical_footer_format="\
END:VCALENDAR\r\n\
";
static const char *tzone="Europe/London";
static char *program=NULL;
static void
ical_print(FILE *fh, const char *line)
{
fputs(line, fh);
fwrite("\r\n", 1, 2, fh);
}
static void
ical_format(FILE *fh, const char *key, const char *attr,
const char *escapes, const unsigned char *value)
{
int col=0;
int i=0;
size_t len;
int c;
int lineno=0;
len=strlen(key);
fwrite(key, 1, len, fh);
col += len;
if(attr) {
fputc(';', fh);
col++;
len=strlen(attr);
fwrite(attr, 1, len, fh);
col += len;
}
fputc(':', fh);
col++;
for(i=0; (c=value[i]); i++) {
if(col == 75) {
fwrite("\r\n ", 1, 3, fh);
col=0;
lineno++;
}
if(c == '\\' ||
(escapes && (strchr(escapes, c) != NULL))) {
fputc('\\', fh);
col++;
}
if(c == '\n') {
#ifdef DO_ESCAPE_NL
fputc('\\', fh);
col++;
c='n';
#else
c=' ';
#endif
}
fputc(c, fh);
col++;
}
fwrite("\r\n", 1, 2, fh);
}
static unsigned char*
iso2vcaldate(const unsigned char* iso_date)
{
unsigned char* vcaldate;
unsigned char c;
int i, j;
/* YYYY-MM-DDTHH:MM:SSZ }
* YYYY-MM-DDTHH:MMZ } to YYYYMMDDTHHMMSSZ
* ... }
*/
vcaldate=(unsigned char*)malloc(17);
strncpy((char*)vcaldate, "00000000T000000Z", 17);
for(i=0, j=0; (c=iso_date[i]); i++) {
if(c == 'Z')
break;
if(c != ':' && c != '-')
vcaldate[j++]=iso_date[i];
}
return vcaldate;
}
static unsigned char*
remove_html_entities(unsigned char* html_desc, size_t len)
{
int i, j;
unsigned char* description;
unsigned char c;
description=malloc(len+1);
/* Trim leading white space */
for(i=0, j=0; (c=html_desc[i]) && (c == ' ' || c == '\n'); i++)
;
for(; (c=html_desc[i]); i++) {
if(c == '\n')
c=' ';
else if(c == '&') {
c=html_desc[++i];
/* Expand &#123; to UTF-8 for codepoint decimal 123 */
if(c == '#') {
unsigned char *orig_p=&html_desc[i];
unsigned long d=0;
int ulen;
i++;
while(c) {
c=html_desc[i++];
if(c<'0' || c>'9')
break;
d=d*10;
d+= (c - '0');
}
if(c != ';') {
fprintf(stderr, "%s: Expected ';' after &#NNN in '%s'\n",
program, orig_p);
abort();
}
#ifdef RSS2ICAL_DEBUG
fprintf(stderr, "%s: Encoding char %d\n", program, d);
#endif
ulen=raptor_unicode_char_to_utf8(d, &description[j]);
#ifdef RSS2ICAL_DEBUG
fprintf(stderr, "%s: UTF-8 len was %d\n", program, ulen);
#endif
j+= ulen;
} else {
const char* here=(const char*)&html_desc[i];
if(!strncmp(here, "amp;", 4)) {
i+= 4;
c='&';
} else if(!strncmp(here, "lt;", 3)) {
i+= 3;
c='<';
} else if(!strncmp(here, "gt;", 3)) {
i+= 3;
c='>';
}
description[j++]=c;
}
continue;
}
description[j++]=c;
}
description[j]='\0';
return description;
}
static char*
uri_to_calid(librdf_uri* uri)
{
size_t len;
unsigned char* uri_string;
char* calid;
unsigned char c;
int i, j;
uri_string=librdf_uri_as_counted_string(uri, &len);
calid=(char*)malloc(len+1);
for(i=0, j=0; (c=uri_string[i]); i++) {
if(c <= 0x20 || c >= 0x7f)
continue;
if(c == '\\' || c == ';' || c == ':' || c == '\"' || c == ',' ||
c == '/')
calid[j++]='-';
else
calid[j++]=c;
}
return calid;
}
int
main(int argc, char *argv[])
{
librdf_world* world;
librdf_storage* storage;
librdf_model* model;
librdf_parser* parser;
librdf_query* query;
librdf_query_results* results;
librdf_uri *uri;
char *p;
char* calendar_name;
char* calendar_id;
program=argv[0];
if((p=strrchr(program, '/')))
program=p+1;
else if((p=strrchr(program, '\\')))
program=p+1;
argv[0]=program;
if(argc != 3) {
fprintf(stderr, "USAGE: %s RSS-URI CALENDAR-NAME\n", program);
return 1;
}
world=librdf_new_world();
librdf_world_open(world);
storage=librdf_new_storage(world, "memory", NULL, NULL);
model=librdf_new_model(world, storage, NULL);
if(!model || !storage) {
fprintf(stderr, "%s: Failed to make model or storage\n", program);
return 1;
}
uri=librdf_new_uri(world, (unsigned char*)argv[1]);
calendar_name=argv[2];
fprintf(stderr, "%s: Reading RSS from %s\n", program,
librdf_uri_as_string(uri));
parser=librdf_new_parser(world, "rss-tag-soup", NULL, NULL);
librdf_parser_parse_into_model(parser, uri, NULL, model);
librdf_free_parser(parser);
fprintf(stderr, "%s: Querying model for RSS items\n", program);
query=librdf_new_query(world, "sparql", NULL, get_items_query, uri);
results=librdf_model_query_execute(model, query);
if(!results) {
fprintf(stderr, "%s: Query of model with SPARQL query '%s' failed\n",
program, get_items_query);
return 1;
}
fprintf(stderr, "%s: Processing results\n", program);
calendar_id=uri_to_calid(uri);
fprintf(stdout, ical_header_format,
"-//librdf/rss2ical Version 1.0//EN",
calendar_name,
calendar_id,
tzone,
tzone);
free(calendar_id);
while(!librdf_query_results_finished(results)) {
unsigned char *uid=NULL;
unsigned char *summary=NULL;
unsigned char *dtstart=NULL;
unsigned char *location=NULL;
unsigned char *html_desc=NULL;
size_t html_desc_len;
unsigned char *description=NULL;
unsigned char *url=NULL;
librdf_node* node;
char *creator=NULL;
node=librdf_query_results_get_binding_value_by_name(results, "item");
if(!librdf_node_is_resource(node))
goto nextresult;
url=librdf_uri_as_string(librdf_node_get_uri(node));
/* uid is a new string */
uid=(unsigned char*)uri_to_calid(librdf_node_get_uri(node));
node=librdf_query_results_get_binding_value_by_name(results, "date");
if(!librdf_node_is_literal(node)) {
fprintf(stderr, "%s: Date in item %s is not a literal\n", program, url);
goto nextresult;
}
dtstart=librdf_node_get_literal_value(node);
dtstart=iso2vcaldate(dtstart);
node=librdf_query_results_get_binding_value_by_name(results, "title");
if(!librdf_node_is_literal(node))
summary=(unsigned char*)"(No Title)";
else
summary=librdf_node_get_literal_value(node);
node=librdf_query_results_get_binding_value_by_name(results, "htmldesc");
if(node && librdf_node_is_literal(node))
html_desc=librdf_node_get_literal_value_as_counted_string(node,
&html_desc_len);
if(!description) {
node=librdf_query_results_get_binding_value_by_name(results, "description");
if(node && librdf_node_is_literal(node))
html_desc=librdf_node_get_literal_value_as_counted_string(node,
&html_desc_len);
}
if(html_desc) {
description=remove_html_entities(html_desc, html_desc_len);
}
node=librdf_query_results_get_binding_value_by_name(results, "source");
if(node && librdf_node_is_literal(node))
location=librdf_node_get_literal_value(node);
node=librdf_query_results_get_binding_value_by_name(results, "creator");
if(node && librdf_node_is_literal(node)) {
unsigned char *value=librdf_node_get_literal_value(node);
creator=malloc(strlen((const char*)value)+6);
sprintf(creator, "CN=\"%s\"", value);
}
ical_print(stdout, "BEGIN:VEVENT");
ical_format(stdout, "UID", NULL, NULL, uid);
ical_format(stdout, "SUMMARY", NULL, NULL, summary);
if(location)
ical_format(stdout, "LOCATION", NULL, NULL, location);
if(creator) {
ical_format(stdout, "ATTENDEE", creator, NULL,
(const unsigned char*)"invalid:nomail");
free(creator);
}
ical_format(stdout, "DTSTART", NULL, NULL, dtstart);
ical_format(stdout, "DTSTAMP", NULL, NULL, dtstart);
ical_format(stdout, "LAST-MODIFIED", NULL, NULL, dtstart);
ical_format(stdout, "DESCRIPTION", NULL, ";,\"", description);
if(url)
ical_format(stdout, "URL", "VALUE=URI", ";", url);
ical_format(stdout, "CLASS", NULL, NULL, (const unsigned char*)"PUBLIC");
ical_print(stdout, "END:VEVENT");
free(description);
free(uid);
nextresult:
librdf_query_results_next(results);
}
fputs(ical_footer_format, stdout);
librdf_free_query_results(results);
librdf_free_query(query);
librdf_free_uri(uri);
librdf_free_model(model);
librdf_free_storage(storage);
librdf_free_world(world);
/* keep gcc -Wall happy */
return(0);
}