Run code through indent

This mainly changes the indentation level from 8 to 4 spaces,
because I have difficulty working with the wide indents.
There should be no non-whitespace changes here.
This commit is contained in:
Mike Sharov 2021-04-09 17:56:41 -04:00
parent 5b73b0dad8
commit 23a08804f8
32 changed files with 5565 additions and 5538 deletions

591
about.c
View File

@ -24,342 +24,355 @@
// Santa Hunta
struct shot {
unsigned x;
unsigned y;
bool fired;
unsigned x;
unsigned y;
bool fired;
};
struct santa {
int x;
unsigned y;
unsigned height;
unsigned length;
unsigned speed;
unsigned anim;
const char* gfx;
const char* gfx_line2;
const char* altgfx;
const char* altgfx_line2;
int x;
unsigned y;
unsigned height;
unsigned length;
unsigned speed;
unsigned anim;
const char* gfx;
const char* gfx_line2;
const char* altgfx;
const char* altgfx_line2;
};
struct scoreDisplay {
int score;
int x;
int y;
int rounds;
int score;
int x;
int y;
int rounds;
};
//----------------------------------------------------------------------
// Generate a random number in given range
static unsigned nrand (unsigned r) { return rand() % r; }
static unsigned nrand (unsigned r)
{
return rand() % r;
}
//----------------------------------------------------------------------
// Santa Hunta
static void SHDrawGun (unsigned gun_pos) {
move (LINES-3, 0);
clrtoeol();
move (LINES-2, 0);
clrtoeol();
static void SHDrawGun (unsigned gun_pos)
{
move (LINES - 3, 0);
clrtoeol();
move (LINES - 2, 0);
clrtoeol();
attron (WA_BOLD);
mvaddstr (LINES-3, gun_pos-3, "___/\\___");
mvaddstr (LINES-2, gun_pos-3, "|######|");
attroff (WA_BOLD);
attron (WA_BOLD);
mvaddstr (LINES - 3, gun_pos - 3, "___/\\___");
mvaddstr (LINES - 2, gun_pos - 3, "|######|");
attroff (WA_BOLD);
}
static void SHDrawStatus (void) {
attron (WA_BOLD);
mvprintw (LINES-1, 1, _("Move: cursor or %c/%c; shoot: space; quit: %c"),
_settings.keybindings.prev, _settings.keybindings.next, _settings.keybindings.quit);
move (LINES-1, COLS-1);
attroff (WA_BOLD);
static void SHDrawStatus (void)
{
attron (WA_BOLD);
mvprintw (LINES - 1, 1, _("Move: cursor or %c/%c; shoot: space; quit: %c"), _settings.keybindings.prev, _settings.keybindings.next, _settings.keybindings.quit);
move (LINES - 1, COLS - 1);
attroff (WA_BOLD);
}
static void SHDrawScore (int score, int level) {
attron (WA_REVERSE);
mvhline (0, 0, ' ', COLS);
mvaddstr (0, 1, "Santa Hunta!");
static void SHDrawScore (int score, int level)
{
attron (WA_REVERSE);
mvhline (0, 0, ' ', COLS);
mvaddstr (0, 1, "Santa Hunta!");
char scorestr[16];
snprintf (scorestr, sizeof(scorestr), _("Score: %d"), score);
mvaddstr (0, COLS-strlen(scorestr)-1, scorestr);
char scorestr[16];
snprintf (scorestr, sizeof (scorestr), _("Score: %d"), score);
mvaddstr (0, COLS - strlen (scorestr) - 1, scorestr);
char levelstr[16];
snprintf (levelstr, sizeof(levelstr), _("Level: %d"), level);
mvaddstr (0, (COLS-strlen(levelstr))/2u, levelstr);
char levelstr[16];
snprintf (levelstr, sizeof (levelstr), _("Level: %d"), level);
mvaddstr (0, (COLS - strlen (levelstr)) / 2u, levelstr);
attroff (WA_REVERSE);
attroff (WA_REVERSE);
}
static void SHDrawProjectile (struct shot shot) {
attron (WA_BOLD);
mvaddstr (shot.y, shot.x, "|");
attroff (WA_BOLD);
static void SHDrawProjectile (struct shot shot)
{
attron (WA_BOLD);
mvaddstr (shot.y, shot.x, "|");
attroff (WA_BOLD);
}
static void SHDrawSanta (struct santa santa) {
int len = COLS - santa.x;
if (santa.anim == 0) {
if (santa.x < 0)
mvaddnstr (santa.y, 0, santa.gfx+abs(santa.x), len);
else
mvaddnstr (santa.y, santa.x, santa.gfx, len);
if (santa.x < 0)
mvaddnstr (santa.y+1, 0, santa.gfx_line2+abs(santa.x), len);
else
mvaddnstr (santa.y+1, santa.x, santa.gfx_line2, len);
} else {
if (santa.x < 0)
mvaddnstr (santa.y, 0, santa.altgfx+abs(santa.x), len);
else
mvaddnstr (santa.y, santa.x, santa.altgfx, len);
if (santa.x < 0)
mvaddnstr (santa.y+1, 0, santa.altgfx_line2+abs(santa.x), len);
else
mvaddnstr (santa.y+1, santa.x, santa.altgfx_line2, len);
}
attron (COLOR_PAIR(10));
mvaddch (santa.y, santa.x + santa.length - 1, '*');
attroff (COLOR_PAIR(10));
static void SHDrawSanta (struct santa santa)
{
int len = COLS - santa.x;
if (santa.anim == 0) {
if (santa.x < 0)
mvaddnstr (santa.y, 0, santa.gfx + abs (santa.x), len);
else
mvaddnstr (santa.y, santa.x, santa.gfx, len);
if (santa.x < 0)
mvaddnstr (santa.y + 1, 0, santa.gfx_line2 + abs (santa.x), len);
else
mvaddnstr (santa.y + 1, santa.x, santa.gfx_line2, len);
} else {
if (santa.x < 0)
mvaddnstr (santa.y, 0, santa.altgfx + abs (santa.x), len);
else
mvaddnstr (santa.y, santa.x, santa.altgfx, len);
if (santa.x < 0)
mvaddnstr (santa.y + 1, 0, santa.altgfx_line2 + abs (santa.x), len);
else
mvaddnstr (santa.y + 1, santa.x, santa.altgfx_line2, len);
}
attron (COLOR_PAIR (10));
mvaddch (santa.y, santa.x + santa.length - 1, '*');
attroff (COLOR_PAIR (10));
}
static void newSanta (struct santa* santa, unsigned level) {
santa->x = -27;
santa->y = 1+nrand(LINES-7);
santa->speed = level + nrand(3);
santa->anim = 0;
santa->height = 2;
santa->length = 27;
santa->gfx = "##___ __-+---+/*__-+---+/*";
santa->gfx_line2 = "_|__|_) /\\ /\\ /\\ /\\ "; // Append 2 spaces!
santa->altgfx = "##___ __-+---+/*__-+---+/*";
santa->altgfx_line2 = "_|__|_) /| |\\ /| |\\ "; // Append 1 space!
static void newSanta (struct santa* santa, unsigned level)
{
santa->x = -27;
santa->y = 1 + nrand (LINES - 7);
santa->speed = level + nrand (3);
santa->anim = 0;
santa->height = 2;
santa->length = 27;
santa->gfx = "##___ __-+---+/*__-+---+/*";
santa->gfx_line2 = "_|__|_) /\\ /\\ /\\ /\\ "; // Append 2 spaces!
santa->altgfx = "##___ __-+---+/*__-+---+/*";
santa->altgfx_line2 = "_|__|_) /| |\\ /| |\\ "; // Append 1 space!
}
static bool SHHit (const struct shot shot, const struct santa santa) {
return ((int) shot.x >= santa.x && shot.x <= santa.x + santa.length &&
(shot.y == santa.y || shot.y == santa.y+1));
static bool SHHit (const struct shot shot, const struct santa santa)
{
return ((int) shot.x >= santa.x && shot.x <= santa.x + santa.length && (shot.y == santa.y || shot.y == santa.y + 1));
}
static unsigned SHAddScore (const struct santa santa) {
return 100 / santa.y * santa.speed;
static unsigned SHAddScore (const struct santa santa)
{
return 100 / santa.y * santa.speed;
}
static void SHDrawHitScore (struct scoreDisplay score) {
attron (WA_BOLD);
unsigned rand_color = 10 + nrand(6);
attron (COLOR_PAIR(rand_color));
mvprintw (score.y, score.x, "%d", score.score);
attroff (COLOR_PAIR(rand_color));
attroff (WA_BOLD);
static void SHDrawHitScore (struct scoreDisplay score)
{
attron (WA_BOLD);
unsigned rand_color = 10 + nrand (6);
attron (COLOR_PAIR (rand_color));
mvprintw (score.y, score.x, "%d", score.score);
attroff (COLOR_PAIR (rand_color));
attroff (WA_BOLD);
}
static void printFinalScore (unsigned score) {
//{{{ numbers ASCII art
static const char numbers[10][5][8] = {
{"_______",
"| _ |",
"| | | |",
"| |_| |",
"|_____|"}, // 0
{" ____ ",
" / | ",
"/_/| | ",
" | | ",
" |_| "}, // 1
{"_______",
"|___ |",
"| ____|",
"| |____",
"|_____|"}, // 2
{"_______",
"|___ |",
" ___ |",
"___| |",
"|_____|"}, // 3
{"___ ___",
"| |_| |",
"|____ |",
" | |",
" |_|"}, // 4
{"_______",
"| ___|",
"|___ |",
"____| |",
"|_____|"}, // 5
{" __ ",
" / / ",
" / \\",
"| O /",
" \\___/ "}, // 6
{"_______",
"|___ |",
" | |",
" | |",
" |_|"}, // 7
{"_______",
"| _ |",
"| |_| |",
"| |_| |",
"|_____|"}, // 8
{" ___ ",
" / \\ ",
"| O |",
" \\ / ",
" /__/ "} // 9
};
//}}}
char numtxt [12];
unsigned ndigits = snprintf (numtxt, 12, "%u", score);
const int sx = (COLS - ndigits*8)/2u, sy = (LINES - 5)/2u;
for (unsigned i = 0; i < ndigits; ++i)
for (unsigned j = 0; j < 5; ++j)
mvaddstr (sy+j, sx+i*8, numbers[numtxt[i]-'0'][j]);
static void printFinalScore (unsigned score)
{
//{{{ numbers ASCII art
static const char numbers[10][5][8] = {
{ "_______",
"| _ |",
"| | | |",
"| |_| |",
"|_____|" }, // 0
{ " ____ ",
" / | ",
"/_/| | ",
" | | ",
" |_| " }, // 1
{ "_______",
"|___ |",
"| ____|",
"| |____",
"|_____|" }, // 2
{ "_______",
"|___ |",
" ___ |",
"___| |",
"|_____|" }, // 3
{ "___ ___",
"| |_| |",
"|____ |",
" | |",
" |_|" }, // 4
{ "_______",
"| ___|",
"|___ |",
"____| |",
"|_____|" }, // 5
{ " __ ",
" / / ",
" / \\",
"| O /",
" \\___/ " }, // 6
{ "_______",
"|___ |",
" | |",
" | |",
" |_|" }, // 7
{ "_______",
"| _ |",
"| |_| |",
"| |_| |",
"|_____|" }, // 8
{ " ___ ",
" / \\ ",
"| O |",
" \\ / ",
" /__/ " } // 9
};
//}}}
char numtxt[12];
unsigned ndigits = snprintf (numtxt, 12, "%u", score);
const int sx = (COLS - ndigits * 8) / 2u, sy = (LINES - 5) / 2u;
for (unsigned i = 0; i < ndigits; ++i)
for (unsigned j = 0; j < 5; ++j)
mvaddstr (sy + j, sx + i * 8, numbers[numtxt[i] - '0'][j]);
refresh();
}
static void SHFinalScore (int score)
{
attron (WA_BOLD);
mvaddstr ((LINES - 5) / 2u - 2, (COLS - strlen ("Final score:")) / 2u, "Final score:");
for (int k; (k = getch()) < ' ' || k > '~';) {
unsigned rand_color = 10 + nrand (6);
attron (COLOR_PAIR (rand_color));
printFinalScore (score);
attroff (COLOR_PAIR (rand_color));
refresh();
}
attroff (WA_BOLD);
}
static void SHFinalScore (int score) {
attron (WA_BOLD);
mvaddstr ((LINES-5)/2u-2, (COLS-strlen("Final score:"))/2u, "Final score:");
for (int k; (k = getch()) < ' ' || k > '~';) {
unsigned rand_color = 10 + nrand(6);
attron (COLOR_PAIR(rand_color));
printFinalScore(score);
attroff (COLOR_PAIR(rand_color));
refresh();
static void santaHunta (void)
{
// Set ncurses halfdelay mode.
// Max resolution is 1/10sec
halfdelay (1);
struct scoreDisplay scoreDisplay = { };
struct shot shot = { 0, 0, false };
bool santalives = false;
struct santa santa = { };
for (unsigned score = 0, level = 1, gun_pos = COLS / 2u, hitcount = 0, draw_delay = 0;;) {
struct timeval before, after;
gettimeofday (&before, NULL);
int input = getch();
gettimeofday (&after, NULL);
if (after.tv_sec > before.tv_sec)
after.tv_usec += 1000000;
if (!santalives) {
newSanta (&santa, level);
santalives = true;
}
attroff (WA_BOLD);
}
static void santaHunta (void) {
// Set ncurses halfdelay mode.
// Max resolution is 1/10sec
halfdelay (1);
struct scoreDisplay scoreDisplay = {};
struct shot shot = { 0, 0, false };
bool santalives = false;
struct santa santa = {};
for (unsigned score = 0, level = 1, gun_pos = COLS/2u, hitcount = 0, draw_delay = 0;;) {
struct timeval before, after;
gettimeofday (&before, NULL);
int input = getch();
gettimeofday (&after, NULL);
if (after.tv_sec > before.tv_sec)
after.tv_usec += 1000000;
if (!santalives) {
newSanta(&santa, level);
santalives = true;
// Pad drawing resolution to 1/10sec
draw_delay += after.tv_usec - before.tv_usec;
if (draw_delay > 100000) {
draw_delay = 0;
erase();
if (santalives)
SHDrawSanta (santa);
santa.anim = !santa.anim;
if (santa.x >= COLS)
santalives = false;
if (shot.fired)
SHDrawProjectile (shot);
if (scoreDisplay.rounds > 0)
SHDrawHitScore (scoreDisplay);
if (SHHit (shot, santa)) {
if (++hitcount >= 10) {
hitcount = 0;
++level;
}
// Pad drawing resolution to 1/10sec
draw_delay += after.tv_usec - before.tv_usec;
if (draw_delay > 100000) {
draw_delay = 0;
erase();
if (santalives)
SHDrawSanta(santa);
santa.anim = !santa.anim;
if (santa.x >= COLS)
santalives = false;
if (shot.fired)
SHDrawProjectile(shot);
if (scoreDisplay.rounds > 0)
SHDrawHitScore(scoreDisplay);
if (SHHit(shot, santa)) {
if (++hitcount >= 10) {
hitcount = 0;
++level;
}
santalives = false;
scoreDisplay.x = shot.x;
scoreDisplay.y = shot.y;
scoreDisplay.score = SHAddScore(santa);
scoreDisplay.rounds = 20;
score += scoreDisplay.score;
}
santa.x += santa.speed;
--scoreDisplay.rounds;
if (!--shot.y)
shot.fired = false;
}
if ((input == KEY_RIGHT) || (input == _settings.keybindings.next)) {
if (gun_pos < COLS-5u)
++gun_pos;
} else if ((input == KEY_LEFT) || (input == _settings.keybindings.prev)) {
if (gun_pos > 3)
--gun_pos;
} else if (input == ' ') {
if (!shot.fired) {
attron (WA_BOLD| COLOR_PAIR(12));
mvaddstr (LINES-4, gun_pos-2, "\\***/");
attroff (WA_BOLD| COLOR_PAIR(12));
shot.x = gun_pos;
shot.y = LINES-4;
shot.fired = true;
}
} else if (input == _settings.keybindings.quit) {
if (score)
SHFinalScore(score);
break;
}
SHDrawGun(gun_pos);
SHDrawScore(score, level);
SHDrawStatus();
refresh();
santalives = false;
scoreDisplay.x = shot.x;
scoreDisplay.y = shot.y;
scoreDisplay.score = SHAddScore (santa);
scoreDisplay.rounds = 20;
score += scoreDisplay.score;
}
santa.x += santa.speed;
--scoreDisplay.rounds;
if (!--shot.y)
shot.fired = false;
}
// Leave halfdelay mode.
cbreak();
if ((input == KEY_RIGHT) || (input == _settings.keybindings.next)) {
if (gun_pos < COLS - 5u)
++gun_pos;
} else if ((input == KEY_LEFT) || (input == _settings.keybindings.prev)) {
if (gun_pos > 3)
--gun_pos;
} else if (input == ' ') {
if (!shot.fired) {
attron (WA_BOLD | COLOR_PAIR (12));
mvaddstr (LINES - 4, gun_pos - 2, "\\***/");
attroff (WA_BOLD | COLOR_PAIR (12));
shot.x = gun_pos;
shot.y = LINES - 4;
shot.fired = true;
}
} else if (input == _settings.keybindings.quit) {
if (score)
SHFinalScore (score);
break;
}
SHDrawGun (gun_pos);
SHDrawScore (score, level);
SHDrawStatus();
refresh();
}
// Leave halfdelay mode.
cbreak();
}
void UIAbout (void) {
void UIAbout (void)
{
if (COLS < 80) {
erase();
mvprintw (0, 0, _("Need at least 80 COLS terminal, sorry!"));
getch();
return;
}
int key = 'S';
if (!easterEgg()) {
unsigned xpos = COLS/2u - 40;
if (COLS < 80) {
erase();
mvprintw (0, 0, _("Need at least 80 COLS terminal, sorry!"));
getch();
return;
}
int key = 'S';
if (!easterEgg()) {
unsigned xpos = COLS / 2u - 40;
erase();
mvaddstr (2, xpos, " ________ _______ ____ __________ _______ ______ __________ ________");
mvaddstr (3, xpos, " / _____/ \\ \\ / \\\\ | | \\\\ \\ / ___/ \\ | | \\ / ______/");
mvaddstr (4, xpos, " \\____ \\ \\ | \\ / /\\ \\\\ | / \\ | \\ / / \\ | / \\____ \\");
mvaddstr (5, xpos, " / \\ / | \\\\ \\/ / \\ | / / | \\\\ ___\\ \\ | / / \\");
mvaddstr (6, xpos, "/______ / / ___|___/ \\____/ /__|__/ / ___|___/ \\____ \\ /__|__/ /______ /");
mvaddstr (7, xpos, " \\/ \\/ \\/ \\/ \\/");
mvprintw (9, (COLS-strlen("Version " SNOWNEWS_VERSTRING))/2u, "Version %s", SNOWNEWS_VERSTRING);
erase();
mvaddstr (2, xpos, " ________ _______ ____ __________ _______ ______ __________ ________");
mvaddstr (3, xpos, " / _____/ \\ \\ / \\\\ | | \\\\ \\ / ___/ \\ | | \\ / ______/");
mvaddstr (4, xpos, " \\____ \\ \\ | \\ / /\\ \\\\ | / \\ | \\ / / \\ | / \\____ \\");
mvaddstr (5, xpos, " / \\ / | \\\\ \\/ / \\ | / / | \\\\ ___\\ \\ | / / \\");
mvaddstr (6, xpos, "/______ / / ___|___/ \\____/ /__|__/ / ___|___/ \\____ \\ /__|__/ /______ /");
mvaddstr (7, xpos, " \\/ \\/ \\/ \\/ \\/");
mvprintw (9, (COLS - strlen ("Version " SNOWNEWS_VERSTRING)) / 2u, "Version %s", SNOWNEWS_VERSTRING);
mvaddstr (11, COLS/2-(strlen(_("Brought to you by:")))/2, _("Brought to you by:"));
mvaddstr (13, COLS/2-(strlen(_("Main code")))/2, _("Main code"));
mvaddstr (14, COLS/2-6, "Oliver Feiler");
mvaddstr (16, COLS/2-(strlen(_("Additional code")))/2, _("Additional code"));
mvaddstr (17, COLS/2-4, "Rene Puls");
mvaddstr (19, COLS/2-(strlen(_("Translation team")))/2, _("Translation team"));
mvaddstr (20, COLS/2-31, "Oliver Feiler, Frank van der Loo, Pascal Varet, Simon Isakovic,");
mvaddstr (21, COLS/2-32, "Fernando J. Pereda, Marco Cova, Cheng-Lung Sung, Dmitry Petukhov");
mvaddstr (22, COLS/2-26, "Douglas Campos, Ray Iwata, Piotr Ozarowski, Yang Huan");
mvaddstr (23, COLS/2-15, "Ihar Hrachyshka, Mats Berglund");
mvaddstr (11, COLS / 2 - (strlen (_("Brought to you by:"))) / 2, _("Brought to you by:"));
mvaddstr (13, COLS / 2 - (strlen (_("Main code"))) / 2, _("Main code"));
mvaddstr (14, COLS / 2 - 6, "Oliver Feiler");
mvaddstr (16, COLS / 2 - (strlen (_("Additional code"))) / 2, _("Additional code"));
mvaddstr (17, COLS / 2 - 4, "Rene Puls");
mvaddstr (19, COLS / 2 - (strlen (_("Translation team"))) / 2, _("Translation team"));
mvaddstr (20, COLS / 2 - 31, "Oliver Feiler, Frank van der Loo, Pascal Varet, Simon Isakovic,");
mvaddstr (21, COLS / 2 - 32, "Fernando J. Pereda, Marco Cova, Cheng-Lung Sung, Dmitry Petukhov");
mvaddstr (22, COLS / 2 - 26, "Douglas Campos, Ray Iwata, Piotr Ozarowski, Yang Huan");
mvaddstr (23, COLS / 2 - 15, "Ihar Hrachyshka, Mats Berglund");
key = getch();
}
if (key == 'S')
santaHunta();
key = getch();
}
if (key == 'S')
santaHunta();
}
bool easterEgg (void) {
time_t tunix = time(NULL);
struct tm* t = localtime(&tunix);
return t->tm_mon == 11 && t->tm_mday >= 24 && t->tm_mday <= 26;
bool easterEgg (void)
{
time_t tunix = time (NULL);
struct tm* t = localtime (&tunix);
return t->tm_mon == 11 && t->tm_mday >= 24 && t->tm_mday <= 26;
}

View File

@ -19,180 +19,188 @@
// Compare global category list with string categoryname and return 1 if a
// matching category was found.
static bool CategoryListItemExists (const char* categoryname) {
for (const struct categories* category = _settings.global_categories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
return true; // Matching category found.
return false;
static bool CategoryListItemExists (const char* categoryname)
{
for (const struct categories * category = _settings.global_categories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
return true; // Matching category found.
return false;
}
// The following functions add items to/delete items from the global category list.
static void CategoryListAddItem (const char* categoryname) {
if (CategoryListItemExists (categoryname)) {
// Only increase refcount
for (struct categories* category = _settings.global_categories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
category->refcount++;
} else {
// Otherwise add item to structure
struct categories* category = malloc (sizeof(struct categories));
category->name = strdup (categoryname);
category->next = NULL;
static void CategoryListAddItem (const char* categoryname)
{
if (CategoryListItemExists (categoryname)) {
// Only increase refcount
for (struct categories * category = _settings.global_categories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
++category->refcount;
} else {
// Otherwise add item to structure
struct categories* category = malloc (sizeof (struct categories));
category->name = strdup (categoryname);
category->next = NULL;
if (!_settings.global_categories) { // Contains no elements
if (!_settings.global_categories) { // Contains no elements
category->next = _settings.global_categories;
_settings.global_categories = category;
} else {
bool inserted = false;
struct categories* before_ptr = NULL;
for (struct categories * new_ptr = _settings.global_categories; new_ptr; new_ptr = new_ptr->next) {
if (strcasecmp (category->name, new_ptr->name) > 0)
before_ptr = new_ptr; // New category still below current one. Move on.
else {
if (!before_ptr) { // Insert before _settings.global_categories
category->next = _settings.global_categories;
_settings.global_categories = category;
} else {
bool inserted = false;
struct categories *before_ptr = NULL;
for (struct categories* new_ptr = _settings.global_categories; new_ptr; new_ptr = new_ptr->next) {
if (strcasecmp (category->name, new_ptr->name) > 0)
before_ptr = new_ptr; // New category still below current one. Move on.
else {
if (!before_ptr) { // Insert before _settings.global_categories
category->next = _settings.global_categories;
_settings.global_categories = category;
} else { // Insert after category
before_ptr->next = category;
category->next = new_ptr;
}
inserted = true;
break; // Done with this loop.
}
}
if (!inserted) // No match from above. Insert new item at the end of the list.
before_ptr->next = category;
} else { // Insert after category
before_ptr->next = category;
category->next = new_ptr;
}
inserted = true;
break; // Done with this loop.
}
// Set refcount to 1 for newly added category.
category->refcount = 1;
}
if (!inserted) // No match from above. Insert new item at the end of the list.
before_ptr->next = category;
}
// Set refcount to 1 for newly added category.
category->refcount = 1;
}
}
// Delete item from category list. Decrease refcount for each deletion.
// If refcount hits 0, remove from list.
static void CategoryListDeleteItem (const char* categoryname) {
for (struct categories *category = _settings.global_categories, *before_ptr = NULL; category; category = category->next) {
if (strcasecmp (category->name, categoryname) == 0) {
// Check refcount
if (category->refcount > 1) {
// Still at least one feed defining this category
category->refcount--;
break;
} else {
// Last feed using this category removed, kill off the structure
if (category == _settings.global_categories) {
_settings.global_categories = _settings.global_categories->next;
free (category->name);
free (category);
break;
} else {
before_ptr->next = category->next;
free (category->name);
free (category);
break;
}
}
static void CategoryListDeleteItem (const char* categoryname)
{
for (struct categories * category = _settings.global_categories, *before_ptr = NULL; category; category = category->next) {
if (strcasecmp (category->name, categoryname) == 0) {
// Check refcount
if (category->refcount > 1) {
// Still at least one feed defining this category
--category->refcount;
break;
} else {
// Last feed using this category removed, kill off the structure
if (category == _settings.global_categories) {
_settings.global_categories = _settings.global_categories->next;
free (category->name);
free (category);
break;
} else {
before_ptr->next = category->next;
free (category->name);
free (category);
break;
}
before_ptr = category;
}
}
before_ptr = category;
}
}
// The following two functions add items to/delete items from a feeds category list.
void FeedCategoryAdd (struct feed* cur_ptr, const char* categoryname) {
struct feedcategories* category = malloc (sizeof(struct feedcategories));
category->name = strdup (categoryname);
category->next = NULL;
void FeedCategoryAdd (struct feed* cur_ptr, const char* categoryname)
{
struct feedcategories* category = malloc (sizeof (struct feedcategories));
category->name = strdup (categoryname);
category->next = NULL;
if (cur_ptr->feedcategories == NULL) {
// Contains no elements
category->next = cur_ptr->feedcategories;
cur_ptr->feedcategories = category;
} else {
bool inserted = false;
struct feedcategories *before_ptr = NULL;
for (struct feedcategories* new_ptr = cur_ptr->feedcategories; new_ptr != NULL; new_ptr = new_ptr->next) {
if (strcasecmp (category->name, new_ptr->name) > 0) {
// New category still below current one. Move on.
before_ptr = new_ptr;
} else {
if (before_ptr == NULL) {
category->next = cur_ptr->feedcategories;
cur_ptr->feedcategories = category;
} else {
// The new category is now > current one. Insert it here.
before_ptr->next = category;
category->next = new_ptr;
}
inserted = true;
break; // Done with this loop.
}
if (cur_ptr->feedcategories == NULL) {
// Contains no elements
category->next = cur_ptr->feedcategories;
cur_ptr->feedcategories = category;
} else {
bool inserted = false;
struct feedcategories* before_ptr = NULL;
for (struct feedcategories * new_ptr = cur_ptr->feedcategories; new_ptr != NULL; new_ptr = new_ptr->next) {
if (strcasecmp (category->name, new_ptr->name) > 0) {
// New category still below current one. Move on.
before_ptr = new_ptr;
} else {
if (before_ptr == NULL) {
category->next = cur_ptr->feedcategories;
cur_ptr->feedcategories = category;
} else {
// The new category is now > current one. Insert it here.
before_ptr->next = category;
category->next = new_ptr;
}
if (!inserted) // No match from above. Insert new item at the end of the list.
before_ptr->next = category;
inserted = true;
break; // Done with this loop.
}
}
CategoryListAddItem (category->name);
if (!inserted) // No match from above. Insert new item at the end of the list.
before_ptr->next = category;
}
CategoryListAddItem (category->name);
}
void FeedCategoryDelete (struct feed* cur_ptr, const char* categoryname) {
// categoryname == category->name which will be freed by this function!
char* tmpname = strdup (categoryname);
for (struct feedcategories *category = cur_ptr->feedcategories, *before_ptr = NULL; category; category = category->next) {
if (strcasecmp (category->name, categoryname) == 0) {
if (category == cur_ptr->feedcategories) {
cur_ptr->feedcategories = cur_ptr->feedcategories->next;
free (category->name);
free (category);
break;
} else {
before_ptr->next = category->next;
free (category->name);
free (category);
break;
}
}
before_ptr = category;
void FeedCategoryDelete (struct feed* cur_ptr, const char* categoryname)
{
// categoryname == category->name which will be freed by this function!
char* tmpname = strdup (categoryname);
for (struct feedcategories * category = cur_ptr->feedcategories, *before_ptr = NULL; category; category = category->next) {
if (strcasecmp (category->name, categoryname) == 0) {
if (category == cur_ptr->feedcategories) {
cur_ptr->feedcategories = cur_ptr->feedcategories->next;
free (category->name);
free (category);
break;
} else {
before_ptr->next = category->next;
free (category->name);
free (category);
break;
}
}
before_ptr = category;
}
// Decrease refcount in global category list.
CategoryListDeleteItem (tmpname);
free (tmpname);
// Decrease refcount in global category list.
CategoryListDeleteItem (tmpname);
free (tmpname);
}
// Compare a feeds category list with string categoryname.
// Return true if a matching category was found.
bool FeedCategoryExists (const struct feed* cur_ptr, const char* categoryname) {
for (const struct feedcategories* category = cur_ptr->feedcategories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
return true;
return false;
bool FeedCategoryExists (const struct feed* cur_ptr, const char* categoryname)
{
for (const struct feedcategories * category = cur_ptr->feedcategories; category; category = category->next)
if (strcasecmp (category->name, categoryname) == 0)
return true;
return false;
}
// Return a comma-separated list of categories defined for the provided feed,
// NULL if no category is defined for the feed.
//
// The caller must free the list.
char* GetCategoryList (const struct feed *feed) {
if (!feed->feedcategories)
return NULL;
char* categories = malloc (1);
categories[0] = 0;
size_t len = 1;
for (const struct feedcategories* category = feed->feedcategories; category; category = category->next) {
len += strlen(", ")+strlen(category->name);
categories = realloc (categories, len);
if (categories[0])
strcat (categories, ", ");
strcat (categories, category->name);
}
return categories;
char* GetCategoryList (const struct feed* feed)
{
if (!feed->feedcategories)
return NULL;
char* categories = malloc (1);
categories[0] = 0;
size_t len = 1;
for (const struct feedcategories * category = feed->feedcategories; category; category = category->next) {
len += strlen (", ") + strlen (category->name);
categories = realloc (categories, len);
if (categories[0])
strcat (categories, ", ");
strcat (categories, category->name);
}
return categories;
}
// Free and reset the filters list.
void ResetFilters (char **filters) {
if (!filters)
return;
for (unsigned i = 0; i < 8; ++i) {
free (filters[i]);
filters[i] = NULL;
}
void ResetFilters (char** filters)
{
if (!filters)
return;
for (unsigned i = 0; i < 8; ++i) {
free (filters[i]);
filters[i] = NULL;
}
}

View File

@ -21,4 +21,4 @@ void FeedCategoryAdd (struct feed* cur_ptr, const char* categoryname);
void FeedCategoryDelete (struct feed* cur_ptr, const char* categoryname);
bool FeedCategoryExists (const struct feed* cur_ptr, const char* categoryname);
char* GetCategoryList (const struct feed* feed);
void ResetFilters (char **filters);
void ResetFilters (char** filters);

View File

@ -43,15 +43,15 @@
// Various switches for enabling definitions
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#define _GNU_SOURCE 1
#endif
// OS X needs this, otherwise socklen_t is not defined.
#ifdef __APPLE__
#define _BSD_SOCKLEN_T_
#define _BSD_SOCKLEN_T_
#endif
// BeOS does not define socklen_t. Using uint as suggested by port creator.
#ifdef __BEOS__
#define socklen_t unsigned int
#define socklen_t unsigned int
#endif
// Common header files
@ -66,10 +66,10 @@
#include <time.h>
#include "os-support.h"
#ifdef LOCALEPATH
#include <libintl.h>
#include <locale.h>
#define _(String) gettext (String)
#include <libintl.h>
#include <locale.h>
#define _(String) gettext (String)
#else
#define _(String) (String)
#define ngettext(Singular, Plural, n) (Plural)
#define _(String) (String)
#define ngettext(Singular, Plural, n) (Plural)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -20,14 +20,14 @@
char* iconvert (const char* inbuf);
char* UIDejunk (const char* feed_description);
char* WrapText (const char* text, unsigned width);
char* base64encode(const char* inbuf, unsigned int inbuf_size);
void CleanupString (char * string, int tidyness);
char * Hashify (const char * url);
char* base64encode (const char* inbuf, unsigned int inbuf_size);
void CleanupString (char* string, int tidyness);
char* Hashify (const char* url);
char* genItemHash (const char* const* hashitems, unsigned items);
time_t ISODateToUnix (const char* ISODate);
time_t pubDateToUnix (const char* pubDate);
char* unixToPostDateString (time_t unixDate);
#ifdef USE_UNSUPPORTED_AND_BROKEN_CODE
char* decodechunked(char * chunked, unsigned int *inputlen);
char* decodechunked (char* chunked, unsigned int* inputlen);
#endif

305
cookies.c
View File

@ -17,167 +17,166 @@
// Netscape cookie file format:
// (http://www.cookiecentral.com/faq/#3.5)
//
// .host.com host_match[BOOL] /path secure[BOOL] expire[unix time] NAME VALUE
// .host.com host_match[BOOL] /path secure[BOOL] expire[unix time] NAME VALUE
#include "config.h"
#include "ui-support.h"
static void CookieCutter (struct feed* cur_ptr, FILE* cookies) {
int len = 0;
int cookienr = 0;
// Get current time.
time_t tunix = time (NULL);
char* url = strdup (cur_ptr->feedurl);
char* freeme = url;
strsep (&url, "/");
strsep (&url, "/");
char* tmphost = url;
strsep (&url, "/");
if (url == NULL) {
free (freeme);
return;
static void CookieCutter (struct feed* cur_ptr, FILE * cookies)
{
int len = 0;
int cookienr = 0;
// Get current time.
time_t tunix = time (NULL);
char* url = strdup (cur_ptr->feedurl);
char* freeme = url;
strsep (&url, "/");
strsep (&url, "/");
char* tmphost = url;
strsep (&url, "/");
if (url == NULL) {
free (freeme);
return;
}
// If tmphost contains an '@' strip authinfo off url.
if (strchr (tmphost, '@'))
strsep (&tmphost, "@");
char* host = strdup (tmphost); // Current feed hostname.
--url;
url[0] = '/';
if (url[strlen (url) - 1] == '\n')
url[strlen (url) - 1] = '\0';
char* path = strdup (url); // Current feed path.
free (freeme);
freeme = NULL;
while (!feof (cookies)) {
char buf[BUFSIZ]; // File read buffer.
if ((fgets (buf, sizeof (buf), cookies)) == NULL)
break;
// Filter \n lines. But ignore them so we can read a NS cookie file.
if (buf[0] == '\n')
continue;
// Allow adding of comments that start with '#'.
// Makes it possible to symlink Mozilla's cookies.txt.
if (buf[0] == '#')
continue;
char* cookie = strdup (buf);
freeme = cookie;
// Munch trailing newline.
if (cookie[strlen (cookie) - 1] == '\n')
cookie[strlen (cookie) - 1] = '\0';
// Decode the cookie string.
char* cookiehost = NULL;
char* cookiepath = NULL;
char* cookiename = NULL;
char* cookievalue = NULL;
time_t cookieexpire = 0;
bool cookiesecure = false;
for (unsigned i = 0; i < 7; ++i) {
const char* tmpstr = strsep (&cookie, "\t");
if (!tmpstr)
break;
switch (i) {
case 0:
// Cookie hostname.
cookiehost = strdup (tmpstr);
break;
case 1:
// Discard host match value.
break;
case 2:
// Cookie path.
cookiepath = strdup (tmpstr);
break;
case 3:
// Secure cookie?
if (strcasecmp (tmpstr, "TRUE") == 0)
cookiesecure = true;
break;
case 4:
// Cookie expiration date.
cookieexpire = strtoul (tmpstr, NULL, 10);
break;
case 5:
// NAME
cookiename = strdup (tmpstr);
break;
case 6:
// VALUE
cookievalue = strdup (tmpstr);
break;
}
}
// If tmphost contains an '@' strip authinfo off url.
if (strchr (tmphost, '@'))
strsep (&tmphost, "@");
char* host = strdup (tmphost); // Current feed hostname.
url--;
url[0] = '/';
if (url[strlen(url)-1] == '\n')
url[strlen(url)-1] = '\0';
char* path = strdup (url); // Current feed path.
// See if current cookie matches cur_ptr.
// Hostname and path must match.
// Ignore secure cookies.
// Discard cookie if it has expired.
if (strstr (host, cookiehost) && strstr (path, cookiepath) && !cookiesecure && cookieexpire > tunix) {
++cookienr;
// Construct and append cookiestring.
//
// Cookie: NAME=VALUE; NAME=VALUE
if (cookienr == 1) {
len = 8 + strlen (cookiename) + 1 + strlen (cookievalue) + 1;
cur_ptr->cookies = malloc (len);
strcpy (cur_ptr->cookies, "Cookie: ");
strcat (cur_ptr->cookies, cookiename);
strcat (cur_ptr->cookies, "=");
strcat (cur_ptr->cookies, cookievalue);
} else {
len += strlen (cookiename) + 1 + strlen (cookievalue) + 3;
cur_ptr->cookies = realloc (cur_ptr->cookies, len);
strcat (cur_ptr->cookies, "; ");
strcat (cur_ptr->cookies, cookiename);
strcat (cur_ptr->cookies, "=");
strcat (cur_ptr->cookies, cookievalue);
}
} else if ((strstr (host, cookiehost) != NULL) && (strstr (path, cookiepath) != NULL) && (cookieexpire < (int) tunix)) { // Cast time_t tunix to int.
// Print cookie expire warning.
char expirebuf[PATH_MAX];
snprintf (expirebuf, sizeof (expirebuf), _("Cookie for %s has expired!"), cookiehost);
UIStatus (expirebuf, 1, 1);
}
free (freeme);
freeme = NULL;
while (!feof(cookies)) {
char buf [BUFSIZ]; // File read buffer.
if ((fgets (buf, sizeof(buf), cookies)) == NULL)
break;
// Filter \n lines. But ignore them so we can read a NS cookie file.
if (buf[0] == '\n')
continue;
// Allow adding of comments that start with '#'.
// Makes it possible to symlink Mozilla's cookies.txt.
if (buf[0] == '#')
continue;
char* cookie = strdup (buf);
freeme = cookie;
// Munch trailing newline.
if (cookie[strlen(cookie)-1] == '\n')
cookie[strlen(cookie)-1] = '\0';
// Decode the cookie string.
char* cookiehost = NULL;
char* cookiepath = NULL;
char* cookiename = NULL;
char* cookievalue = NULL;
time_t cookieexpire = 0;
bool cookiesecure = false;
for (unsigned i = 0; i < 7; ++i) {
const char* tmpstr = strsep (&cookie, "\t");
if (!tmpstr)
break;
switch (i) {
case 0:
// Cookie hostname.
cookiehost = strdup (tmpstr);
break;
case 1:
// Discard host match value.
break;
case 2:
// Cookie path.
cookiepath = strdup (tmpstr);
break;
case 3:
// Secure cookie?
if (strcasecmp (tmpstr, "TRUE") == 0)
cookiesecure = true;
break;
case 4:
// Cookie expiration date.
cookieexpire = strtoul (tmpstr, NULL, 10);
break;
case 5:
// NAME
cookiename = strdup (tmpstr);
break;
case 6:
// VALUE
cookievalue = strdup (tmpstr);
break;
}
}
// See if current cookie matches cur_ptr.
// Hostname and path must match.
// Ignore secure cookies.
// Discard cookie if it has expired.
if (strstr (host, cookiehost) && strstr (path, cookiepath) && !cookiesecure && cookieexpire > tunix) {
cookienr++;
// Construct and append cookiestring.
//
// Cookie: NAME=VALUE; NAME=VALUE
if (cookienr == 1) {
len = 8 + strlen(cookiename) + 1 + strlen(cookievalue) + 1;
cur_ptr->cookies = malloc (len);
strcpy (cur_ptr->cookies, "Cookie: ");
strcat (cur_ptr->cookies, cookiename);
strcat (cur_ptr->cookies, "=");
strcat (cur_ptr->cookies, cookievalue);
} else {
len += strlen(cookiename) + 1 + strlen(cookievalue) + 3;
cur_ptr->cookies = realloc (cur_ptr->cookies, len);
strcat (cur_ptr->cookies, "; ");
strcat (cur_ptr->cookies, cookiename);
strcat (cur_ptr->cookies, "=");
strcat (cur_ptr->cookies, cookievalue);
}
} else if ((strstr (host, cookiehost) != NULL) &&
(strstr (path, cookiepath) != NULL) &&
(cookieexpire < (int) tunix)) { // Cast time_t tunix to int.
// Print cookie expire warning.
char expirebuf [PATH_MAX];
snprintf (expirebuf, sizeof(expirebuf), _("Cookie for %s has expired!"), cookiehost);
UIStatus (expirebuf, 1, 1);
}
free (cookiehost);
free (cookiepath);
free (cookiename);
free (cookievalue);
}
free (freeme);
freeme = NULL;
free (cookiehost);
free (cookiepath);
free (cookiename);
free (cookievalue);
}
free (host);
free (path);
free (freeme);
// Append \r\n to cur_ptr->cookies
if (cur_ptr->cookies != NULL) {
cur_ptr->cookies = realloc (cur_ptr->cookies, len+2);
strcat (cur_ptr->cookies, "\r\n");
}
free (host);
free (path);
free (freeme);
// Append \r\n to cur_ptr->cookies
if (cur_ptr->cookies != NULL) {
cur_ptr->cookies = realloc (cur_ptr->cookies, len + 2);
strcat (cur_ptr->cookies, "\r\n");
}
}
void LoadCookies (struct feed * cur_ptr) {
char file [PATH_MAX]; // File locations.
snprintf (file, sizeof(file), SNOWNEWS_CONFIG_DIR "cookies", getenv("HOME"));
FILE* cookies = fopen (file, "r");
if (!cookies) // No cookies to load.
return;
CookieCutter (cur_ptr, cookies);
fclose (cookies);
void LoadCookies (struct feed* cur_ptr)
{
char file[PATH_MAX]; // File locations.
snprintf (file, sizeof (file), SNOWNEWS_CONFIG_DIR "cookies", getenv ("HOME"));
FILE* cookies = fopen (file, "r");
if (!cookies) // No cookies to load.
return;
CookieCutter (cur_ptr, cookies);
fclose (cookies);
}

View File

@ -16,4 +16,4 @@
#pragma once
void LoadCookies (struct feed * cur_ptr);
void LoadCookies (struct feed* cur_ptr);

1013
dialog.c

File diff suppressed because it is too large Load Diff

View File

@ -20,12 +20,12 @@
void UIHelpScreen (void);
void UIDisplayFeedHelp (void);
void UIDisplayItemHelp (void);
char * UIOneLineEntryField (int x, int y);
char* UIOneLineEntryField (int x, int y);
void UIChangeBrowser (void);
void UIChangeFeedName (struct feed *cur_ptr);
int UIAddFeed (char * newurl);
void UIChangeFeedName (struct feed* cur_ptr);
int UIAddFeed (char* newurl);
void FeedInfo (const struct feed* current_feed);
bool UIDeleteFeed (const char* feedname);
void CategorizeFeed (struct feed * current_feed);
char * DialogGetCategoryFilter (void);
int UIPerFeedFilter (struct feed * current_feed);
void CategorizeFeed (struct feed* current_feed);
char* DialogGetCategoryFilter (void);
int UIPerFeedFilter (struct feed* current_feed);

105
digcalc.c
View File

@ -21,72 +21,69 @@
#include "digcalc.h"
// calculate H(A1) as per spec
void DigestCalcHA1 (const char* pszAlg, const char* pszUserName, const char* pszRealm,
const char* pszPassword, const char* pszNonce, const char* pszCNonce,
HASHHEX SessionKey)
void DigestCalcHA1 (const char* pszAlg, const char* pszUserName, const char* pszRealm, const char* pszPassword, const char* pszNonce, const char* pszCNonce, HASHHEX SessionKey)
{
struct HashMD5 hash;
struct HashMD5 hash;
hash_md5_init (&hash);
hash_md5_data (&hash, pszUserName, strlen (pszUserName));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszRealm, strlen (pszRealm));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszPassword, strlen (pszPassword));
hash_md5_finish (&hash);
if (strcmp (pszAlg, "md5-sess") == 0) {
hash_md5_init (&hash);
hash_md5_data (&hash, pszUserName, strlen(pszUserName));
hash_md5_data (&hash, hash.hash, sizeof (hash.hash));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszRealm, strlen(pszRealm));
hash_md5_data (&hash, pszNonce, strlen (pszNonce));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszPassword, strlen(pszPassword));
hash_md5_data (&hash, pszCNonce, strlen (pszCNonce));
hash_md5_finish (&hash);
if (strcmp (pszAlg, "md5-sess") == 0) {
hash_md5_init (&hash);
hash_md5_data (&hash, hash.hash, sizeof(hash.hash));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszNonce, strlen(pszNonce));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszCNonce, strlen(pszCNonce));
hash_md5_finish (&hash);
}
hash_md5_to_text (&hash, SessionKey);
}
hash_md5_to_text (&hash, SessionKey);
}
// calculate request-digest/response-digest as per HTTP Digest spec
void DigestCalcResponse(
const HASHHEX HA1, // H(A1)
const char* pszNonce, // nonce from server
const char* pszNonceCount, // 8 hex digits
const char* pszCNonce, // client nonce
const char* pszQop, // qop-value: "", "auth", "auth-int"
const char* pszMethod, // method from the request
const char* pszDigestUri, // requested URL
const HASHHEX HEntity, // H(entity body) if qop="auth-int"
HASHHEX Response // request-digest or response-digest
void DigestCalcResponse (const HASHHEX HA1, // H(A1)
const char* pszNonce, // nonce from server
const char* pszNonceCount, // 8 hex digits
const char* pszCNonce, // client nonce
const char* pszQop, // qop-value: "", "auth", "auth-int"
const char* pszMethod, // method from the request
const char* pszDigestUri, // requested URL
const HASHHEX HEntity, // H(entity body) if qop="auth-int"
HASHHEX Response // request-digest or response-digest
)
{
// calculate H(A2)
struct HashMD5 hash;
hash_md5_init (&hash);
hash_md5_data (&hash, pszMethod, strlen(pszMethod));
// calculate H(A2)
struct HashMD5 hash;
hash_md5_init (&hash);
hash_md5_data (&hash, pszMethod, strlen (pszMethod));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszDigestUri, strlen (pszDigestUri));
if (strcmp (pszQop, "auth-int") == 0) {
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszDigestUri, strlen(pszDigestUri));
if (strcmp (pszQop, "auth-int") == 0) {
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, HEntity, HASHHEXLEN);
}
hash_md5_finish (&hash);
HASHHEX HA2Hex;
hash_md5_to_text (&hash, HA2Hex);
hash_md5_data (&hash, HEntity, HASHHEXLEN);
}
hash_md5_finish (&hash);
HASHHEX HA2Hex;
hash_md5_to_text (&hash, HA2Hex);
// calculate response
hash_md5_init (&hash);
hash_md5_data (&hash, HA1, HASHHEXLEN);
// calculate response
hash_md5_init (&hash);
hash_md5_data (&hash, HA1, HASHHEXLEN);
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszNonce, strlen (pszNonce));
hash_md5_data (&hash, ":", 1);
if (*pszQop) {
hash_md5_data (&hash, pszNonceCount, strlen (pszNonceCount));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszNonce, strlen(pszNonce));
hash_md5_data (&hash, pszCNonce, strlen (pszCNonce));
hash_md5_data (&hash, ":", 1);
if (*pszQop) {
hash_md5_data (&hash, pszNonceCount, strlen(pszNonceCount));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszCNonce, strlen(pszCNonce));
hash_md5_data (&hash, ":", 1);
hash_md5_data (&hash, pszQop, strlen(pszQop));
hash_md5_data (&hash, ":", 1);
};
hash_md5_data (&hash, HA2Hex, HASHHEXLEN);
hash_md5_finish (&hash);
hash_md5_to_text (&hash, Response);
hash_md5_data (&hash, pszQop, strlen (pszQop));
hash_md5_data (&hash, ":", 1);
};
hash_md5_data (&hash, HA2Hex, HASHHEXLEN);
hash_md5_finish (&hash);
hash_md5_to_text (&hash, Response);
}

View File

@ -21,23 +21,20 @@
#pragma once
#include "md5.h"
enum { HASHHEXLEN = HASH_SIZE_MD5*2 };
typedef char HASHHEX [HASHHEXLEN+1];
enum { HASHHEXLEN = HASH_SIZE_MD5 * 2 };
typedef char HASHHEX[HASHHEXLEN + 1];
// calculate H(A1) as per HTTP Digest spec
void DigestCalcHA1 (const char* pszAlg, const char* pszUserName, const char* pszRealm,
const char* pszPassword, const char* pszNonce, const char* pszCNonce,
HASHHEX SessionKey);
void DigestCalcHA1 (const char* pszAlg, const char* pszUserName, const char* pszRealm, const char* pszPassword, const char* pszNonce, const char* pszCNonce, HASHHEX SessionKey);
// calculate request-digest/response-digest as per HTTP Digest spec
void DigestCalcResponse (
const HASHHEX HA1, // H(A1)
const char* pszNonce, // nonce from server
const char* pszNonceCount, // 8 hex digits
const char* pszCNonce, // client nonce
const char* pszQop, // qop-value: "", "auth", "auth-int"
const char* pszMethod, // method from the request
const char* pszDigestUri, // requested URL
const HASHHEX HEntity, // H(entity body) if qop="auth-int"
HASHHEX Response // request-digest or response-digest
);
void DigestCalcResponse (const HASHHEX HA1, // H(A1)
const char* pszNonce, // nonce from server
const char* pszNonceCount, // 8 hex digits
const char* pszCNonce, // client nonce
const char* pszQop, // qop-value: "", "auth", "auth-int"
const char* pszMethod, // method from the request
const char* pszDigestUri, // requested URL
const HASHHEX HEntity, // H(entity body) if qop="auth-int"
HASHHEX Response // request-digest or response-digest
);

268
filters.c
View File

@ -27,160 +27,162 @@ static int pipe_command_buf (const char* command, char** argv, const void* inbuf
//----------------------------------------------------------------------
// Load output of local script. Must be valid RSS.
int FilterExecURL (struct feed * cur_ptr) {
char* command = strdup (cur_ptr->feedurl);
char* freeme = command;
int FilterExecURL (struct feed* cur_ptr)
{
char* command = strdup (cur_ptr->feedurl);
char* freeme = command;
strsep (&command, ":");
strsep (&command, ":");
char buf [BUFSIZ];
snprintf (buf, sizeof(buf), _("Loading \"%s\""), command);
UIStatus (buf, 0, 0);
char buf[BUFSIZ];
snprintf (buf, sizeof (buf), _("Loading \"%s\""), command);
UIStatus (buf, 0, 0);
FILE* scriptoutput = popen (command, "r");
free (freeme);
if (!scriptoutput)
return -1;
FILE* scriptoutput = popen (command, "r");
free (freeme);
if (!scriptoutput)
return -1;
cur_ptr->content_length = 0;
cur_ptr->xmltext = realloc (cur_ptr->xmltext, cur_ptr->content_length+1);
cur_ptr->xmltext[cur_ptr->content_length] = '\0';
cur_ptr->content_length = 0;
cur_ptr->xmltext = realloc (cur_ptr->xmltext, cur_ptr->content_length + 1);
cur_ptr->xmltext[cur_ptr->content_length] = '\0';
while (!feof(scriptoutput) && fgets (buf, sizeof(buf), scriptoutput)) {
size_t br = strlen(buf);
cur_ptr->xmltext = realloc (cur_ptr->xmltext, cur_ptr->content_length+br+1);
memcpy (&cur_ptr->xmltext[cur_ptr->content_length], buf, br+1);
cur_ptr->content_length += br;
}
pclose (scriptoutput);
while (!feof (scriptoutput) && fgets (buf, sizeof (buf), scriptoutput)) {
size_t br = strlen (buf);
cur_ptr->xmltext = realloc (cur_ptr->xmltext, cur_ptr->content_length + br + 1);
memcpy (&cur_ptr->xmltext[cur_ptr->content_length], buf, br + 1);
cur_ptr->content_length += br;
}
pclose (scriptoutput);
// Set title and link structure to something.
// To the feedurl in this case so the program shows something
// as placeholder instead of crash.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
if (cur_ptr->link == NULL)
cur_ptr->link = strdup (cur_ptr->feedurl);
return 0;
// Set title and link structure to something.
// To the feedurl in this case so the program shows something
// as placeholder instead of crash.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
if (cur_ptr->link == NULL)
cur_ptr->link = strdup (cur_ptr->feedurl);
return 0;
}
int FilterPipeNG (struct feed * cur_ptr) {
if (cur_ptr->perfeedfilter == NULL)
return -1;
int FilterPipeNG (struct feed* cur_ptr)
{
if (cur_ptr->perfeedfilter == NULL)
return -1;
char* data = malloc (cur_ptr->content_length+1);
memcpy (data, cur_ptr->xmltext, cur_ptr->content_length);
data[cur_ptr->content_length] = 0;
char* data = malloc (cur_ptr->content_length + 1);
memcpy (data, cur_ptr->xmltext, cur_ptr->content_length);
data[cur_ptr->content_length] = 0;
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
char* filter = strdup (cur_ptr->perfeedfilter);
char* command = strsep (&filter, " ");
char* filter = strdup (cur_ptr->perfeedfilter);
char* command = strsep (&filter, " ");
char** options = malloc (sizeof(char*));
size_t nopts = 0;
options[nopts++] = command;
char** options = malloc (sizeof (char *));
size_t nopts = 0;
options[nopts++] = command;
for (;;) {
options = realloc (options, sizeof(char*)*(nopts+1));
if (!(options[nopts++] = strsep(&filter, " ")))
break;
}
for (;;) {
options = realloc (options, sizeof (char *) * (nopts + 1));
if (!(options[nopts++] = strsep (&filter, " ")))
break;
}
int rc = pipe_command_buf (command, options,
data, cur_ptr->content_length,
&cur_ptr->xmltext, &cur_ptr->content_length);
int rc = pipe_command_buf (command, options,
data, cur_ptr->content_length,
&cur_ptr->xmltext, &cur_ptr->content_length);
free (data);
free (command);
free (options); // options[i] contains only pointers!
return rc;
free (data);
free (command);
free (options); // options[i] contains only pointers!
return rc;
}
static int pipe_command_buf (const char* command, char** argv, const void* inbuf, size_t inbuf_size, char** outbuf, unsigned* outbuf_size)
{
enum { READ_END, WRITE_END, N_ENDS };
enum { READ_END, WRITE_END, N_ENDS };
int input_pipe [N_ENDS], output_pipe [N_ENDS];
if (0 != pipe(input_pipe)) {
perror("Couldn't create input pipe");
return -1;
}
if (0 != pipe(output_pipe)) {
close (input_pipe[READ_END]);
close (input_pipe[WRITE_END]);
perror("Couldn't create output pipe");
return -1;
}
int input_pipe[N_ENDS], output_pipe[N_ENDS];
if (0 != pipe (input_pipe)) {
perror ("Couldn't create input pipe");
return -1;
}
if (0 != pipe (output_pipe)) {
close (input_pipe[READ_END]);
close (input_pipe[WRITE_END]);
perror ("Couldn't create output pipe");
return -1;
}
int result = fork();
if (result < 0) {
close (input_pipe[READ_END]);
close (input_pipe[WRITE_END]);
close (output_pipe[READ_END]);
close (output_pipe[WRITE_END]);
perror ("fork");
int result = fork();
if (result < 0) {
close (input_pipe[READ_END]);
close (input_pipe[WRITE_END]);
close (output_pipe[READ_END]);
close (output_pipe[WRITE_END]);
perror ("fork");
return -1;
} else if (result == 0) {
close (output_pipe[WRITE_END]);
close (input_pipe[READ_END]);
if (0 > dup2 (output_pipe[READ_END], STDIN_FILENO)) {
perror ("dup2 error on output_pipe");
exit (EXIT_FAILURE);
}
close (output_pipe[READ_END]);
if (0 > dup2 (input_pipe[WRITE_END], STDOUT_FILENO)) {
perror ("dup2 error on input_pipe");
exit (EXIT_FAILURE);
}
close (input_pipe[WRITE_END]);
if (0 > execvp (command, argv)) {
char tmp[PATH_MAX];
snprintf (tmp, sizeof (tmp), "Exec of \"%s\" failed", command);
perror (tmp);
}
exit (EXIT_FAILURE);
} else {
close (output_pipe[READ_END]);
close (input_pipe[WRITE_END]);
write (output_pipe[WRITE_END], inbuf, inbuf_size);
close (output_pipe[WRITE_END]);
char* buf = NULL, *buf_cur = NULL;
int buf_size = 0, bytes_read = 0;
do {
buf_size += BUFSIZ;
buf = realloc (buf, buf_size + 1);
buf_cur = buf + buf_size - BUFSIZ;
result = read (input_pipe[READ_END], buf_cur, BUFSIZ);
if (result > 0) {
bytes_read += result;
if (result < BUFSIZ) {
buf_size -= (BUFSIZ - result);
buf = realloc (buf, buf_size + 1);
break;
}
} else if (result < 0) {
free (buf);
buf = NULL;
return -1;
} else if (result == 0) {
close(output_pipe[WRITE_END]);
close(input_pipe[READ_END]);
}
} while (result > 0);
close (input_pipe[READ_END]);
if (0 > dup2 (output_pipe[READ_END], STDIN_FILENO)) {
perror("dup2 error on output_pipe");
exit (EXIT_FAILURE);
}
close(output_pipe[READ_END]);
if (0 > dup2 (input_pipe[WRITE_END], STDOUT_FILENO)) {
perror("dup2 error on input_pipe");
exit (EXIT_FAILURE);
}
close(input_pipe[WRITE_END]);
if (0 > execvp(command, argv)) {
char tmp [PATH_MAX];
snprintf (tmp, sizeof(tmp), "Exec of \"%s\" failed", command);
perror (tmp);
}
exit (EXIT_FAILURE);
} else {
close(output_pipe[READ_END]);
close(input_pipe[WRITE_END]);
write(output_pipe[WRITE_END], inbuf, inbuf_size);
close(output_pipe[WRITE_END]);
char *buf = NULL, *buf_cur = NULL;
int buf_size = 0, bytes_read = 0;
do {
buf_size += BUFSIZ;
buf = realloc(buf, buf_size+1);
buf_cur = buf+buf_size-BUFSIZ;
result = read(input_pipe[READ_END], buf_cur, BUFSIZ);
if (result > 0) {
bytes_read += result;
if (result < BUFSIZ) {
buf_size -= (BUFSIZ - result);
buf = realloc(buf, buf_size+1);
break;
}
} else if (result < 0) {
free (buf);
buf = NULL;
return -1;
}
} while (result > 0);
close(input_pipe[READ_END]);
if (bytes_read == 0) {
free(buf);
return -1;
} else {
buf[buf_size] = '\0';
*outbuf = buf;
*outbuf_size = buf_size;
}
}
return 0;
if (bytes_read == 0) {
free (buf);
return -1;
} else {
buf[buf_size] = '\0';
*outbuf = buf;
*outbuf_size = buf_size;
}
}
return 0;
}

View File

@ -17,5 +17,5 @@
#pragma once
#include "main.h"
int FilterExecURL (struct feed * cur_ptr);
int FilterPipeNG (struct feed * cur_ptr);
int FilterExecURL (struct feed* cur_ptr);
int FilterPipeNG (struct feed* cur_ptr);

File diff suppressed because it is too large Load Diff

View File

@ -16,5 +16,5 @@
#pragma once
void sig_winch(int p);
void sig_winch (int p);
void UIMainInterface (void);

View File

@ -28,412 +28,430 @@
#include <libxml/parser.h>
#include <syslog.h>
struct feed * newFeedStruct (void) {
struct feed* new = calloc (1, sizeof(struct feed));
new->netio_error = NET_ERR_OK;
return new;
struct feed* newFeedStruct (void)
{
struct feed* new = calloc (1, sizeof (struct feed));
new->netio_error = NET_ERR_OK;
return new;
}
static void GetHTTPErrorString (char* errorstring, size_t size, unsigned httpstatus) {
const char* statusmsg = "HTTP %u!";
switch (httpstatus) {
case 400: statusmsg = "Bad request"; break;
case 402: statusmsg = "Payment required"; break;
case 403: statusmsg = "Access denied"; break;
case 500: statusmsg = "Internal server error"; break;
case 501: statusmsg = "Not implemented"; break;
case 502:
case 503: statusmsg = "Service unavailable"; break;
}
snprintf (errorstring, size, statusmsg, httpstatus);
static void GetHTTPErrorString (char* errorstring, size_t size, unsigned httpstatus)
{
const char* statusmsg = "HTTP %u!";
switch (httpstatus) {
case 400:
statusmsg = "Bad request";
break;
case 402:
statusmsg = "Payment required";
break;
case 403:
statusmsg = "Access denied";
break;
case 500:
statusmsg = "Internal server error";
break;
case 501:
statusmsg = "Not implemented";
break;
case 502:
case 503:
statusmsg = "Service unavailable";
break;
}
snprintf (errorstring, size, statusmsg, httpstatus);
}
static void PrintUpdateError (const struct feed* cur_ptr) {
enum netio_error err = cur_ptr->netio_error;
char errstr[256];
switch (err) {
case NET_ERR_URL_INVALID:
snprintf(errstr, sizeof(errstr), _("%s: Invalid URL!"), cur_ptr->title);
break;
case NET_ERR_SOCK_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Couldn't create network socket!"), cur_ptr->title);
break;
case NET_ERR_HOST_NOT_FOUND:
snprintf(errstr, sizeof(errstr), _("%s: Can't resolve host!"), cur_ptr->title);
break;
case NET_ERR_CONN_REFUSED:
snprintf(errstr, sizeof(errstr), _("%s: Connection refused!"), cur_ptr->title);
break;
case NET_ERR_CONN_FAILED:
snprintf(errstr, sizeof(errstr), _("%s: Couldn't connect to server: %s"),
cur_ptr->title,
(strerror(cur_ptr->connectresult) ? strerror(cur_ptr->connectresult) : "(null)"));
break;
case NET_ERR_TIMEOUT:
snprintf(errstr, sizeof(errstr), _("%s: Connection timed out."), cur_ptr->title);
break;
case NET_ERR_UNKNOWN:
break;
case NET_ERR_REDIRECT_COUNT_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Too many HTTP redirects encountered! Giving up."), cur_ptr->title);
break;
case NET_ERR_REDIRECT_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Server sent an invalid redirect!"), cur_ptr->title);
break;
case NET_ERR_HTTP_410:
case NET_ERR_HTTP_404:
snprintf(errstr, sizeof(errstr), _("%s: This feed no longer exists. Please unsubscribe!"), cur_ptr->title);
break;
case NET_ERR_HTTP_NON_200: {
char httperrstr[64];
GetHTTPErrorString(httperrstr, sizeof(httperrstr), cur_ptr->lasthttpstatus);
snprintf(errstr, sizeof(errstr), _("%s: Could not download feed: %s"), cur_ptr->title, httperrstr);
} break;
case NET_ERR_HTTP_PROTO_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Error in server reply."), cur_ptr->title);
break;
case NET_ERR_AUTH_FAILED:
snprintf(errstr, sizeof(errstr), _("%s: Authentication failed!"), cur_ptr->title);
break;
case NET_ERR_AUTH_NO_AUTHINFO:
snprintf(errstr, sizeof(errstr), _("%s: URL does not contain authentication information!"), cur_ptr->title);
break;
case NET_ERR_AUTH_GEN_AUTH_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Could not generate authentication information!"), cur_ptr->title);
break;
case NET_ERR_AUTH_UNSUPPORTED:
snprintf(errstr, sizeof(errstr), _("%s: Unsupported authentication method requested by server!"), cur_ptr->title);
break;
case NET_ERR_GZIP_ERR:
snprintf(errstr, sizeof(errstr), _("%s: Error decompressing server reply!"), cur_ptr->title);
break;
case NET_ERR_CHUNKED:
snprintf(errstr, sizeof(errstr), _("%s: Error in server reply. Chunked encoding is not supported."), cur_ptr->title);
break;
default:
snprintf(errstr, sizeof(errstr), _("%s: Some error occured for which no specific error message was written."), cur_ptr->title);
break;
}
UIStatus(errstr, 2, 1);
syslog (LOG_ERR, "%s", errstr);
static void PrintUpdateError (const struct feed* cur_ptr)
{
enum netio_error err = cur_ptr->netio_error;
char errstr[256];
switch (err) {
case NET_ERR_URL_INVALID:
snprintf (errstr, sizeof (errstr), _("%s: Invalid URL!"), cur_ptr->title);
break;
case NET_ERR_SOCK_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Couldn't create network socket!"), cur_ptr->title);
break;
case NET_ERR_HOST_NOT_FOUND:
snprintf (errstr, sizeof (errstr), _("%s: Can't resolve host!"), cur_ptr->title);
break;
case NET_ERR_CONN_REFUSED:
snprintf (errstr, sizeof (errstr), _("%s: Connection refused!"), cur_ptr->title);
break;
case NET_ERR_CONN_FAILED:
snprintf (errstr, sizeof (errstr), _("%s: Couldn't connect to server: %s"), cur_ptr->title, (strerror (cur_ptr->connectresult) ? strerror (cur_ptr->connectresult) : "(null)"));
break;
case NET_ERR_TIMEOUT:
snprintf (errstr, sizeof (errstr), _("%s: Connection timed out."), cur_ptr->title);
break;
case NET_ERR_UNKNOWN:
break;
case NET_ERR_REDIRECT_COUNT_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Too many HTTP redirects encountered! Giving up."), cur_ptr->title);
break;
case NET_ERR_REDIRECT_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Server sent an invalid redirect!"), cur_ptr->title);
break;
case NET_ERR_HTTP_410:
case NET_ERR_HTTP_404:
snprintf (errstr, sizeof (errstr), _("%s: This feed no longer exists. Please unsubscribe!"), cur_ptr->title);
break;
case NET_ERR_HTTP_NON_200:{
char httperrstr[64];
GetHTTPErrorString (httperrstr, sizeof (httperrstr), cur_ptr->lasthttpstatus);
snprintf (errstr, sizeof (errstr), _("%s: Could not download feed: %s"), cur_ptr->title, httperrstr);
}
break;
case NET_ERR_HTTP_PROTO_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Error in server reply."), cur_ptr->title);
break;
case NET_ERR_AUTH_FAILED:
snprintf (errstr, sizeof (errstr), _("%s: Authentication failed!"), cur_ptr->title);
break;
case NET_ERR_AUTH_NO_AUTHINFO:
snprintf (errstr, sizeof (errstr), _("%s: URL does not contain authentication information!"), cur_ptr->title);
break;
case NET_ERR_AUTH_GEN_AUTH_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Could not generate authentication information!"), cur_ptr->title);
break;
case NET_ERR_AUTH_UNSUPPORTED:
snprintf (errstr, sizeof (errstr), _("%s: Unsupported authentication method requested by server!"), cur_ptr->title);
break;
case NET_ERR_GZIP_ERR:
snprintf (errstr, sizeof (errstr), _("%s: Error decompressing server reply!"), cur_ptr->title);
break;
case NET_ERR_CHUNKED:
snprintf (errstr, sizeof (errstr), _("%s: Error in server reply. Chunked encoding is not supported."), cur_ptr->title);
break;
default:
snprintf (errstr, sizeof (errstr), _("%s: Some error occured for which no specific error message was written."), cur_ptr->title);
break;
}
UIStatus (errstr, 2, 1);
syslog (LOG_ERR, "%s", errstr);
}
// Update given feed from server.
// Reload XML document and replace in memory cur_ptr->xmltext with it.
int UpdateFeed (struct feed * cur_ptr) {
if (cur_ptr == NULL)
return 1;
int UpdateFeed (struct feed* cur_ptr)
{
if (cur_ptr == NULL)
return 1;
// Smart feeds are generated in the fly.
if (cur_ptr->smartfeed == 1)
return 0;
// If current feed is an exec URL, run that command, otherwise fetch resource from network.
if (cur_ptr->execurl)
FilterExecURL (cur_ptr);
else {
// Need to work on a copy of ->feedurl, because DownloadFeed() changes the pointer.
char* feedurl = strdup (cur_ptr->feedurl);
free (cur_ptr->xmltext);
cur_ptr->xmltext = DownloadFeed (feedurl, cur_ptr, 0);
free (feedurl);
// Set title and link structure to something.
// To the feedurl in this case so the program show something
// as placeholder instead of crash.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
if (cur_ptr->link == NULL)
cur_ptr->link = strdup (cur_ptr->feedurl);
// If the download function returns a NULL pointer return from here.
if (!cur_ptr->xmltext) {
if (cur_ptr->problem)
PrintUpdateError (cur_ptr);
return 1;
}
}
// Feed downloaded content through the defined filter.
if (cur_ptr->perfeedfilter != NULL)
FilterPipeNG (cur_ptr);
// If there is no feed, return.
if (!cur_ptr->xmltext)
return 1;
if (DeXML(cur_ptr)) {
UIStatus (_("Invalid XML! Cannot parse this feed!"), 2, 1);
// Activate feed problem flag.
cur_ptr->problem = true;
return 1;
}
// We don't need these anymore. Free the raw XML to save some memory.
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
// Mark the time to detect modifications
cur_ptr->mtime = time(NULL);
// Smart feeds are generated in the fly.
if (cur_ptr->smartfeed == 1)
return 0;
// If current feed is an exec URL, run that command, otherwise fetch resource from network.
if (cur_ptr->execurl)
FilterExecURL (cur_ptr);
else {
// Need to work on a copy of ->feedurl, because DownloadFeed() changes the pointer.
char* feedurl = strdup (cur_ptr->feedurl);
free (cur_ptr->xmltext);
cur_ptr->xmltext = DownloadFeed (feedurl, cur_ptr, 0);
free (feedurl);
// Set title and link structure to something.
// To the feedurl in this case so the program show something
// as placeholder instead of crash.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
if (cur_ptr->link == NULL)
cur_ptr->link = strdup (cur_ptr->feedurl);
// If the download function returns a NULL pointer return from here.
if (!cur_ptr->xmltext) {
if (cur_ptr->problem)
PrintUpdateError (cur_ptr);
return 1;
}
}
// Feed downloaded content through the defined filter.
if (cur_ptr->perfeedfilter != NULL)
FilterPipeNG (cur_ptr);
// If there is no feed, return.
if (!cur_ptr->xmltext)
return 1;
if (DeXML (cur_ptr)) {
UIStatus (_("Invalid XML! Cannot parse this feed!"), 2, 1);
// Activate feed problem flag.
cur_ptr->problem = true;
return 1;
}
// We don't need these anymore. Free the raw XML to save some memory.
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
// Mark the time to detect modifications
cur_ptr->mtime = time (NULL);
return 0;
}
int UpdateAllFeeds (void) {
for (struct feed* f = _feed_list; f; f = f->next)
if (0 != UpdateFeed (f))
continue;
return 0;
int UpdateAllFeeds (void)
{
for (struct feed * f = _feed_list; f; f = f->next)
if (0 != UpdateFeed (f))
continue;
return 0;
}
// Load feed from disk. And call UpdateFeed if neccessary.
int LoadFeed (struct feed * cur_ptr) {
// Smart feeds are generated in the fly.
if (cur_ptr->smartfeed == 1)
return 0;
char* hashme = Hashify(cur_ptr->feedurl);
char cachefilename [PATH_MAX];
snprintf (cachefilename, sizeof(cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv("HOME"), hashme);
free (hashme);
FILE* cache = fopen (cachefilename, "r");
if (cache == NULL) {
char msgbuf[128];
snprintf (msgbuf, sizeof(msgbuf), _("Cache for %s is toast. Reloading from server..."), cur_ptr->feedurl);
UIStatus (msgbuf, 0, 0);
if (UpdateFeed (cur_ptr) != 0)
return 1;
return 0;
}
struct stat cachest;
fstat (fileno(cache), &cachest);
// Read complete cachefile.
int len = 0; // Internal usage for realloc.
char filebuf[BUFSIZ]; // File I/O block buffer.
while (!feof(cache)) {
// Use binary read, UTF-8 data!
size_t retval = fread (filebuf, 1, sizeof(filebuf), cache);
if (retval == 0)
break;
cur_ptr->xmltext = realloc (cur_ptr->xmltext, len+retval + 1);
memcpy (cur_ptr->xmltext+len, filebuf, retval);
len += retval;
if (retval != sizeof(filebuf))
break;
}
fclose (cache);
cur_ptr->content_length = len;
cur_ptr->xmltext[len] = '\0';
if (!cur_ptr->xmltext)
return 1;
// After loading DeXMLize the mess.
// If loading the feed from the disk fails, try downloading from the net.
if (DeXML(cur_ptr) != 0 && UpdateFeed(cur_ptr) != 0) {
// And if that fails as well, just continue without this feed.
char msgbuf [64];
snprintf (msgbuf, sizeof(msgbuf), _("Could not load %s!"), cur_ptr->feedurl);
UIStatus (msgbuf, 2, 1);
}
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
cur_ptr->mtime = cachest.st_mtime;
int LoadFeed (struct feed* cur_ptr)
{
// Smart feeds are generated in the fly.
if (cur_ptr->smartfeed == 1)
return 0;
char* hashme = Hashify (cur_ptr->feedurl);
char cachefilename[PATH_MAX];
snprintf (cachefilename, sizeof (cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme);
free (hashme);
FILE* cache = fopen (cachefilename, "r");
if (cache == NULL) {
char msgbuf[128];
snprintf (msgbuf, sizeof (msgbuf), _("Cache for %s is toast. Reloading from server..."), cur_ptr->feedurl);
UIStatus (msgbuf, 0, 0);
if (UpdateFeed (cur_ptr) != 0)
return 1;
return 0;
}
struct stat cachest;
fstat (fileno (cache), &cachest);
// Read complete cachefile.
int len = 0; // Internal usage for realloc.
char filebuf[BUFSIZ]; // File I/O block buffer.
while (!feof (cache)) {
// Use binary read, UTF-8 data!
size_t retval = fread (filebuf, 1, sizeof (filebuf), cache);
if (retval == 0)
break;
cur_ptr->xmltext = realloc (cur_ptr->xmltext, len + retval + 1);
memcpy (cur_ptr->xmltext + len, filebuf, retval);
len += retval;
if (retval != sizeof (filebuf))
break;
}
fclose (cache);
cur_ptr->content_length = len;
cur_ptr->xmltext[len] = '\0';
if (!cur_ptr->xmltext)
return 1;
// After loading DeXMLize the mess.
// If loading the feed from the disk fails, try downloading from the net.
if (DeXML (cur_ptr) != 0 && UpdateFeed (cur_ptr) != 0) {
// And if that fails as well, just continue without this feed.
char msgbuf[64];
snprintf (msgbuf, sizeof (msgbuf), _("Could not load %s!"), cur_ptr->feedurl);
UIStatus (msgbuf, 2, 1);
}
free (cur_ptr->xmltext);
cur_ptr->xmltext = NULL;
cur_ptr->mtime = cachest.st_mtime;
return 0;
}
int LoadAllFeeds (unsigned numfeeds) {
if (!numfeeds)
return 0;
UIStatus (_("Loading cache ["), 0, 0);
unsigned titlestrlen = strlen (_("Loading cache ["));
int oldnumobjects = 0;
unsigned count = 1;
for (struct feed* f = _feed_list; f; f = f->next) {
// Progress bar
int numobjects = count*(COLS-titlestrlen-2)/numfeeds-2;
if (numobjects < 1)
numobjects = 1;
if (numobjects > oldnumobjects) {
DrawProgressBar(numobjects, titlestrlen);
oldnumobjects = numobjects;
}
if (LoadFeed(f) != 0)
continue;
++count;
}
int LoadAllFeeds (unsigned numfeeds)
{
if (!numfeeds)
return 0;
UIStatus (_("Loading cache ["), 0, 0);
unsigned titlestrlen = strlen (_("Loading cache ["));
int oldnumobjects = 0;
unsigned count = 1;
for (struct feed * f = _feed_list; f; f = f->next) {
// Progress bar
int numobjects = count * (COLS - titlestrlen - 2) / numfeeds - 2;
if (numobjects < 1)
numobjects = 1;
if (numobjects > oldnumobjects) {
DrawProgressBar (numobjects, titlestrlen);
oldnumobjects = numobjects;
}
if (LoadFeed (f) != 0)
continue;
++count;
}
return 0;
}
static void WriteFeedUrls (void)
{
if (!_feed_list_changed)
return;
if (!_feed_list_changed)
return;
// Make a backup of urls.
char urlsfilename [PATH_MAX];
snprintf (urlsfilename, sizeof(urlsfilename), SNOWNEWS_CONFIG_DIR "urls", getenv("HOME"));
// Make a backup of urls.
char urlsfilename[PATH_MAX];
snprintf (urlsfilename, sizeof (urlsfilename), SNOWNEWS_CONFIG_DIR "urls", getenv ("HOME"));
// Write urls
FILE* urlfile = fopen (urlsfilename, "w");
if (!urlfile) {
syslog (LOG_ERR, "error saving urls: %s", strerror(errno));
return;
// Write urls
FILE* urlfile = fopen (urlsfilename, "w");
if (!urlfile) {
syslog (LOG_ERR, "error saving urls: %s", strerror (errno));
return;
}
for (const struct feed * f = _feed_list; f; f = f->next) {
fputs (f->feedurl, urlfile);
fputc ('|', urlfile);
if (f->custom_title)
fputs (f->title, urlfile);
fputc ('|', urlfile);
for (const struct feedcategories * c = f->feedcategories; c; c = c->next) {
fputs (c->name, urlfile);
if (c->next) // Only add a colon of we run the loop again!
fputc (',', urlfile);
}
for (const struct feed* f = _feed_list; f; f = f->next) {
fputs (f->feedurl, urlfile);
fputc ('|', urlfile);
if (f->custom_title)
fputs (f->title, urlfile);
fputc ('|', urlfile);
for (const struct feedcategories* c = f->feedcategories; c; c = c->next) {
fputs (c->name, urlfile);
if (c->next) // Only add a colon of we run the loop again!
fputc (',', urlfile);
}
fputc ('|', urlfile);
if (f->perfeedfilter != NULL)
fputs (f->perfeedfilter, urlfile);
fputc ('|', urlfile);
if (f->perfeedfilter != NULL)
fputs (f->perfeedfilter, urlfile);
fputc ('\n', urlfile); // Add newline character.
}
fclose (urlfile);
_feed_list_changed = false;
fputc ('\n', urlfile); // Add newline character.
}
fclose (urlfile);
_feed_list_changed = false;
}
static void WriteFeedCache (const struct feed* feed)
{
char* hashme = Hashify(feed->feedurl);
char cachefilename [PATH_MAX];
snprintf (cachefilename, sizeof(cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv("HOME"), hashme);
free (hashme);
char* hashme = Hashify (feed->feedurl);
char cachefilename[PATH_MAX];
snprintf (cachefilename, sizeof (cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme);
free (hashme);
// Check if the feed has been modified since last loaded from this file
struct stat cachest;
if (0 == stat (cachefilename, &cachest) && cachest.st_mtime >= feed->mtime)
return;
// Check if the feed has been modified since last loaded from this file
struct stat cachest;
if (0 == stat (cachefilename, &cachest) && cachest.st_mtime >= feed->mtime)
return;
FILE* cache = fopen (cachefilename, "w");
if (!cache) {
syslog (LOG_ERR, "error writing cache file '%s': %s", cachefilename, strerror(errno));
return;
}
FILE* cache = fopen (cachefilename, "w");
if (!cache) {
syslog (LOG_ERR, "error writing cache file '%s': %s", cachefilename, strerror (errno));
return;
}
fputs ("<?xml version=\"1.0\" ?>\n\n<rdf:RDF\n xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n xmlns=\"http://purl.org/rss/1.0/\"\n xmlns:snow=\"http://snownews.kcore.de/ns/1.0/\">\n\n", cache);
fputs ("<?xml version=\"1.0\" ?>\n\n<rdf:RDF\n xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n xmlns=\"http://purl.org/rss/1.0/\"\n xmlns:snow=\"http://snownews.kcore.de/ns/1.0/\">\n\n", cache);
if (feed->lastmodified != NULL) {
fputs ("<snow:lastmodified>", cache);
fputs (feed->lastmodified, cache);
fputs ("</snow:lastmodified>\n", cache);
}
if (feed->lastmodified != NULL) {
fputs ("<snow:lastmodified>", cache);
fputs (feed->lastmodified, cache);
fputs ("</snow:lastmodified>\n", cache);
}
fputs ("<channel rdf:about=\"", cache);
fputs ("<channel rdf:about=\"", cache);
char* encoded = (char*) xmlEncodeEntitiesReentrant (NULL, (xmlChar*) feed->feedurl);
char* encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->feedurl);
fputs (encoded, cache);
free (encoded);
fputs ("\">\n<title>", cache);
if (feed->original != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->original);
fputs (encoded, cache);
free (encoded);
} else if (feed->title != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->title);
fputs (encoded, cache);
free (encoded);
}
fputs ("</title>\n<link>", cache);
if (feed->link != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->link);
fputs (encoded, cache);
free (encoded);
}
fputs ("</link>\n<description>", cache);
if (feed->description != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->description);
fputs (encoded, cache);
free (encoded);
}
fputs ("</description>\n</channel>\n\n", cache);
for (const struct newsitem * item = feed->items; item; item = item->next) {
fputs ("<item rdf:about=\"", cache);
if (item->data->link != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) item->data->link);
fputs (encoded, cache);
free (encoded);
}
fputs ("\">\n<title>", cache);
if (feed->original != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)feed->original);
fputs (encoded, cache);
free (encoded);
} else if (feed->title != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)feed->title);
fputs (encoded, cache);
free (encoded);
if (item->data->title != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) item->data->title);
fputs (encoded, cache);
free (encoded);
}
fputs ("</title>\n<link>", cache);
if (feed->link != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)feed->link);
fputs (encoded, cache);
free (encoded);
if (item->data->link != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) item->data->link);
fputs (encoded, cache);
free (encoded);
}
fputs ("</link>\n<description>", cache);
if (feed->description != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)feed->description);
fputs (encoded, cache);
free (encoded);
if (item->data->description != NULL) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) item->data->description);
fputs (encoded, cache);
free (encoded);
}
fputs ("</description>\n</channel>\n\n", cache);
for (const struct newsitem* item = feed->items; item; item = item->next) {
fputs ("<item rdf:about=\"", cache);
if (item->data->link != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->link);
fputs (encoded, cache);
free (encoded);
}
fputs ("\">\n<title>", cache);
if (item->data->title != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->title);
fputs (encoded, cache);
free (encoded);
}
fputs ("</title>\n<link>", cache);
if (item->data->link != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->link);
fputs (encoded, cache);
free (encoded);
}
fputs ("</link>\n<description>", cache);
if (item->data->description != NULL) {
encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->description);
fputs (encoded, cache);
free (encoded);
}
fputs ("</description>\n<snow:readstatus>", cache);
putc ('0'+item->data->readstatus, cache);
fputs ("</snow:readstatus>\n<snow:hash>", cache);
if (item->data->hash)
fputs (item->data->hash, cache);
fputs ("</snow:hash>\n<snow:date>", cache);
fprintf (cache, "%u", item->data->date);
fputs ("</snow:date>\n</item>\n\n", cache);
}
fputs ("</rdf:RDF>", cache);
fclose (cache);
fputs ("</description>\n<snow:readstatus>", cache);
putc ('0' + item->data->readstatus, cache);
fputs ("</snow:readstatus>\n<snow:hash>", cache);
if (item->data->hash)
fputs (item->data->hash, cache);
fputs ("</snow:hash>\n<snow:date>", cache);
fprintf (cache, "%u", item->data->date);
fputs ("</snow:date>\n</item>\n\n", cache);
}
fputs ("</rdf:RDF>", cache);
fclose (cache);
}
// Write in memory structures to disk cache.
// Usually called before program exit.
void WriteCache (void) {
UIStatus (_("Saving settings ["), 0, 0);
void WriteCache (void)
{
UIStatus (_("Saving settings ["), 0, 0);
WriteFeedUrls();
WriteFeedUrls();
// Save feed cache
unsigned numfeeds = 0;
for (const struct feed* f = _feed_list; f; f = f->next)
++numfeeds;
// Save feed cache
unsigned numfeeds = 0;
for (const struct feed * f = _feed_list; f; f = f->next)
++numfeeds;
unsigned count = 1;
int oldnumobjects = 0;
unsigned titlestrlen = strlen (_("Saving settings ["));
unsigned count = 1;
int oldnumobjects = 0;
unsigned titlestrlen = strlen (_("Saving settings ["));
for (const struct feed* cur_ptr = _feed_list; cur_ptr; cur_ptr = cur_ptr->next) {
// Progress bar
int numobjects = count*(COLS-titlestrlen-2)/numfeeds-2;
if (numobjects < 1)
numobjects = 1;
if (numobjects > oldnumobjects) {
DrawProgressBar(numobjects, titlestrlen);
oldnumobjects = numobjects;
}
++count;
// Discard smart feeds from cache.
if (cur_ptr->smartfeed)
continue;
// Write cache.
WriteFeedCache (cur_ptr);
for (const struct feed * cur_ptr = _feed_list; cur_ptr; cur_ptr = cur_ptr->next) {
// Progress bar
int numobjects = count * (COLS - titlestrlen - 2) / numfeeds - 2;
if (numobjects < 1)
numobjects = 1;
if (numobjects > oldnumobjects) {
DrawProgressBar (numobjects, titlestrlen);
oldnumobjects = numobjects;
}
return;
++count;
// Discard smart feeds from cache.
if (cur_ptr->smartfeed)
continue;
// Write cache.
WriteFeedCache (cur_ptr);
}
return;
}

View File

@ -17,9 +17,9 @@
#pragma once
#include "main.h"
struct feed * newFeedStruct (void);
int UpdateFeed (struct feed * cur_ptr);
struct feed* newFeedStruct (void);
int UpdateFeed (struct feed* cur_ptr);
int UpdateAllFeeds (void);
int LoadFeed (struct feed * cur_ptr);
int LoadFeed (struct feed* cur_ptr);
int LoadAllFeeds (unsigned numfeeds);
void WriteCache (void);

385
main.c
View File

@ -35,56 +35,55 @@ struct feed* _unfiltered_feed_list = NULL; // Backup first pointer for filter mo
bool _feed_list_changed = false;
struct settings _settings = {
.keybindings = {
//{{{2 Default values for keybindings.
// If some are defined differently in the keybindings file
// they will be overwritten. If some are missing or broken
// these are sane defaults.
.about = 'A',
.addfeed = 'a',
.andxor = 'X',
.categorize = 'C',
.changefeedname = 'c',
.deletefeed = 'D',
.dfltbrowser = 'B',
.end = '>',
.enter = 'l',
.feedinfo = 'i',
.filter = 'f',
.filtercurrent = 'g',
.forcereload = 'T',
.help = 'h',
.home = '<',
.markallread = 'm',
.markread = 'm',
.markunread = 'M',
.movedown = 'N',
.moveup = 'P',
.newheadlines = 'H',
.next = 'n',
.nofilter = 'F',
.pdown = ' ',
.perfeedfilter = 'e',
.prev = 'p',
.prevmenu = 'q',
.pup = 'b',
.quit = 'q',
.reload = 'r',
.reloadall = 'R',
.sortfeeds = 's',
.typeahead = '/',
.urljump = 'o',
.urljump2 = 'O'
//}}}2
},
.color = {
.feedtitle = -1,
.feedtitlebold = 0,
.newitems = 5,
.newitemsbold = 0,
.urljump = 4,
.urljumpbold = 0
},
.keybindings = {
//{{{2 Default values for keybindings.
// If some are defined differently in the keybindings file
// they will be overwritten. If some are missing or broken
// these are sane defaults.
.about = 'A',
.addfeed = 'a',
.andxor = 'X',
.categorize = 'C',
.changefeedname = 'c',
.deletefeed = 'D',
.dfltbrowser = 'B',
.end = '>',
.enter = 'l',
.feedinfo = 'i',
.filter = 'f',
.filtercurrent = 'g',
.forcereload = 'T',
.help = 'h',
.home = '<',
.markallread = 'm',
.markread = 'm',
.markunread = 'M',
.movedown = 'N',
.moveup = 'P',
.newheadlines = 'H',
.next = 'n',
.nofilter = 'F',
.pdown = ' ',
.perfeedfilter = 'e',
.prev = 'p',
.prevmenu = 'q',
.pup = 'b',
.quit = 'q',
.reload = 'r',
.reloadall = 'R',
.sortfeeds = 's',
.typeahead = '/',
.urljump = 'o',
.urljump2 = 'O'
//}}}2
},
.color = {
.feedtitle = -1,
.feedtitlebold = 0,
.newitems = 5,
.newitemsbold = 0,
.urljump = 4,
.urljumpbold = 0 },
};
//----------------------------------------------------------------------
@ -98,53 +97,55 @@ enum EPIDAction { pid_file_delete, pid_file_create };
static void make_pidfile_name (char* fnbuf, size_t fnbufsz)
{
const char* rundir = getenv("XDG_RUNTIME_DIR");
if (rundir)
snprintf (fnbuf, fnbufsz, "%s/snownews.pid", rundir);
else
snprintf (fnbuf, fnbufsz, SNOWNEWS_CONFIG_DIR "pid", getenv("HOME"));
const char* rundir = getenv ("XDG_RUNTIME_DIR");
if (rundir)
snprintf (fnbuf, fnbufsz, "%s/snownews.pid", rundir);
else
snprintf (fnbuf, fnbufsz, SNOWNEWS_CONFIG_DIR "pid", getenv ("HOME"));
}
static void modifyPIDFile (enum EPIDAction action) {
char pid_path [PATH_MAX];
make_pidfile_name (pid_path, sizeof(pid_path));
if (action == pid_file_create) {
FILE* file = fopen(pid_path, "w");
if (!file) {
printf("Unable to write PID file %s!", pid_path);
return;
}
fprintf(file, "%d", getpid());
fclose(file);
} else
unlink(pid_path);
}
static void checkPIDFile (void) {
char pid_path [PATH_MAX];
make_pidfile_name (pid_path, sizeof(pid_path));
FILE* pidfile = fopen(pid_path, "r");
if (!pidfile)
return;
char pidbuf[12];
fgets (pidbuf, sizeof(pidbuf), pidfile);
fclose (pidfile);
pid_t pid = atoi(pidbuf);
if (kill(pid, 0) == 0) {
printf("Snownews seems to be already running with process ID %d.\n", pid);
printf("A pid file exists at \"%s\".\n", pid_path);
exit(2);
} else {
printf("A pid file exists at \"%s\",\nbut Snownews doesn't seem to be running. Delete that file and start it again.\n", pid_path);
printf("Continue anyway? (y/n) ");
char ybuf[2] = {};
fgets (ybuf, sizeof(ybuf), stdin);
if (ybuf[0] != 'y')
exit(2);
modifyPIDFile (pid_file_delete);
static void modifyPIDFile (enum EPIDAction action)
{
char pid_path[PATH_MAX];
make_pidfile_name (pid_path, sizeof (pid_path));
if (action == pid_file_create) {
FILE* file = fopen (pid_path, "w");
if (!file) {
printf ("Unable to write PID file %s!", pid_path);
return;
}
fprintf (file, "%d", getpid());
fclose (file);
} else
unlink (pid_path);
}
static void checkPIDFile (void)
{
char pid_path[PATH_MAX];
make_pidfile_name (pid_path, sizeof (pid_path));
FILE* pidfile = fopen (pid_path, "r");
if (!pidfile)
return;
char pidbuf[12];
fgets (pidbuf, sizeof (pidbuf), pidfile);
fclose (pidfile);
pid_t pid = atoi (pidbuf);
if (kill (pid, 0) == 0) {
printf ("Snownews seems to be already running with process ID %d.\n", pid);
printf ("A pid file exists at \"%s\".\n", pid_path);
exit (2);
} else {
printf ("A pid file exists at \"%s\",\nbut Snownews doesn't seem to be running. Delete that file and start it again.\n", pid_path);
printf ("Continue anyway? (y/n) ");
char ybuf[2] = { };
fgets (ybuf, sizeof (ybuf), stdin);
if (ybuf[0] != 'y')
exit (2);
modifyPIDFile (pid_file_delete);
}
}
//}}}-------------------------------------------------------------------
@ -162,8 +163,8 @@ static int MakeStderrLogFileName (char* logfile, size_t logfilesz)
// If the log file is empty at exit, delete it
static void CleanupStderrLog (void)
{
char logfile [PATH_MAX];
if (0 == MakeStderrLogFileName (logfile, sizeof(logfile))) {
char logfile[PATH_MAX];
if (0 == MakeStderrLogFileName (logfile, sizeof (logfile))) {
struct stat st;
if (0 == stat (logfile, &st) && st.st_size == 0)
unlink (logfile);
@ -173,11 +174,11 @@ static void CleanupStderrLog (void)
// Redirects stderr into a log file in /tmp
void RedirectStderrToLog (void)
{
char logfile [PATH_MAX];
if (0 > MakeStderrLogFileName (logfile, sizeof(logfile)))
char logfile[PATH_MAX];
if (0 > MakeStderrLogFileName (logfile, sizeof (logfile)))
return;
int fd = open (logfile, O_WRONLY| O_APPEND| O_CREAT, S_IRUSR| S_IWUSR);
int fd = open (logfile, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0)
return;
@ -190,42 +191,45 @@ void RedirectStderrToLog (void)
//{{{ Signal handling
// Deinit ncurses and quit.
_Noreturn void MainQuit (const char* func, const char* error) {
if (!error) // Only save settings if we didn't exit on error.
WriteCache();
endwin(); // Make sure no ncurses function is called after this point!
modifyPIDFile(pid_file_delete);
_Noreturn void MainQuit (const char* func, const char* error)
{
if (!error) // Only save settings if we didn't exit on error.
WriteCache();
endwin(); // Make sure no ncurses function is called after this point!
modifyPIDFile (pid_file_delete);
if (last_signal)
printf ("Exiting via signal %d.\n", last_signal);
if (error) {
printf (_("Aborting program execution!\nAn internal error occured. Snownews has quit, no changes has been saved!\n"));
printf (_("Please submit a bugreport at https://github.com/msharov/snownews/issues\n"));
printf ("----\n");
// Please don't localize! I don't want to receive Japanese error messages.
// Thanks. :)
printf ("While executing: %s\n", func);
printf ("Error as reported by the system: %s\n\n", error);
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
if (last_signal)
printf ("Exiting via signal %d.\n", last_signal);
if (error) {
printf (_("Aborting program execution!\nAn internal error occured. Snownews has quit, no changes has been saved!\n"));
printf (_("Please submit a bugreport at https://github.com/msharov/snownews/issues\n"));
printf ("----\n");
// Please don't localize! I don't want to receive Japanese error messages.
// Thanks. :)
printf ("While executing: %s\n", func);
printf ("Error as reported by the system: %s\n\n", error);
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
}
// Signal handler function.
static _Noreturn void MainSignalHandler (int sig) {
last_signal = sig;
static _Noreturn void MainSignalHandler (int sig)
{
last_signal = sig;
// If there is a _unfiltered_feed_list!=NULL a filter is set. Reset _feed_list
// so the correct list gets written on the disk when exisiting via SIGINT.
if (_unfiltered_feed_list)
_feed_list = _unfiltered_feed_list;
MainQuit (NULL, "Signal");
// If there is a _unfiltered_feed_list!=NULL a filter is set. Reset _feed_list
// so the correct list gets written on the disk when exisiting via SIGINT.
if (_unfiltered_feed_list)
_feed_list = _unfiltered_feed_list;
MainQuit (NULL, "Signal");
}
// Automatic child reaper.
static void sigChildHandler (int sig __attribute__((unused))) {
// Wait for any child without blocking
waitpid (-1, NULL, WNOHANG);
static void sigChildHandler (int sig __attribute__((unused)))
{
// Wait for any child without blocking
waitpid (-1, NULL, WNOHANG);
}
static void InstallSignalHandlers (void)
@ -239,33 +243,37 @@ static void InstallSignalHandlers (void)
// Un-broken pipify
signal (SIGPIPE, SIG_IGN);
signal (SIGCHLD, sigChildHandler);
#ifdef SIGWINCH
signal (SIGWINCH, sig_winch);
#endif
#ifdef SIGWINCH
signal (SIGWINCH, sig_winch);
#endif
}
//}}}-------------------------------------------------------------------
//{{{ Command line options help
static void printHelp (void) {
printf (_("Snownews %s\n\n"), SNOWNEWS_VERSTRING);
printf (_("usage: snownews [-huV] [--help|--update|--version]\n\n"));
printf (_("\t--charset|-l\tForce using this charset.\n"));
printf (_("\t--cursor-on|-c\tForce cursor always visible.\n"));
printf (_("\t--help|-h\tPrint this help message.\n"));
printf (_("\t--update|-u\tAutomatically update every feed.\n"));
printf (_("\t--version|-V\tPrint version number and exit.\n"));
static void printHelp (void)
{
printf (_("Snownews %s\n\n"), SNOWNEWS_VERSTRING);
printf (_("usage: snownews [-huV] [--help|--update|--version]\n\n"));
printf (_("\t--charset|-l\tForce using this charset.\n"));
printf (_("\t--cursor-on|-c\tForce cursor always visible.\n"));
printf (_("\t--help|-h\tPrint this help message.\n"));
printf (_("\t--update|-u\tAutomatically update every feed.\n"));
printf (_("\t--version|-V\tPrint version number and exit.\n"));
}
static void badOption (const char * arg) {
printf (_("Option %s requires an argument.\n"), arg);
static void badOption (const char* arg)
{
printf (_("Option %s requires an argument.\n"), arg);
}
//}}}-------------------------------------------------------------------
//{{{ srandrand
inline static uint32_t ror32 (uint32_t v, unsigned n)
{ return (v >> n)|(v << (32-n)); }
{
return (v >> n) | (v << (32 - n));
}
// Randomly initializes the random number generator
static void srandrand (void)
@ -282,58 +290,59 @@ static void srandrand (void)
//}}}-------------------------------------------------------------------
int main (int argc, char *argv[]) {
InstallSignalHandlers();
srandrand(); // Init the pRNG. See about.c for usages of rand() ;)
#ifdef LOCALEPATH
setlocale (LC_ALL, "");
bindtextdomain ("snownews", LOCALEPATH);
textdomain ("snownews");
#endif
RedirectStderrToLog();
int main (int argc, char* argv[])
{
InstallSignalHandlers();
srandrand(); // Init the pRNG. See about.c for usages of rand() ;)
#ifdef LOCALEPATH
setlocale (LC_ALL, "");
bindtextdomain ("snownews", LOCALEPATH);
textdomain ("snownews");
#endif
RedirectStderrToLog();
bool autoupdate = false; // Automatically update feeds on app start... or not if set to 0.
for (int i = 1; i < argc; ++i) {
char* arg = argv[i];
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
printf (_("Snownews version %s\n\n"), SNOWNEWS_VERSION);
return EXIT_SUCCESS;
} else if (strcmp(arg, "-u") == 0 || strcmp(arg, "--update") == 0) {
autoupdate = true;
} else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--cursor-on") == 0) {
_settings.cursor_always_visible = true;
} else if (strcmp(arg, "-l") == 0 || strcmp(arg, "--charset") == 0) {
const char* chset = argv[i++];
if (chset)
_settings.global_charset = chset;
else {
badOption(arg);
return EXIT_FAILURE;
}
} else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
printHelp();
return EXIT_SUCCESS;
} else {
printf ("Unkown option given: \"%s\".\n", arg);
printHelp();
return EXIT_SUCCESS;
}
bool autoupdate = false; // Automatically update feeds on app start... or not if set to 0.
for (int i = 1; i < argc; ++i) {
char* arg = argv[i];
if (strcmp (arg, "--version") == 0 || strcmp (arg, "-V") == 0) {
printf (_("Snownews version %s\n\n"), SNOWNEWS_VERSION);
return EXIT_SUCCESS;
} else if (strcmp (arg, "-u") == 0 || strcmp (arg, "--update") == 0) {
autoupdate = true;
} else if (strcmp (arg, "-c") == 0 || strcmp (arg, "--cursor-on") == 0) {
_settings.cursor_always_visible = true;
} else if (strcmp (arg, "-l") == 0 || strcmp (arg, "--charset") == 0) {
const char* chset = argv[i++];
if (chset)
_settings.global_charset = chset;
else {
badOption (arg);
return EXIT_FAILURE;
}
} else if (strcmp (arg, "-h") == 0 || strcmp (arg, "--help") == 0) {
printHelp();
return EXIT_SUCCESS;
} else {
printf ("Unkown option given: \"%s\".\n", arg);
printHelp();
return EXIT_SUCCESS;
}
}
// Create PID file.
checkPIDFile();
modifyPIDFile (pid_file_create);
// Create PID file.
checkPIDFile();
modifyPIDFile (pid_file_create);
InitCurses();
InitCurses();
// Check if configfiles exist and create/read them.
LoadAllFeeds (Config());
if (autoupdate)
UpdateAllFeeds();
// Check if configfiles exist and create/read them.
LoadAllFeeds (Config());
if (autoupdate)
UpdateAllFeeds();
// Give control to main program loop.
UIMainInterface();
// Give control to main program loop.
UIMainInterface();
// We really shouldn't be here at all... ah well.
return EXIT_SUCCESS;
// We really shouldn't be here at all... ah well.
return EXIT_SUCCESS;
}

248
main.h
View File

@ -20,166 +20,166 @@
//----------------------------------------------------------------------
enum netio_error {
NET_ERR_OK,
// Init errors
NET_ERR_URL_INVALID,
// Connect errors
NET_ERR_SOCK_ERR,
NET_ERR_HOST_NOT_FOUND,
NET_ERR_CONN_REFUSED,
NET_ERR_CONN_FAILED,
NET_ERR_TIMEOUT,
NET_ERR_UNKNOWN,
// Transfer errors
NET_ERR_REDIRECT_COUNT_ERR,
NET_ERR_REDIRECT_ERR,
NET_ERR_HTTP_410,
NET_ERR_HTTP_404,
NET_ERR_HTTP_NON_200,
NET_ERR_HTTP_PROTO_ERR,
NET_ERR_AUTH_FAILED,
NET_ERR_AUTH_NO_AUTHINFO,
NET_ERR_AUTH_GEN_AUTH_ERR,
NET_ERR_AUTH_UNSUPPORTED,
NET_ERR_GZIP_ERR,
NET_ERR_CHUNKED
NET_ERR_OK,
// Init errors
NET_ERR_URL_INVALID,
// Connect errors
NET_ERR_SOCK_ERR,
NET_ERR_HOST_NOT_FOUND,
NET_ERR_CONN_REFUSED,
NET_ERR_CONN_FAILED,
NET_ERR_TIMEOUT,
NET_ERR_UNKNOWN,
// Transfer errors
NET_ERR_REDIRECT_COUNT_ERR,
NET_ERR_REDIRECT_ERR,
NET_ERR_HTTP_410,
NET_ERR_HTTP_404,
NET_ERR_HTTP_NON_200,
NET_ERR_HTTP_PROTO_ERR,
NET_ERR_AUTH_FAILED,
NET_ERR_AUTH_NO_AUTHINFO,
NET_ERR_AUTH_GEN_AUTH_ERR,
NET_ERR_AUTH_UNSUPPORTED,
NET_ERR_GZIP_ERR,
NET_ERR_CHUNKED
};
struct feed {
struct newsitem* items;
struct feed* next;
struct feed* prev;
char* feedurl; // Non hashified URL
char* xmltext; // Raw XML
char* title;
char* link;
char* description;
char* lastmodified; // Content of header as sent by the server.
char* content_type;
char* cookies; // Login cookies for this feed.
char* authinfo; // HTTP authinfo string.
char* servauth; // Server supplied authorization header.
char* custom_title; // Custom feed title.
char* original; // Original feed title.
char* perfeedfilter; // Pipe feed through this program before parsing.
time_t mtime; // Last modification time
unsigned content_length;
enum netio_error netio_error; // See netio.h
int connectresult; // Socket errno
int lasthttpstatus;
bool problem; // Set if there was a problem downloading the feed.
bool execurl; // Execurl?
bool smartfeed; // 1: new items feed.
struct feedcategories* feedcategories;
struct newsitem* items;
struct feed* next;
struct feed* prev;
char* feedurl; // Non hashified URL
char* xmltext; // Raw XML
char* title;
char* link;
char* description;
char* lastmodified; // Content of header as sent by the server.
char* content_type;
char* cookies; // Login cookies for this feed.
char* authinfo; // HTTP authinfo string.
char* servauth; // Server supplied authorization header.
char* custom_title; // Custom feed title.
char* original; // Original feed title.
char* perfeedfilter; // Pipe feed through this program before parsing.
time_t mtime; // Last modification time
unsigned content_length;
enum netio_error netio_error; // See netio.h
int connectresult; // Socket errno
int lasthttpstatus;
bool problem; // Set if there was a problem downloading the feed.
bool execurl; // Execurl?
bool smartfeed; // 1: new items feed.
struct feedcategories* feedcategories;
};
struct newsitem {
struct newsdata* data;
struct newsitem* next;
struct newsitem* prev; // Pointer to next/prev item in double linked list
struct newsdata* data;
struct newsitem* next;
struct newsitem* prev; // Pointer to next/prev item in double linked list
};
struct newsdata {
struct feed* parent;
char* title;
char* link;
char* description;
char* hash;
int date;
bool readstatus;
struct feed* parent;
char* title;
char* link;
char* description;
char* hash;
int date;
bool readstatus;
};
// Global program keybindings. Loaded from setup:Config()
struct keybindings {
char next;
char prev;
char prevmenu;
char quit;
char addfeed;
char deletefeed;
char markread;
char markunread;
char markallread;
char dfltbrowser;
char moveup;
char movedown;
char feedinfo;
char reload;
char forcereload;
char reloadall;
char urljump;
char urljump2;
char changefeedname;
char sortfeeds;
char pdown;
char pup;
char categorize;
char filter;
char filtercurrent;
char nofilter;
char help;
char about;
char perfeedfilter;
char andxor;
char home;
char end;
char enter;
char newheadlines;
char typeahead;
char next;
char prev;
char prevmenu;
char quit;
char addfeed;
char deletefeed;
char markread;
char markunread;
char markallread;
char dfltbrowser;
char moveup;
char movedown;
char feedinfo;
char reload;
char forcereload;
char reloadall;
char urljump;
char urljump2;
char changefeedname;
char sortfeeds;
char pdown;
char pup;
char categorize;
char filter;
char filtercurrent;
char nofilter;
char help;
char about;
char perfeedfilter;
char andxor;
char home;
char end;
char enter;
char newheadlines;
char typeahead;
};
// Color definitions
struct color {
int8_t newitems;
int8_t urljump;
int8_t feedtitle;
bool newitemsbold;
bool urljumpbold;
bool feedtitlebold;
int8_t newitems;
int8_t urljump;
int8_t feedtitle;
bool newitemsbold;
bool urljumpbold;
bool feedtitlebold;
};
struct entity {
char* entity;
char* converted_entity;
int entity_length;
struct entity* next;
char* entity;
char* converted_entity;
int entity_length;
struct entity* next;
};
// A feeds categories
struct feedcategories {
char* name; // Category name
struct feedcategories * next;
char* name; // Category name
struct feedcategories* next;
};
// Global list of all defined categories, their refcounts and color labels.
struct categories {
struct categories* next;
char* name; // Category name
unsigned short refcount; // Number of feeds using this category.
unsigned short label; // Color label of this category.
bool labelbold;
struct categories* next;
char* name; // Category name
unsigned short refcount; // Number of feeds using this category.
unsigned short label; // Color label of this category.
bool labelbold;
};
struct settings {
struct entity* html_entities;
struct categories* global_categories;
const char* global_charset;
char* browser; // Browser command. lynx is standard.
char* useragent; // Snownews User-Agent string.
char* proxyname; // Hostname of proxyserver.
unsigned short proxyport; // Port on proxyserver to use.
struct color color;
struct keybindings keybindings;
bool monochrome;
bool cursor_always_visible;
struct entity* html_entities;
struct categories* global_categories;
const char* global_charset;
char* browser; // Browser command. lynx is standard.
char* useragent; // Snownews User-Agent string.
char* proxyname; // Hostname of proxyserver.
unsigned short proxyport; // Port on proxyserver to use.
struct color color;
struct keybindings keybindings;
bool monochrome;
bool cursor_always_visible;
};
//----------------------------------------------------------------------
// Global variables
extern struct feed* _feed_list;
extern struct feed* _unfiltered_feed_list;
extern struct settings _settings;
extern struct feed* _feed_list;
extern struct feed* _unfiltered_feed_list;
extern struct settings _settings;
extern bool _feed_list_changed;
//----------------------------------------------------------------------

54
md5.c
View File

@ -17,13 +17,18 @@
#include "md5.h"
static inline uint32_t remove_bits (uint32_t v, uint8_t f, uint8_t n)
{ return ((v>>(f+n))<<f)|(v&((1<<f)-1)); }
{
return ((v >> (f + n)) << f) | (v & ((1 << f) - 1));
}
static inline uint32_t Rol (uint32_t v, uint32_t n)
{ return (v<<n)|(v>>(32-n)); }
{
return (v << n) | (v >> (32 - n));
}
static void hash_md5_block (struct HashMD5* h)
{
static const uint8_t r[16] = { 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 };
static const uint8_t r[16] = { 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 };
//{{{ K table
static const uint32_t K[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
@ -51,26 +56,33 @@ static void hash_md5_block (struct HashMD5* h)
g = i;
} else if (i < 32) {
f = c ^ (d & (b ^ c));
g = (5*i + 1) % 16;
g = (5 * i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3*i + 5) % 16;
g = (3 * i + 5) % 16;
} else {
f = c ^ (b | ~d);
g = (7*i) % 16;
g = (7 * i) % 16;
}
f = Rol (a + f + K[i] + h->words[g], r[remove_bits(i,2,2)]);
g = d; d = c; c = b; b += f; a = g;
f = Rol (a + f + K[i] + h->words[g], r[remove_bits (i, 2, 2)]);
g = d;
d = c;
c = b;
b += f;
a = g;
}
h->hash[0] += a; h->hash[1] += b; h->hash[2] += c; h->hash[3] += d;
h->hash[0] += a;
h->hash[1] += b;
h->hash[2] += c;
h->hash[3] += d;
}
void hash_md5_init (struct HashMD5* h)
{
memset (h, 0, sizeof(*h));
static const uint32_t hash_md5_initial_value [HASH_SIZE_MD5_WORDS]
= { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
memcpy (h->hash, hash_md5_initial_value, sizeof(h->hash));
memset (h, 0, sizeof (*h));
static const uint32_t hash_md5_initial_value[HASH_SIZE_MD5_WORDS]
= { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
memcpy (h->hash, hash_md5_initial_value, sizeof (h->hash));
}
void hash_md5_data (struct HashMD5* h, const void* d, size_t n)
@ -80,8 +92,8 @@ void hash_md5_data (struct HashMD5* h, const void* d, size_t n)
uint32_t copied = HASH_BLOCK_SIZE_MD5 - blockOffset;
if (copied > n)
copied = n;
memcpy (h->bytes+blockOffset, d, copied);
d = (const char*)d + copied;
memcpy (h->bytes + blockOffset, d, copied);
d = (const char *) d + copied;
n -= copied;
h->offset += copied;
blockOffset = (blockOffset + copied) % HASH_BLOCK_SIZE_MD5;
@ -93,12 +105,12 @@ void hash_md5_data (struct HashMD5* h, const void* d, size_t n)
void hash_md5_finish (struct HashMD5* h)
{
uint64_t size = h->offset;
uint8_t pad = 0x80; // End message with one bit
uint8_t pad = 0x80; // End message with one bit
do {
hash_md5_data (h, &pad, sizeof(pad));
pad = 0; // Pad with zeroes until there is enough space for offset at the end
} while (h->offset % HASH_BLOCK_SIZE_MD5 != HASH_BLOCK_SIZE_MD5-sizeof(h->offset));
h->quads[7] = size*8; // Write size in bits at end of block
hash_md5_data (h, &pad, sizeof (pad));
pad = 0; // Pad with zeroes until there is enough space for offset at the end
} while (h->offset % HASH_BLOCK_SIZE_MD5 != HASH_BLOCK_SIZE_MD5 - sizeof (h->offset));
h->quads[7] = size * 8; // Write size in bits at end of block
hash_md5_block (h);
}
@ -106,5 +118,5 @@ void hash_md5_to_text (const struct HashMD5* h, char* text)
{
for (unsigned i = 0; i < HASH_SIZE_MD5_WORDS; ++i)
for (unsigned j = 0; j < 4; ++j)
sprintf (&text[i*8+j*2], "%02hhx", (uint8_t)(h->hash[i]>>(j*8)));
sprintf (&text[i * 8 + j * 2], "%02hhx", (uint8_t) (h->hash[i] >> (j * 8)));
}

12
md5.h
View File

@ -19,17 +19,17 @@
enum {
HASH_SIZE_MD5 = 16,
HASH_SIZE_MD5_WORDS = HASH_SIZE_MD5/sizeof(uint32_t),
HASH_SIZE_MD5_WORDS = HASH_SIZE_MD5 / sizeof (uint32_t),
HASH_BLOCK_SIZE_MD5 = 64
};
struct HashMD5 {
uint32_t hash [HASH_SIZE_MD5_WORDS];
uint64_t offset;
uint32_t hash[HASH_SIZE_MD5_WORDS];
uint64_t offset;
union {
uint8_t bytes [HASH_BLOCK_SIZE_MD5];
uint32_t words [HASH_BLOCK_SIZE_MD5/sizeof(uint32_t)];
uint64_t quads [HASH_BLOCK_SIZE_MD5/sizeof(uint64_t)];
uint8_t bytes[HASH_BLOCK_SIZE_MD5];
uint32_t words[HASH_BLOCK_SIZE_MD5 / sizeof (uint32_t)];
uint64_t quads[HASH_BLOCK_SIZE_MD5 / sizeof (uint64_t)];
};
};

View File

@ -19,160 +19,163 @@
#include "ui-support.h"
#include "digcalc.h"
static char* ConstructBasicAuth (const char* username, const char* password) {
// Create base64 authinfo.
static char* ConstructBasicAuth (const char* username, const char* password)
{
// Create base64 authinfo.
// RFC 2617. Basic HTTP authentication.
// Authorization: Basic username:password[base64 encoded]
// RFC 2617. Basic HTTP authentication.
// Authorization: Basic username:password[base64 encoded]
// Construct the cleartext authstring.
char authstring [128];
unsigned len = snprintf (authstring, sizeof(authstring), "%s:%s", username, password);
char* encoded = base64encode (authstring, len);
// Construct the cleartext authstring.
char authstring[128];
unsigned len = snprintf (authstring, sizeof (authstring), "%s:%s", username, password);
char* encoded = base64encode (authstring, len);
// "Authorization: Basic " + base64str + \r\n\0
len = 21 + strlen(encoded) + 3;
char* authinfo = malloc (len);
snprintf (authinfo, len, "Authorization: Basic %s\r\n", encoded);
free (encoded);
return authinfo;
// "Authorization: Basic " + base64str + \r\n\0
len = 21 + strlen (encoded) + 3;
char* authinfo = malloc (len);
snprintf (authinfo, len, "Authorization: Basic %s\r\n", encoded);
free (encoded);
return authinfo;
}
static char* GetRandomBytes (void) {
char raw[8];
FILE* devrandom = fopen ("/dev/random", "r");
if (devrandom) {
fread (raw, sizeof(raw), 1, devrandom);
fclose (devrandom);
} else {
for (unsigned i = 0; i < sizeof(raw); ++i)
raw[i] = rand(); // Use rand() if we don't have access to /dev/random.
}
char* randomness = calloc (sizeof(raw)*2+1, 1);
snprintf (randomness, sizeof(raw)*2+1, "%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx", raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7]);
return randomness;
static char* GetRandomBytes (void)
{
char raw[8];
FILE* devrandom = fopen ("/dev/random", "r");
if (devrandom) {
fread (raw, sizeof (raw), 1, devrandom);
fclose (devrandom);
} else {
for (unsigned i = 0; i < sizeof (raw); ++i)
raw[i] = rand(); // Use rand() if we don't have access to /dev/random.
}
char* randomness = calloc (sizeof (raw) * 2 + 1, 1);
snprintf (randomness, sizeof (raw) * 2 + 1, "%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx", raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7]);
return randomness;
}
static char* ConstructDigestAuth (const char* username, const char* password, const char* url, char* authdata) {
// Variables for the overcomplicated and annoying HTTP digest algo.
char* cnonce = GetRandomBytes();
char *realm = NULL, *qop = NULL, *nonce = NULL, *opaque = NULL;
while (1) {
char* token = strsep (&authdata, ", ");
if (token == NULL)
break;
static char* ConstructDigestAuth (const char* username, const char* password, const char* url, char* authdata)
{
// Variables for the overcomplicated and annoying HTTP digest algo.
char* cnonce = GetRandomBytes();
char* realm = NULL, *qop = NULL, *nonce = NULL, *opaque = NULL;
while (1) {
char* token = strsep (&authdata, ", ");
if (token == NULL)
break;
if (strncasecmp (token, "realm", 5) == 0) {
unsigned len = strlen(token)-8;
memmove (token, token+7, len);
token[len] = '\0';
realm = strdup (token);
} else if (strncasecmp (token, "qop", 3) == 0) {
unsigned len = strlen(token)-6;
memmove (token, token+5, len);
token[len] = '\0';
qop = strdup (token);
} else if (strncasecmp (token, "nonce", 5) == 0) {
unsigned len = strlen(token)-8;
memmove (token, token+7, len);
token[len] = '\0';
nonce = strdup (token);
} else if (strncasecmp (token, "opaque", 6) == 0) {
unsigned len = strlen(token)-9;
memmove (token, token+8, len);
token[len] = '\0';
opaque = strdup (token);
}
if (strncasecmp (token, "realm", 5) == 0) {
unsigned len = strlen (token) - 8;
memmove (token, token + 7, len);
token[len] = '\0';
realm = strdup (token);
} else if (strncasecmp (token, "qop", 3) == 0) {
unsigned len = strlen (token) - 6;
memmove (token, token + 5, len);
token[len] = '\0';
qop = strdup (token);
} else if (strncasecmp (token, "nonce", 5) == 0) {
unsigned len = strlen (token) - 8;
memmove (token, token + 7, len);
token[len] = '\0';
nonce = strdup (token);
} else if (strncasecmp (token, "opaque", 6) == 0) {
unsigned len = strlen (token) - 9;
memmove (token, token + 8, len);
token[len] = '\0';
opaque = strdup (token);
}
}
HASHHEX HA1;
DigestCalcHA1 ("md5", username, realm, password, nonce, cnonce, HA1);
static const char szNonceCount[9] = "00000001"; // Can be always 1 if we never use the same cnonce twice.
HASHHEX HA2 = "", Response;
DigestCalcResponse(HA1, nonce, szNonceCount, cnonce, "auth", "GET", url, HA2, Response);
HASHHEX HA1;
DigestCalcHA1 ("md5", username, realm, password, nonce, cnonce, HA1);
static const char szNonceCount[9] = "00000001"; // Can be always 1 if we never use the same cnonce twice.
HASHHEX HA2 = "", Response;
DigestCalcResponse (HA1, nonce, szNonceCount, cnonce, "auth", "GET", url, HA2, Response);
// Determine length of Authorize header.
//
// Authorization: Digest username="(username)", realm="(realm)",
// nonce="(nonce)", uri="(url)", algorithm=MD5, response="(Response)",
// qop=(auth), nc=(szNonceCount), cnonce="deadbeef"
//
unsigned len = 32 + strlen(username) + 10 + strlen(realm) + 10 + strlen(nonce) + 8 + strlen(url) + 28 + strlen(Response) + 16 + strlen(szNonceCount) + 10 + strlen(cnonce) + 4;
if (opaque)
len += 6 + strlen(opaque) + 4;
// Determine length of Authorize header.
//
// Authorization: Digest username="(username)", realm="(realm)",
// nonce="(nonce)", uri="(url)", algorithm=MD5, response="(Response)",
// qop=(auth), nc=(szNonceCount), cnonce="deadbeef"
//
unsigned len = 32 + strlen (username) + 10 + strlen (realm) + 10 + strlen (nonce) + 8 + strlen (url) + 28 + strlen (Response) + 16 + strlen (szNonceCount) + 10 + strlen (cnonce) + 4;
if (opaque)
len += 6 + strlen (opaque) + 4;
// Authorization header as sent to the server.
char* authinfo = malloc (len);
snprintf (authinfo, len, "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=MD5, response=\"%s\", qop=auth, nc=%s, cnonce=\"%s\"\r\n",
username, realm, nonce, url, Response, szNonceCount, cnonce);
free (realm);
free (qop);
free (nonce);
free (cnonce);
if (opaque)
sprintf (authinfo+strlen(authinfo)-strlen("\r\n"), ", opaque=\"%s\"\r\n", opaque);
free (opaque);
// Authorization header as sent to the server.
char* authinfo = malloc (len);
snprintf (authinfo, len, "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=MD5, response=\"%s\", qop=auth, nc=%s, cnonce=\"%s\"\r\n", username, realm, nonce, url, Response, szNonceCount, cnonce);
free (realm);
free (qop);
free (nonce);
free (cnonce);
if (opaque)
sprintf (authinfo + strlen (authinfo) - strlen ("\r\n"), ", opaque=\"%s\"\r\n", opaque);
free (opaque);
return authinfo;
return authinfo;
}
// Authorization: Digest username="(username)", realm="(realm)",
// nonce="(nonce)", uri="(url)", algorithm=MD5, response="(Response)",
// qop=(auth), nc=(szNonceCount), cnonce="deadbeef"
int NetSupportAuth (struct feed* cur_ptr, const char* authdata, const char* url, const char* netbuf) {
// Reset cur_ptr->authinfo.
free (cur_ptr->authinfo);
cur_ptr->authinfo = NULL;
int NetSupportAuth (struct feed* cur_ptr, const char* authdata, const char* url, const char* netbuf)
{
// Reset cur_ptr->authinfo.
free (cur_ptr->authinfo);
cur_ptr->authinfo = NULL;
// Catch invalid authdata.
if (!authdata)
return 1;
else if (!strchr (authdata, ':')) // No authinfo found in URL. This should not happen.
return 1;
// Catch invalid authdata.
if (!authdata)
return 1;
else if (!strchr (authdata, ':')) // No authinfo found in URL. This should not happen.
return 1;
// Parse username:password
char* username = strdup (authdata);
char* pwtok = username;
strsep (&pwtok, ":");
char* password = strdup (pwtok);
// Parse username:password
char* username = strdup (authdata);
char* pwtok = username;
strsep (&pwtok, ":");
char* password = strdup (pwtok);
// Extract requested auth type from webserver reply.
char* header = strdup (netbuf);
char* freeme = header;
strsep (&header, " ");
char* authtype = header;
// Extract requested auth type from webserver reply.
char* header = strdup (netbuf);
char* freeme = header;
strsep (&header, " ");
char* authtype = header;
// Catch invalid server replies. authtype should contain at least _something_.
if (!authtype) {
free (freeme);
free (username);
free (password);
return -1;
}
strsep (&header, " ");
// header now contains:
// Basic auth: realm
// Digest auth: realm + a lot of other stuff somehow needed by digest auth.
// Determine auth type the server requests.
if (strncasecmp (authtype, "Basic", 5) == 0) // Basic auth.
cur_ptr->authinfo = ConstructBasicAuth (username, password);
else if (strncasecmp (authtype, "Digest", 6) == 0) // Digest auth.
cur_ptr->authinfo = ConstructDigestAuth (username, password, url, header);
else {
// Unkown auth type.
free (freeme);
free (username);
free (password);
return -1;
}
// Catch invalid server replies. authtype should contain at least _something_.
if (!authtype) {
free (freeme);
free (username);
free (password);
free (freeme);
return -1;
}
strsep (&header, " ");
// header now contains:
// Basic auth: realm
// Digest auth: realm + a lot of other stuff somehow needed by digest auth.
if (!cur_ptr->authinfo)
return 2;
return 0;
// Determine auth type the server requests.
if (strncasecmp (authtype, "Basic", 5) == 0) // Basic auth.
cur_ptr->authinfo = ConstructBasicAuth (username, password);
else if (strncasecmp (authtype, "Digest", 6) == 0) // Digest auth.
cur_ptr->authinfo = ConstructDigestAuth (username, password, url, header);
else {
// Unkown auth type.
free (freeme);
free (username);
free (password);
return -1;
}
free (username);
free (password);
free (freeme);
if (!cur_ptr->authinfo)
return 2;
return 0;
}
// HTTP token may only contain ASCII characters.
@ -183,21 +186,23 @@ int NetSupportAuth (struct feed* cur_ptr, const char* authdata, const char* url,
// If given binary data return at once if we read beyond
// the boundary of sizeof(header).
//
int checkValidHTTPHeader (const unsigned char* header, unsigned size) {
unsigned len = strlen ((const char*) header);
if (len > size)
return -1;
for (unsigned i = 0; i < len && header[i] != ':'; ++i)
if ((header[i] < ' ' || header[i] > '~') && header[i] != '\r' && header[i] != '\n')
return -1;
return 0;
int checkValidHTTPHeader (const unsigned char* header, unsigned size)
{
unsigned len = strlen ((const char *) header);
if (len > size)
return -1;
for (unsigned i = 0; i < len && header[i] != ':'; ++i)
if ((header[i] < ' ' || header[i] > '~') && header[i] != '\r' && header[i] != '\n')
return -1;
return 0;
}
int checkValidHTTPURL (const unsigned char * url) {
if (strncasecmp ((const char*) url, "http://", 7) != 0)
return -1;
for (unsigned i = 0, len = strlen((const char*) url); i < len; ++i)
if (url[i] < ' ' || url[i] > '~')
return -1;
return 0;
int checkValidHTTPURL (const unsigned char* url)
{
if (strncasecmp ((const char *) url, "http://", 7) != 0)
return -1;
for (unsigned i = 0, len = strlen ((const char *)url); i < len; ++i)
if (url[i] < ' ' || url[i] > '~')
return -1;
return 0;
}

1343
netio.c

File diff suppressed because it is too large Load Diff

View File

@ -26,53 +26,54 @@
#ifdef SUN
char* strsep (char** str, const char* delims)
{
if (!*str) // No more tokens
return NULL;
char* token = *str;
while (**str) {
if (strchr (delims, **str)) {
*(*str)++ = '\0';
return token;
}
(*str)++;
if (!*str) // No more tokens
return NULL;
char* token = *str;
while (**str) {
if (strchr (delims, **str)) {
*(*str)++ = '\0';
return token;
}
// There is no other token
*str = NULL;
return token;
++(*str);
}
// There is no other token
*str = NULL;
return token;
}
// timegm() is not available on Solaris
time_t timegm (struct tm *t)
time_t timegm (struct tm* t)
{
time_t tl = mktime (t);
if (tl == -1) {
--t->tm_hour;
tl = mktime (t);
if (tl == -1)
return -1; // can't deal with output from strptime
tl += 3600;
}
struct tm* tg = gmtime (&tl);
tg->tm_isdst = 0;
time_t tb = mktime (tg);
if (tb == -1) {
tg->tm_hour--;
tb = mktime (tg);
if (tb == -1)
return -1; // can't deal with output from gmtime
tb += 3600;
}
return tl - (tb - tl);
time_t tl = mktime (t);
if (tl == -1) {
--t->tm_hour;
tl = mktime (t);
if (tl == -1)
return -1; // can't deal with output from strptime
tl += 3600;
}
struct tm* tg = gmtime (&tl);
tg->tm_isdst = 0;
time_t tb = mktime (tg);
if (tb == -1) {
--tg->tm_hour;
tb = mktime (tg);
if (tb == -1)
return -1; // can't deal with output from gmtime
tb += 3600;
}
return tl - (tb - tl);
}
#endif
// strcasestr stolen from: http://www.unixpapa.com/incnote/string.html
const char* s_strcasestr (const char* a, const char* b) {
const size_t lena = strlen(a), lenb = strlen(b);
char f[3];
snprintf(f, sizeof(f), "%c%c", tolower(*b), toupper(*b));
for (size_t l = strcspn(a, f); l != lena; l += strcspn(a + l + 1, f) + 1)
if (strncasecmp(a + l, b, lenb) == 0)
return a + l;
return NULL;
const char* s_strcasestr (const char* a, const char* b)
{
const size_t lena = strlen (a), lenb = strlen (b);
char f[3];
snprintf (f, sizeof (f), "%c%c", tolower (*b), toupper (*b));
for (size_t l = strcspn (a, f); l != lena; l += strcspn (a + l + 1, f) + 1)
if (strncasecmp (a + l, b, lenb) == 0)
return a + l;
return NULL;
}

View File

@ -18,7 +18,7 @@
#ifdef SUN
char* strsep (char** str, const char* delims);
time_t timegm (struct tm *t);
time_t timegm (struct tm* t);
#endif
const char* s_strcasestr (const char* a, const char* b);

777
setup.c
View File

@ -27,410 +27,396 @@
#include <fcntl.h>
// Load browser command from ~./snownews/browser.
static void SetupBrowser (const char* filename) {
char linebuf[128] = "lynx %s";
FILE* browserfile = fopen (filename, "r");
if (browserfile) {
fgets (linebuf, sizeof(linebuf), browserfile);
if (linebuf[strlen(linebuf)-1] == '\n')
linebuf[strlen(linebuf)-1] = '\0';
fclose (browserfile);
}
_settings.browser = strdup (linebuf);
static void SetupBrowser (const char* filename)
{
char linebuf[128] = "lynx %s";
FILE* browserfile = fopen (filename, "r");
if (browserfile) {
fgets (linebuf, sizeof (linebuf), browserfile);
if (linebuf[strlen (linebuf) - 1] == '\n')
linebuf[strlen (linebuf) - 1] = '\0';
fclose (browserfile);
}
_settings.browser = strdup (linebuf);
}
void SaveBrowserSetting (void)
{
char browserfilename [PATH_MAX];
snprintf (browserfilename, sizeof(browserfilename), SNOWNEWS_CONFIG_DIR "browser", getenv("HOME"));
FILE* browserfile = fopen (browserfilename, "w");
if (!browserfile)
MainQuit (_("Save settings (browser)"), strerror(errno));
fputs (_settings.browser, browserfile);
fclose (browserfile);
char browserfilename[PATH_MAX];
snprintf (browserfilename, sizeof (browserfilename), SNOWNEWS_CONFIG_DIR "browser", getenv ("HOME"));
FILE* browserfile = fopen (browserfilename, "w");
if (!browserfile)
MainQuit (_("Save settings (browser)"), strerror (errno));
fputs (_settings.browser, browserfile);
fclose (browserfile);
}
// Parse http_proxy environment variable and define proxyname and proxyport.
static void SetupProxy (void) {
// Check for proxy environment variable.
const char* proxyenv = getenv("http_proxy");
if (!proxyenv)
return;
// The pointer returned by getenv must not be altered.
// What about mentioning this in the manpage of getenv?
char* proxystring = strdup (proxyenv);
char* proxystrbase = proxystring;
static void SetupProxy (void)
{
// Check for proxy environment variable.
const char* proxyenv = getenv ("http_proxy");
if (!proxyenv)
return;
// The pointer returned by getenv must not be altered.
// What about mentioning this in the manpage of getenv?
char* proxystring = strdup (proxyenv);
char* proxystrbase = proxystring;
strsep (&proxystring, "/");
if (proxystring) {
strsep (&proxystring, "/");
if (proxystring) {
strsep (&proxystring, "/");
if (proxystring) {
_settings.proxyname = strdup (strsep (&proxystring, ":"));
if (proxystring)
_settings.proxyport = atoi (strsep (&proxystring, "/"));
}
_settings.proxyname = strdup (strsep (&proxystring, ":"));
if (proxystring)
_settings.proxyport = atoi (strsep (&proxystring, "/"));
}
free (proxystrbase);
}
free (proxystrbase);
}
// Construct the user agent string that snownews sends to the webserver.
// This includes Snownews/VERSION, OS name and language setting.
static void SetupUserAgent (void) {
// Constuct the User-Agent string of snownews. This is done here in program init,
// because we need to do it exactly once and it will never change while the program
// is running.
const char* lang = getenv("LANG");
if (!lang)
lang = "C";
// Snonews/VERSION (Linux; de_DE; (http://kiza.kcore.de/software/snownews/)
static const char url[] = "http://snownews.kcore.de/software/snownews/";
const unsigned urllen = strlen(url);
unsigned ualen = strlen("Snownews/" SNOWNEWS_VERSION) + 2 + strlen(lang) + 2 + strlen(OS)+2 + urllen + 2;
_settings.useragent = malloc(ualen);
snprintf (_settings.useragent, ualen, "Snownews/" SNOWNEWS_VERSION " (" OS "; %s; %s)", lang, url);
static void SetupUserAgent (void)
{
// Constuct the User-Agent string of snownews. This is done here in program init,
// because we need to do it exactly once and it will never change while the program
// is running.
const char* lang = getenv ("LANG");
if (!lang)
lang = "C";
// Snonews/VERSION (Linux; de_DE; (http://kiza.kcore.de/software/snownews/)
static const char url[] = "http://snownews.kcore.de/software/snownews/";
const unsigned urllen = strlen (url);
unsigned ualen = strlen ("Snownews/" SNOWNEWS_VERSION) + 2 + strlen (lang) + 2 + strlen (OS) + 2 + urllen + 2;
_settings.useragent = malloc (ualen);
snprintf (_settings.useragent, ualen, "Snownews/" SNOWNEWS_VERSION " (" OS "; %s; %s)", lang, url);
}
// Define global keybindings and load user customized bindings.
static void SetupKeybindings (const char* filename) {
FILE* configfile = fopen (filename, "r");
if (configfile) {
// Read keybindings and populate keybindings struct.
while (!feof(configfile)) {
char linebuf[128];
if (!fgets (linebuf, sizeof(linebuf), configfile))
break;
if (linebuf[0] == '#')
continue;
linebuf[strlen(linebuf)-1] = 0; // chop newline
char* value = linebuf;
strsep (&value, ":");
static void SetupKeybindings (const char* filename)
{
FILE* configfile = fopen (filename, "r");
if (configfile) {
// Read keybindings and populate keybindings struct.
while (!feof (configfile)) {
char linebuf[128];
if (!fgets (linebuf, sizeof (linebuf), configfile))
break;
if (linebuf[0] == '#')
continue;
linebuf[strlen (linebuf) - 1] = 0; // chop newline
char* value = linebuf;
strsep (&value, ":");
if (strcmp (linebuf, "next item") == 0)
_settings.keybindings.next = value[0];
else if (strcmp (linebuf, "previous item") == 0)
_settings.keybindings.prev = value[0];
else if (strcmp (linebuf, "return to previous menu") == 0)
_settings.keybindings.prevmenu = value[0];
else if (strcmp (linebuf, "quit") == 0)
_settings.keybindings.quit = value[0];
else if (strcmp (linebuf, "add feed") == 0)
_settings.keybindings.addfeed = value[0];
else if (strcmp (linebuf, "delete feed") == 0)
_settings.keybindings.deletefeed = value[0];
else if (strcmp (linebuf, "mark feed as read") == 0)
_settings.keybindings.markread = value[0];
else if (strcmp (linebuf, "mark item unread") == 0)
_settings.keybindings.markunread = value[0];
else if (strcmp (linebuf, "mark all as read") == 0)
_settings.keybindings.markallread = value[0];
else if (strcmp (linebuf, "change default browser") == 0)
_settings.keybindings.dfltbrowser = value[0];
else if (strcmp (linebuf, "sort feeds") == 0)
_settings.keybindings.sortfeeds = value[0];
else if (strcmp (linebuf, "move item up") == 0)
_settings.keybindings.moveup = value[0];
else if (strcmp (linebuf, "move item down") == 0)
_settings.keybindings.movedown = value[0];
else if (strcmp (linebuf, "show feedinfo") == 0)
_settings.keybindings.feedinfo = value[0];
else if (strcmp (linebuf, "reload feed") == 0)
_settings.keybindings.reload = value[0];
else if (strcmp (linebuf, "force reload feed") == 0)
_settings.keybindings.forcereload = value[0];
else if (strcmp (linebuf, "reload all feeds") == 0)
_settings.keybindings.reloadall = value[0];
else if (strcmp (linebuf, "open url") == 0)
_settings.keybindings.urljump = value[0];
else if (strcmp (linebuf, "open item url in overview") == 0)
_settings.keybindings.urljump2 = value[0];
else if (strcmp (linebuf, "change feedname") == 0)
_settings.keybindings.changefeedname = value[0];
else if (strcmp (linebuf, "page up") == 0)
_settings.keybindings.pup = value[0];
else if (strcmp (linebuf, "page down") == 0)
_settings.keybindings.pdown = value[0];
else if (strcmp (linebuf, "top") == 0)
_settings.keybindings.home = value[0];
else if (strcmp (linebuf, "bottom") == 0)
_settings.keybindings.end = value[0];
else if (strcmp (linebuf, "categorize feed") == 0)
_settings.keybindings.categorize = value[0];
else if (strcmp (linebuf, "apply filter") == 0)
_settings.keybindings.filter = value[0];
else if (strcmp (linebuf, "only current category") == 0)
_settings.keybindings.filtercurrent = value[0];
else if (strcmp (linebuf, "remove filter") == 0)
_settings.keybindings.nofilter = value[0];
else if (strcmp (linebuf, "per feed filter") == 0)
_settings.keybindings.perfeedfilter = value[0];
else if (strcmp (linebuf, "help menu") == 0)
_settings.keybindings.help = value[0];
else if (strcmp (linebuf, "about") == 0)
_settings.keybindings.about = value[0];
else if (strcmp (linebuf, "toggle AND/OR filtering") == 0)
_settings.keybindings.andxor = value[0];
else if (strcmp (linebuf, "enter") == 0)
_settings.keybindings.enter = value[0];
else if (strcmp (linebuf, "show new headlines") == 0)
_settings.keybindings.newheadlines = value[0];
else if (strcmp (linebuf, "type ahead find") == 0)
_settings.keybindings.typeahead = value[0];
}
// Override old default settings and make sure there is no clash.
// Default browser is now B; b moved to page up.
if (_settings.keybindings.dfltbrowser == 'b')
_settings.keybindings.dfltbrowser = 'B';
fclose (configfile);
} else {
// Write default bindings if the file is not there
configfile = fopen (filename, "w");
fputs ("# Snownews keybindings configfile\n", configfile);
fputs ("# Main menu bindings\n", configfile);
fprintf (configfile, "add feed:%c\n", _settings.keybindings.addfeed);
fprintf (configfile, "delete feed:%c\n", _settings.keybindings.deletefeed);
fprintf (configfile, "reload all feeds:%c\n", _settings.keybindings.reloadall);
fprintf (configfile, "change default browser:%c\n", _settings.keybindings.dfltbrowser);
fprintf (configfile, "move item up:%c\n", _settings.keybindings.moveup);
fprintf (configfile, "move item down:%c\n", _settings.keybindings.movedown);
fprintf (configfile, "change feedname:%c\n", _settings.keybindings.changefeedname);
fprintf (configfile, "sort feeds:%c\n", _settings.keybindings.sortfeeds);
fprintf (configfile, "categorize feed:%c\n", _settings.keybindings.categorize);
fprintf (configfile, "apply filter:%c\n", _settings.keybindings.filter);
fprintf (configfile, "only current category:%c\n", _settings.keybindings.filtercurrent);
fprintf (configfile, "mark all as read:%c\n", _settings.keybindings.markallread);
fprintf (configfile, "remove filter:%c\n", _settings.keybindings.nofilter);
fprintf (configfile, "per feed filter:%c\n", _settings.keybindings.perfeedfilter);
fprintf (configfile, "toggle AND/OR filtering:%c\n", _settings.keybindings.andxor);
fprintf (configfile, "quit:%c\n", _settings.keybindings.quit);
fputs ("# Feed display menu bindings\n", configfile);
fprintf (configfile, "show feedinfo:%c\n", _settings.keybindings.feedinfo);
fprintf (configfile, "mark feed as read:%c\n", _settings.keybindings.markread);
fprintf (configfile, "mark item unread:%c\n", _settings.keybindings.markunread);
fputs ("# General keybindungs\n", configfile);
fprintf (configfile, "next item:%c\n", _settings.keybindings.next);
fprintf (configfile, "previous item:%c\n", _settings.keybindings.prev);
fprintf (configfile, "return to previous menu:%c\n", _settings.keybindings.prevmenu);
fprintf (configfile, "reload feed:%c\n", _settings.keybindings.reload);
fprintf (configfile, "force reload feed:%c\n", _settings.keybindings.forcereload);
fprintf (configfile, "open url:%c\n", _settings.keybindings.urljump);
fprintf (configfile, "open item url in overview:%c\n", _settings.keybindings.urljump2);
fprintf (configfile, "page up:%c\n", _settings.keybindings.pup);
fprintf (configfile, "page down:%c\n", _settings.keybindings.pdown);
fprintf (configfile, "top:%c\n", _settings.keybindings.home);
fprintf (configfile, "bottom:%c\n", _settings.keybindings.end);
fprintf (configfile, "enter:%c\n", _settings.keybindings.enter);
fprintf (configfile, "show new headlines:%c\n", _settings.keybindings.newheadlines);
fprintf (configfile, "help menu:%c\n", _settings.keybindings.help);
fprintf (configfile, "about:%c\n", _settings.keybindings.about);
fprintf (configfile, "type ahead find:%c\n", _settings.keybindings.typeahead);
fclose (configfile);
if (strcmp (linebuf, "next item") == 0)
_settings.keybindings.next = value[0];
else if (strcmp (linebuf, "previous item") == 0)
_settings.keybindings.prev = value[0];
else if (strcmp (linebuf, "return to previous menu") == 0)
_settings.keybindings.prevmenu = value[0];
else if (strcmp (linebuf, "quit") == 0)
_settings.keybindings.quit = value[0];
else if (strcmp (linebuf, "add feed") == 0)
_settings.keybindings.addfeed = value[0];
else if (strcmp (linebuf, "delete feed") == 0)
_settings.keybindings.deletefeed = value[0];
else if (strcmp (linebuf, "mark feed as read") == 0)
_settings.keybindings.markread = value[0];
else if (strcmp (linebuf, "mark item unread") == 0)
_settings.keybindings.markunread = value[0];
else if (strcmp (linebuf, "mark all as read") == 0)
_settings.keybindings.markallread = value[0];
else if (strcmp (linebuf, "change default browser") == 0)
_settings.keybindings.dfltbrowser = value[0];
else if (strcmp (linebuf, "sort feeds") == 0)
_settings.keybindings.sortfeeds = value[0];
else if (strcmp (linebuf, "move item up") == 0)
_settings.keybindings.moveup = value[0];
else if (strcmp (linebuf, "move item down") == 0)
_settings.keybindings.movedown = value[0];
else if (strcmp (linebuf, "show feedinfo") == 0)
_settings.keybindings.feedinfo = value[0];
else if (strcmp (linebuf, "reload feed") == 0)
_settings.keybindings.reload = value[0];
else if (strcmp (linebuf, "force reload feed") == 0)
_settings.keybindings.forcereload = value[0];
else if (strcmp (linebuf, "reload all feeds") == 0)
_settings.keybindings.reloadall = value[0];
else if (strcmp (linebuf, "open url") == 0)
_settings.keybindings.urljump = value[0];
else if (strcmp (linebuf, "open item url in overview") == 0)
_settings.keybindings.urljump2 = value[0];
else if (strcmp (linebuf, "change feedname") == 0)
_settings.keybindings.changefeedname = value[0];
else if (strcmp (linebuf, "page up") == 0)
_settings.keybindings.pup = value[0];
else if (strcmp (linebuf, "page down") == 0)
_settings.keybindings.pdown = value[0];
else if (strcmp (linebuf, "top") == 0)
_settings.keybindings.home = value[0];
else if (strcmp (linebuf, "bottom") == 0)
_settings.keybindings.end = value[0];
else if (strcmp (linebuf, "categorize feed") == 0)
_settings.keybindings.categorize = value[0];
else if (strcmp (linebuf, "apply filter") == 0)
_settings.keybindings.filter = value[0];
else if (strcmp (linebuf, "only current category") == 0)
_settings.keybindings.filtercurrent = value[0];
else if (strcmp (linebuf, "remove filter") == 0)
_settings.keybindings.nofilter = value[0];
else if (strcmp (linebuf, "per feed filter") == 0)
_settings.keybindings.perfeedfilter = value[0];
else if (strcmp (linebuf, "help menu") == 0)
_settings.keybindings.help = value[0];
else if (strcmp (linebuf, "about") == 0)
_settings.keybindings.about = value[0];
else if (strcmp (linebuf, "toggle AND/OR filtering") == 0)
_settings.keybindings.andxor = value[0];
else if (strcmp (linebuf, "enter") == 0)
_settings.keybindings.enter = value[0];
else if (strcmp (linebuf, "show new headlines") == 0)
_settings.keybindings.newheadlines = value[0];
else if (strcmp (linebuf, "type ahead find") == 0)
_settings.keybindings.typeahead = value[0];
}
// Override old default settings and make sure there is no clash.
// Default browser is now B; b moved to page up.
if (_settings.keybindings.dfltbrowser == 'b')
_settings.keybindings.dfltbrowser = 'B';
fclose (configfile);
} else {
// Write default bindings if the file is not there
configfile = fopen (filename, "w");
fputs ("# Snownews keybindings configfile\n", configfile);
fputs ("# Main menu bindings\n", configfile);
fprintf (configfile, "add feed:%c\n", _settings.keybindings.addfeed);
fprintf (configfile, "delete feed:%c\n", _settings.keybindings.deletefeed);
fprintf (configfile, "reload all feeds:%c\n", _settings.keybindings.reloadall);
fprintf (configfile, "change default browser:%c\n", _settings.keybindings.dfltbrowser);
fprintf (configfile, "move item up:%c\n", _settings.keybindings.moveup);
fprintf (configfile, "move item down:%c\n", _settings.keybindings.movedown);
fprintf (configfile, "change feedname:%c\n", _settings.keybindings.changefeedname);
fprintf (configfile, "sort feeds:%c\n", _settings.keybindings.sortfeeds);
fprintf (configfile, "categorize feed:%c\n", _settings.keybindings.categorize);
fprintf (configfile, "apply filter:%c\n", _settings.keybindings.filter);
fprintf (configfile, "only current category:%c\n", _settings.keybindings.filtercurrent);
fprintf (configfile, "mark all as read:%c\n", _settings.keybindings.markallread);
fprintf (configfile, "remove filter:%c\n", _settings.keybindings.nofilter);
fprintf (configfile, "per feed filter:%c\n", _settings.keybindings.perfeedfilter);
fprintf (configfile, "toggle AND/OR filtering:%c\n", _settings.keybindings.andxor);
fprintf (configfile, "quit:%c\n", _settings.keybindings.quit);
fputs ("# Feed display menu bindings\n", configfile);
fprintf (configfile, "show feedinfo:%c\n", _settings.keybindings.feedinfo);
fprintf (configfile, "mark feed as read:%c\n", _settings.keybindings.markread);
fprintf (configfile, "mark item unread:%c\n", _settings.keybindings.markunread);
fputs ("# General keybindungs\n", configfile);
fprintf (configfile, "next item:%c\n", _settings.keybindings.next);
fprintf (configfile, "previous item:%c\n", _settings.keybindings.prev);
fprintf (configfile, "return to previous menu:%c\n", _settings.keybindings.prevmenu);
fprintf (configfile, "reload feed:%c\n", _settings.keybindings.reload);
fprintf (configfile, "force reload feed:%c\n", _settings.keybindings.forcereload);
fprintf (configfile, "open url:%c\n", _settings.keybindings.urljump);
fprintf (configfile, "open item url in overview:%c\n", _settings.keybindings.urljump2);
fprintf (configfile, "page up:%c\n", _settings.keybindings.pup);
fprintf (configfile, "page down:%c\n", _settings.keybindings.pdown);
fprintf (configfile, "top:%c\n", _settings.keybindings.home);
fprintf (configfile, "bottom:%c\n", _settings.keybindings.end);
fprintf (configfile, "enter:%c\n", _settings.keybindings.enter);
fprintf (configfile, "show new headlines:%c\n", _settings.keybindings.newheadlines);
fprintf (configfile, "help menu:%c\n", _settings.keybindings.help);
fprintf (configfile, "about:%c\n", _settings.keybindings.about);
fprintf (configfile, "type ahead find:%c\n", _settings.keybindings.typeahead);
fclose (configfile);
}
}
// Set up colors and load user customized colors.
static void SetupColors (const char* filename) {
// If there is a config file, read it.
FILE* configfile = fopen (filename, "r");
if (configfile) {
while (!feof(configfile)) {
char linebuf[128];
if (!fgets (linebuf, sizeof(linebuf), configfile))
break;
if (linebuf[0] == '#')
continue;
linebuf[strlen(linebuf)-1] = 0; // chop newline
char* value = linebuf;
strsep (&value, ":");
unsigned cval = atoi(value);
bool cvalbold = (cval > 7);
if (strcmp (linebuf, "enabled") == 0)
_settings.monochrome = !cval;
else if (strcmp (linebuf, "new item") == 0) {
_settings.color.newitems = cval;
_settings.color.newitemsbold = cvalbold;
} else if (strcmp (linebuf, "goto url") == 0) {
_settings.color.urljump = cval;
_settings.color.urljumpbold = cvalbold;
} else if (strcmp (linebuf, "feedtitle") == 0) {
_settings.color.feedtitle = cval;
_settings.color.feedtitlebold = cvalbold;
}
}
fclose (configfile);
} else {
// Write the file. This will write all setting with their
// values read before and new ones with the defaults.
configfile = fopen (filename, "w");
fputs ("# Snownews color definitons\n", configfile);
fputs ("# black:0\n", configfile);
fputs ("# red:1\n", configfile);
fputs ("# green:2\n", configfile);
fputs ("# orange:3\n", configfile);
fputs ("# blue:4\n", configfile);
fputs ("# magenta(tm):5\n", configfile);
fputs ("# cyan:6\n", configfile);
fputs ("# gray:7\n", configfile);
fputs ("# brightred:9\n", configfile);
fputs ("# brightgreen:10\n", configfile);
fputs ("# yellow:11\n", configfile);
fputs ("# brightblue:12\n", configfile);
fputs ("# brightmagenta:13\n", configfile);
fputs ("# brightcyan:14\n", configfile);
fputs ("# white:15\n", configfile);
fputs ("# no color:-1\n", configfile);
fprintf (configfile, "enabled:%d\n", !_settings.monochrome);
fprintf (configfile, "new item:%d\n", _settings.color.newitems);
fprintf (configfile, "goto url:%d\n", _settings.color.urljump);
fprintf (configfile, "feedtitle:%d\n", _settings.color.feedtitle);
fclose (configfile);
static void SetupColors (const char* filename)
{
// If there is a config file, read it.
FILE* configfile = fopen (filename, "r");
if (configfile) {
while (!feof (configfile)) {
char linebuf[128];
if (!fgets (linebuf, sizeof (linebuf), configfile))
break;
if (linebuf[0] == '#')
continue;
linebuf[strlen (linebuf) - 1] = 0; // chop newline
char* value = linebuf;
strsep (&value, ":");
unsigned cval = atoi (value);
bool cvalbold = (cval > 7);
if (strcmp (linebuf, "enabled") == 0)
_settings.monochrome = !cval;
else if (strcmp (linebuf, "new item") == 0) {
_settings.color.newitems = cval;
_settings.color.newitemsbold = cvalbold;
} else if (strcmp (linebuf, "goto url") == 0) {
_settings.color.urljump = cval;
_settings.color.urljumpbold = cvalbold;
} else if (strcmp (linebuf, "feedtitle") == 0) {
_settings.color.feedtitle = cval;
_settings.color.feedtitlebold = cvalbold;
}
}
if (!_settings.monochrome) {
start_color();
fclose (configfile);
} else {
// Write the file. This will write all setting with their
// values read before and new ones with the defaults.
configfile = fopen (filename, "w");
fputs ("# Snownews color definitons\n", configfile);
fputs ("# black:0\n", configfile);
fputs ("# red:1\n", configfile);
fputs ("# green:2\n", configfile);
fputs ("# orange:3\n", configfile);
fputs ("# blue:4\n", configfile);
fputs ("# magenta(tm):5\n", configfile);
fputs ("# cyan:6\n", configfile);
fputs ("# gray:7\n", configfile);
fputs ("# brightred:9\n", configfile);
fputs ("# brightgreen:10\n", configfile);
fputs ("# yellow:11\n", configfile);
fputs ("# brightblue:12\n", configfile);
fputs ("# brightmagenta:13\n", configfile);
fputs ("# brightcyan:14\n", configfile);
fputs ("# white:15\n", configfile);
fputs ("# no color:-1\n", configfile);
fprintf (configfile, "enabled:%d\n", !_settings.monochrome);
fprintf (configfile, "new item:%d\n", _settings.color.newitems);
fprintf (configfile, "goto url:%d\n", _settings.color.urljump);
fprintf (configfile, "feedtitle:%d\n", _settings.color.feedtitle);
fclose (configfile);
}
if (!_settings.monochrome) {
start_color();
// The following call will automagically implement -1 as the terminal's
// default color for fg/bg in init_pair.
use_default_colors();
// The following call will automagically implement -1 as the terminal's
// default color for fg/bg in init_pair.
use_default_colors();
// Internally used color pairs.
// Use with WA_BOLD to get bright color (orange->yellow, gray->white, etc).
init_pair (10, 1, -1); // red
init_pair (11, 2, -1); // green
init_pair (12, 3, -1); // orange
init_pair (13, 4, -1); // blue
init_pair (14, 5, -1); // magenta
init_pair (15, 6, -1); // cyan
init_pair (16, 7, -1); // gray
// Internally used color pairs.
// Use with WA_BOLD to get bright color (orange->yellow, gray->white, etc).
init_pair (10, 1, -1); // red
init_pair (11, 2, -1); // green
init_pair (12, 3, -1); // orange
init_pair (13, 4, -1); // blue
init_pair (14, 5, -1); // magenta
init_pair (15, 6, -1); // cyan
init_pair (16, 7, -1); // gray
// Default terminal color color pair
init_pair (63, -1, -1);
// Default terminal color color pair
init_pair (63, -1, -1);
// Initialize all color pairs we're gonna use.
init_pair (2, _settings.color.newitems-8*_settings.color.newitemsbold, -1); // New item.
init_pair (3, _settings.color.urljump-8*_settings.color.urljumpbold, -1); // Goto url line
init_pair (4, _settings.color.feedtitle-8*_settings.color.feedtitlebold, -1); // Feed title header
}
// Initialize all color pairs we're gonna use.
init_pair (2, _settings.color.newitems - 8 * _settings.color.newitemsbold, -1); // New item.
init_pair (3, _settings.color.urljump - 8 * _settings.color.urljumpbold, -1); // Goto url line
init_pair (4, _settings.color.feedtitle - 8 * _settings.color.feedtitlebold, -1); // Feed title header
}
}
// Load user customized entity conversion table.
static void SetupEntities (const char * file) {
FILE* configfile = fopen (file, "r");
if (!configfile) {
configfile = fopen (file, "w");
enum { // Non-ascii character codes that should not be in source code
Umlauta = 0xe4, Umlauto = 0xf6, Umlautu = 0xfc,
UmlautA = 0xc4, UmlautO = 0xd6, UmlautU = 0xdc,
GermanB = 0xdf
};
fprintf (configfile, "# HTML entity conversion table\n"
"#\n"
"# Put all entities you want to have converted into this file.\n"
"# File format: &entity;[converted string/char]\n"
"# Example: &mdash;--\n"
"#\n"
"# XML defined entities are converted by default. These are:\n"
"# &amp;, &lt;, &gt;, &apos;, &quot;\n"
"#\n"
"&auml;%c\n"
"&ouml;%c\n"
"&uuml;%c\n"
"&Auml;%c\n"
"&Ouml;%c\n"
"&Uuml;%c\n"
"&szlig;%c\n"
"&nbsp; \n"
"&mdash;--\n"
"&hellip;...\n"
"&#8220;\"\n"
"&#8221;\"\n"
"&raquo;\"\n"
"&laquo;\"\n",
Umlauta, Umlauto, Umlautu, UmlautA, UmlautO, UmlautU, GermanB);
fclose (configfile);
configfile = fopen (file, "r");
}
while (!feof(configfile)) {
char linebuf [128];
if (!fgets (linebuf, sizeof(linebuf), configfile))
break;
if (linebuf[0] != '&')
continue; // Entities must start with '&'
linebuf[strlen(linebuf)-1] = 0; // chop newline
char* parse = &linebuf[1]; // Go past the '&'
const char* ename = strsep (&parse, ";");
const char* evalue = parse;
struct entity* entity = calloc (1, sizeof (struct entity));
entity->entity = strdup (ename);
entity->converted_entity = strdup (evalue);
entity->entity_length = strlen (entity->converted_entity);
// Add entity to linked list structure.
if (!_settings.html_entities)
_settings.html_entities = entity;
else {
entity->next = _settings.html_entities;
_settings.html_entities = entity;
}
static void SetupEntities (const char* file)
{
FILE* configfile = fopen (file, "r");
if (!configfile) {
configfile = fopen (file, "w");
enum { // Non-ascii character codes that should not be in source code
Umlauta = 0xe4, Umlauto = 0xf6, Umlautu = 0xfc,
UmlautA = 0xc4, UmlautO = 0xd6, UmlautU = 0xdc,
GermanB = 0xdf
};
fprintf (configfile,
"# HTML entity conversion table\n" "#\n" "# Put all entities you want to have converted into this file.\n" "# File format: &entity;[converted string/char]\n" "# Example: &mdash;--\n" "#\n"
"# XML defined entities are converted by default. These are:\n" "# &amp;, &lt;, &gt;, &apos;, &quot;\n" "#\n" "&auml;%c\n" "&ouml;%c\n" "&uuml;%c\n" "&Auml;%c\n" "&Ouml;%c\n" "&Uuml;%c\n" "&szlig;%c\n" "&nbsp; \n" "&mdash;--\n"
"&hellip;...\n" "&#8220;\"\n" "&#8221;\"\n" "&raquo;\"\n" "&laquo;\"\n", Umlauta, Umlauto, Umlautu, UmlautA, UmlautO, UmlautU, GermanB);
fclose (configfile);
configfile = fopen (file, "r");
}
while (!feof (configfile)) {
char linebuf[128];
if (!fgets (linebuf, sizeof (linebuf), configfile))
break;
if (linebuf[0] != '&')
continue; // Entities must start with '&'
linebuf[strlen (linebuf) - 1] = 0; // chop newline
char* parse = &linebuf[1]; // Go past the '&'
const char* ename = strsep (&parse, ";");
const char* evalue = parse;
struct entity* entity = calloc (1, sizeof (struct entity));
entity->entity = strdup (ename);
entity->converted_entity = strdup (evalue);
entity->entity_length = strlen (entity->converted_entity);
// Add entity to linked list structure.
if (!_settings.html_entities)
_settings.html_entities = entity;
else {
entity->next = _settings.html_entities;
_settings.html_entities = entity;
}
}
}
static unsigned SetupFeedList (const char* filename)
{
unsigned numfeeds = 0;
FILE* configfile = fopen (filename, "r");
if (!configfile)
return numfeeds; // no feeds
while (!feof(configfile)) {
char linebuf [256];
if (!fgets (linebuf, sizeof(linebuf), configfile))
break;
if (linebuf[0] == '\n')
break;
linebuf[strlen(linebuf)-1] = 0; // chop newline
unsigned numfeeds = 0;
FILE* configfile = fopen (filename, "r");
if (!configfile)
return numfeeds; // no feeds
while (!feof (configfile)) {
char linebuf[256];
if (!fgets (linebuf, sizeof (linebuf), configfile))
break;
if (linebuf[0] == '\n')
break;
linebuf[strlen (linebuf) - 1] = 0; // chop newline
// File format is url|custom name|comma seperated categories|filters
char* parse = linebuf;
const char* url = strsep (&parse, "|");
if (!url || !url[0])
continue; // no url
const char* cname = strsep (&parse, "|");
char* categories = strsep (&parse, "|");
const char* filters = parse;
// File format is url|custom name|comma seperated categories|filters
char* parse = linebuf;
const char* url = strsep (&parse, "|");
if (!url || !url[0])
continue; // no url
const char* cname = strsep (&parse, "|");
char* categories = strsep (&parse, "|");
const char* filters = parse;
++numfeeds;
++numfeeds;
struct feed* new_ptr = newFeedStruct();
new_ptr->feedurl = strdup (url);
if (strncasecmp (new_ptr->feedurl, "exec:", strlen("exec:")) == 0)
new_ptr->execurl = true;
else if (strncasecmp (new_ptr->feedurl, "smartfeed:", strlen("smartfeed:")) == 0)
new_ptr->smartfeed = true;
if (cname && cname[0])
new_ptr->custom_title = strdup (cname);
if (filters && filters[0])
new_ptr->perfeedfilter = strdup (filters);
if (categories && categories[0]) // Put categories into cat struct.
for (char *catnext = categories, *catname; (catname = strsep (&catnext, ","));)
FeedCategoryAdd (new_ptr, catname);
struct feed* new_ptr = newFeedStruct();
new_ptr->feedurl = strdup (url);
if (strncasecmp (new_ptr->feedurl, "exec:", strlen ("exec:")) == 0)
new_ptr->execurl = true;
else if (strncasecmp (new_ptr->feedurl, "smartfeed:", strlen ("smartfeed:")) == 0)
new_ptr->smartfeed = true;
if (cname && cname[0])
new_ptr->custom_title = strdup (cname);
if (filters && filters[0])
new_ptr->perfeedfilter = strdup (filters);
if (categories && categories[0]) // Put categories into cat struct.
for (char *catnext = categories, *catname; (catname = strsep (&catnext, ","));)
FeedCategoryAdd (new_ptr, catname);
// Load cookies for this feed.
// But skip loading cookies for execurls.
if (new_ptr->execurl != 1)
LoadCookies (new_ptr);
// Load cookies for this feed.
// But skip loading cookies for execurls.
if (new_ptr->execurl != 1)
LoadCookies (new_ptr);
// Add to bottom of pointer chain.
if (!_feed_list)
_feed_list = new_ptr;
else {
new_ptr->prev = _feed_list;
while (new_ptr->prev->next)
new_ptr->prev = new_ptr->prev->next;
new_ptr->prev->next = new_ptr;
}
// Add to bottom of pointer chain.
if (!_feed_list)
_feed_list = new_ptr;
else {
new_ptr->prev = _feed_list;
while (new_ptr->prev->next)
new_ptr->prev = new_ptr->prev->next;
new_ptr->prev->next = new_ptr;
}
fclose (configfile);
return numfeeds;
}
fclose (configfile);
return numfeeds;
}
// Create snownews' config directories if they do not exist yet,
@ -438,47 +424,48 @@ static unsigned SetupFeedList (const char* filename)
//
// Returns number of feeds successfully loaded.
//
unsigned Config (void) {
// Set umask to 077 for all created files.
umask (0077);
unsigned Config (void)
{
// Set umask to 077 for all created files.
umask (0077);
SetupProxy();
SetupUserAgent();
SetupProxy();
SetupUserAgent();
// Setup config directories.
char dirname [PATH_MAX];
snprintf (dirname, sizeof(dirname), SNOWNEWS_CONFIG_DIR, getenv("HOME"));
struct stat dirtest;
if (0 > stat (dirname, &dirtest)) {
if (0 > mkdir (dirname, S_IRWXU| S_IRGRP| S_IXGRP| S_IROTH| S_IXOTH))
MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, strerror(errno));
} else if (!S_ISDIR(dirtest.st_mode))
MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, "A file with the name \"" SNOWNEWS_CONFIG_DIR "\" exists!");
// Setup config directories.
char dirname[PATH_MAX];
snprintf (dirname, sizeof (dirname), SNOWNEWS_CONFIG_DIR, getenv ("HOME"));
struct stat dirtest;
if (0 > stat (dirname, &dirtest)) {
if (0 > mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH))
MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, strerror (errno));
} else if (!S_ISDIR (dirtest.st_mode))
MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, "A file with the name \"" SNOWNEWS_CONFIG_DIR "\" exists!");
snprintf (dirname, sizeof(dirname), SNOWNEWS_CACHE_DIR, getenv("HOME"));
if (0 > stat (dirname, &dirtest)) {
if (0 > mkdir (dirname, S_IRWXU| S_IRGRP| S_IXGRP| S_IROTH| S_IXOTH))
MainQuit (_("Creating config directory " SNOWNEWS_CACHE_DIR), strerror(errno));
} else if (!S_ISDIR(dirtest.st_mode))
MainQuit ("Creating config directory " SNOWNEWS_CACHE_DIR, "A file with the name \"" SNOWNEWS_CACHE_DIR "\" exists!");
snprintf (dirname, sizeof (dirname), SNOWNEWS_CACHE_DIR, getenv ("HOME"));
if (0 > stat (dirname, &dirtest)) {
if (0 > mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH))
MainQuit (_("Creating config directory " SNOWNEWS_CACHE_DIR), strerror (errno));
} else if (!S_ISDIR (dirtest.st_mode))
MainQuit ("Creating config directory " SNOWNEWS_CACHE_DIR, "A file with the name \"" SNOWNEWS_CACHE_DIR "\" exists!");
UIStatus (_("Reading configuration settings..."), 0, 0);
UIStatus (_("Reading configuration settings..."), 0, 0);
char filename [PATH_MAX];
snprintf (filename, sizeof(filename), SNOWNEWS_CONFIG_DIR "browser", getenv("HOME"));
SetupBrowser (filename);
char filename[PATH_MAX];
snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "browser", getenv ("HOME"));
SetupBrowser (filename);
snprintf (filename, sizeof(filename), SNOWNEWS_CONFIG_DIR "urls", getenv("HOME"));
unsigned numfeeds = SetupFeedList (filename);
snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "urls", getenv ("HOME"));
unsigned numfeeds = SetupFeedList (filename);
snprintf (filename, sizeof(filename), SNOWNEWS_CONFIG_DIR "keybindings", getenv("HOME"));
SetupKeybindings (filename);
snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "keybindings", getenv ("HOME"));
SetupKeybindings (filename);
snprintf (filename, sizeof(filename), SNOWNEWS_CONFIG_DIR "colors", getenv("HOME"));
SetupColors (filename);
snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "colors", getenv ("HOME"));
SetupColors (filename);
snprintf (filename, sizeof(filename), SNOWNEWS_CONFIG_DIR "html_entities", getenv("HOME"));
SetupEntities (filename);
snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "html_entities", getenv ("HOME"));
SetupEntities (filename);
return numfeeds;
return numfeeds;
}

View File

@ -19,205 +19,218 @@
#include <unistd.h>
// Init the ncurses library.
void InitCurses (void) {
initscr();
keypad (stdscr, TRUE); // Activate keypad so we can read function keys with getch.
cbreak(); // No buffering.
noecho(); // Do not echo typed chars onto the screen.
ESCDELAY = 10; // Only wait 10ms after ESC
clear();
curs_set (_settings.cursor_always_visible);
void InitCurses (void)
{
initscr();
keypad (stdscr, TRUE); // Activate keypad so we can read function keys with getch.
cbreak(); // No buffering.
noecho(); // Do not echo typed chars onto the screen.
ESCDELAY = 10; // Only wait 10ms after ESC
clear();
curs_set (_settings.cursor_always_visible);
}
// Print text in statusbar.
void UIStatus (const char* text, int delay, int warning) {
int attr = WA_REVERSE;
if (warning)
attr |= COLOR_PAIR(10);
attron (attr);
clearLine (LINES-1, INVERSE);
attron (WA_REVERSE);
mvaddnstr (LINES-1, 1, text, COLS-2);
attroff (attr);
refresh();
if (delay)
sleep (delay);
void UIStatus (const char* text, int delay, int warning)
{
int attr = WA_REVERSE;
if (warning)
attr |= COLOR_PAIR (10);
attron (attr);
clearLine (LINES - 1, INVERSE);
attron (WA_REVERSE);
mvaddnstr (LINES - 1, 1, text, COLS - 2);
attroff (attr);
refresh();
if (delay)
sleep (delay);
}
// Swap all pointers inside a feed struct except next and prev
void SwapPointers (struct feed* one, struct feed* two) {
struct feed *two_next = two->next, *two_prev = two->prev;
struct feed tmp = *one;
*one = *two;
*two = tmp;
one->next = two->next;
one->prev = two->prev;
two->next = two_next;
two->prev = two_prev;
void SwapPointers (struct feed* one, struct feed* two)
{
struct feed* two_next = two->next, *two_prev = two->prev;
struct feed tmp = *one;
*one = *two;
*two = tmp;
one->next = two->next;
one->prev = two->prev;
two->next = two_next;
two->prev = two_prev;
}
// Ignore "A", "The", etc. prefixes when sorting feeds.
const char* SnowSortIgnore (const char* title) {
if (strncasecmp (title, "a ", strlen("a ")) == 0)
return title+strlen("a ");
else if (strncasecmp (title, "the ", strlen("the ")) == 0)
return title+strlen("the ");
return title;
const char* SnowSortIgnore (const char* title)
{
if (strncasecmp (title, "a ", strlen ("a ")) == 0)
return title + strlen ("a ");
else if (strncasecmp (title, "the ", strlen ("the ")) == 0)
return title + strlen ("the ");
return title;
}
// Sort the struct list alphabetically.
// Sorting criteria is struct feed->title
void SnowSort (void) {
// If there is no element do not run sort or it'll crash!
if (!_feed_list)
return;
UIStatus(_("Sorting, please wait..."), 0, 0);
void SnowSort (void)
{
// If there is no element do not run sort or it'll crash!
if (!_feed_list)
return;
UIStatus (_("Sorting, please wait..."), 0, 0);
unsigned elements = 0;
for (const struct feed* f = _feed_list; f; f = f->next)
elements++;
for (unsigned i = 0; i <= elements; ++i) {
for (struct feed* f = _feed_list; f->next; f = f->next) {
const char* one = SnowSortIgnore (f->title);
const char* two = SnowSortIgnore (f->next->title);
if (strcasecmp (one, two) > 0)
SwapPointers (f, f->next);
}
unsigned elements = 0;
for (const struct feed * f = _feed_list; f; f = f->next)
++elements;
for (unsigned i = 0; i <= elements; ++i) {
for (struct feed * f = _feed_list; f->next; f = f->next) {
const char* one = SnowSortIgnore (f->title);
const char* two = SnowSortIgnore (f->next->title);
if (strcasecmp (one, two) > 0)
SwapPointers (f, f->next);
}
}
}
// Draw a filled box with WA_REVERSE at coordinates x1y1/x2y2
void UISupportDrawBox (int x1, int y1, int x2, int y2) {
attron (WA_REVERSE);
for (int y = y1; y <= y2; ++y) {
for (int x = x1; x <= x2; ++x) {
int c = ' ';
if (y == y1) {
if (x == x1)
c = ACS_ULCORNER;
else if (x == x2)
c = ACS_URCORNER;
else
c = ACS_HLINE;
} else if (y == y2) {
if (x == x1)
c = ACS_LLCORNER;
else if (x == x2)
c = ACS_LRCORNER;
else
c = ACS_HLINE;
} else if (x == x1 || x == x2)
c = ACS_VLINE;
mvaddch (y, x, c);
}
void UISupportDrawBox (int x1, int y1, int x2, int y2)
{
attron (WA_REVERSE);
for (int y = y1; y <= y2; ++y) {
for (int x = x1; x <= x2; ++x) {
int c = ' ';
if (y == y1) {
if (x == x1)
c = ACS_ULCORNER;
else if (x == x2)
c = ACS_URCORNER;
else
c = ACS_HLINE;
} else if (y == y2) {
if (x == x1)
c = ACS_LLCORNER;
else if (x == x2)
c = ACS_LRCORNER;
else
c = ACS_HLINE;
} else if (x == x1 || x == x2)
c = ACS_VLINE;
mvaddch (y, x, c);
}
attroff (WA_REVERSE);
}
attroff (WA_REVERSE);
}
// Draw main program header.
void UISupportDrawHeader (const char* headerstring) {
clearLine (0, INVERSE);
attron (WA_REVERSE);
mvprintw (0, 1, "* Snownews %s %s", SNOWNEWS_VERSTRING, headerstring ? headerstring : "");
attroff (WA_REVERSE);
void UISupportDrawHeader (const char* headerstring)
{
clearLine (0, INVERSE);
attron (WA_REVERSE);
mvprintw (0, 1, "* Snownews %s %s", SNOWNEWS_VERSTRING, headerstring ? headerstring : "");
attroff (WA_REVERSE);
}
// Take a URL and execute in default browser.
// Apply all security restrictions before running system()!
void UISupportURLJump (const char* url) {
if (!url)
return; // Should not happen. Nope, really should not happen.
if (strncmp(url, "smartfeed", strlen("smartfeed")) == 0)
return; // Smartfeeds cannot be opened (for now).
if (strchr (_settings.browser, '\'')) {
UIStatus (_("Unsafe browser string (contains quotes)! See snownews.kcore.de/faq#toc2.4"), 5, 1);
return; // Complain loudly if browser string contains a single quote.
}
if (strchr (url, '\'')) {
UIStatus (_("Invalid URL passed. Possible shell exploit attempted!"), 3, 1);
return;
}
char escapedurl [PATH_MAX];
snprintf (escapedurl, sizeof(escapedurl), "'%s' 2>/dev/null", url);
char browcall [PATH_MAX];
snprintf (browcall, sizeof(browcall), _settings.browser, escapedurl);
void UISupportURLJump (const char* url)
{
if (!url)
return; // Should not happen. Nope, really should not happen.
if (strncmp (url, "smartfeed", strlen ("smartfeed")) == 0)
return; // Smartfeeds cannot be opened (for now).
if (strchr (_settings.browser, '\'')) {
UIStatus (_("Unsafe browser string (contains quotes)! See snownews.kcore.de/faq#toc2.4"), 5, 1);
return; // Complain loudly if browser string contains a single quote.
}
if (strchr (url, '\'')) {
UIStatus (_("Invalid URL passed. Possible shell exploit attempted!"), 3, 1);
return;
}
char escapedurl[PATH_MAX];
snprintf (escapedurl, sizeof (escapedurl), "'%s' 2>/dev/null", url);
char browcall[PATH_MAX];
snprintf (browcall, sizeof (browcall), _settings.browser, escapedurl);
char execmsg [sizeof(browcall)+strlen("Executing ")];
snprintf (execmsg, sizeof(execmsg), _("Executing %s"), browcall);
UIStatus (execmsg, 0, 0);
char execmsg[sizeof (browcall) + strlen ("Executing ")];
snprintf (execmsg, sizeof (execmsg), _("Executing %s"), browcall);
UIStatus (execmsg, 0, 0);
endwin();
system (browcall);
endwin();
system (browcall);
}
void SmartFeedsUpdate (void) {
for (struct feed* f = _feed_list; f; f = f->next)
if (f->smartfeed)
SmartFeedNewitems (f);
void SmartFeedsUpdate (void)
{
for (struct feed * f = _feed_list; f; f = f->next)
if (f->smartfeed)
SmartFeedNewitems (f);
}
void SmartFeedNewitems (struct feed* smart_feed) {
// Be smart and don't leak the smart feed.
// The items->data structures must not be freed, since a smart feed is only
// a pointer collection and does not contain allocated memory.
if (smart_feed->items) {
while (smart_feed->items->next) {
smart_feed->items = smart_feed->items->next;
free (smart_feed->items->prev);
}
free (smart_feed->items);
smart_feed->items = NULL;
void SmartFeedNewitems (struct feed* smart_feed)
{
// Be smart and don't leak the smart feed.
// The items->data structures must not be freed, since a smart feed is only
// a pointer collection and does not contain allocated memory.
if (smart_feed->items) {
while (smart_feed->items->next) {
smart_feed->items = smart_feed->items->next;
free (smart_feed->items->prev);
}
for (struct feed* f = _feed_list; f; f = f->next) {
// Do not add the smart feed recursively. 8)
if (f == smart_feed)
continue;
for (struct newsitem* item = f->items; item; item = item->next) {
// If item is unread, add to smart feed.
if (item->data->readstatus)
continue;
struct newsitem* new_item = calloc (1, sizeof (struct newsitem));
new_item->data = item->data;
free (smart_feed->items);
smart_feed->items = NULL;
}
for (struct feed * f = _feed_list; f; f = f->next) {
// Do not add the smart feed recursively. 8)
if (f == smart_feed)
continue;
for (struct newsitem * item = f->items; item; item = item->next) {
// If item is unread, add to smart feed.
if (item->data->readstatus)
continue;
struct newsitem* new_item = calloc (1, sizeof (struct newsitem));
new_item->data = item->data;
// Add to data structure.
if (!smart_feed->items)
smart_feed->items = new_item;
else {
new_item->prev = smart_feed->items;
while (new_item->prev->next)
new_item->prev = new_item->prev->next;
new_item->prev->next = new_item;
}
}
// Add to data structure.
if (!smart_feed->items)
smart_feed->items = new_item;
else {
new_item->prev = smart_feed->items;
while (new_item->prev->next)
new_item->prev = new_item->prev->next;
new_item->prev->next = new_item;
}
}
// Only fill out once.
if (!smart_feed->title)
smart_feed->title = strdup (_("(New headlines)"));
if (!smart_feed->link)
smart_feed->link = strdup (smart_feed->feedurl);
}
// Only fill out once.
if (!smart_feed->title)
smart_feed->title = strdup (_("(New headlines)"));
if (!smart_feed->link)
smart_feed->link = strdup (smart_feed->feedurl);
}
bool SmartFeedExists (const char * smartfeed) {
// Find our smart feed.
if (strcmp (smartfeed, "newitems") == 0)
for (const struct feed* f = _feed_list; f; f = f->next)
if (f->smartfeed == 1)
return true;
return false;
bool SmartFeedExists (const char* smartfeed)
{
// Find our smart feed.
if (strcmp (smartfeed, "newitems") == 0)
for (const struct feed * f = _feed_list; f; f = f->next)
if (f->smartfeed == 1)
return true;
return false;
}
void DrawProgressBar (int numobjects, int titlestrlen) {
void DrawProgressBar (int numobjects, int titlestrlen)
{
attron (WA_REVERSE);
mvhline (LINES - 1, titlestrlen + 1, '=', numobjects);
mvaddch (LINES - 1, COLS - 3, ']');
refresh();
attroff (WA_REVERSE);
}
void clearLine (int line, clear_line how)
{
if (how == INVERSE)
attron (WA_REVERSE);
mvhline (line, 0, ' ', COLS);
if (how == INVERSE)
attron (WA_REVERSE);
mvhline (LINES-1, titlestrlen+1, '=', numobjects);
mvaddch (LINES-1, COLS-3, ']');
refresh();
attroff(WA_REVERSE);
}
void clearLine (int line, clear_line how) {
if (how == INVERSE)
attron(WA_REVERSE);
mvhline (line, 0, ' ', COLS);
if (how == INVERSE)
attron(WA_REVERSE);
}

View File

@ -18,19 +18,19 @@
#include "main.h"
typedef enum {
NORMAL,
INVERSE
NORMAL,
INVERSE
} clear_line;
void InitCurses (void);
void UIStatus (const char * text, int delay, int warning);
void SwapPointers (struct feed * one, struct feed * two);
void UIStatus (const char* text, int delay, int warning);
void SwapPointers (struct feed* one, struct feed* two);
void SnowSort (void);
void UISupportDrawBox (int x1, int y1, int x2, int y2);
void UISupportDrawHeader (const char * headerstring);
void UISupportURLJump (const char * url);
void UISupportDrawHeader (const char* headerstring);
void UISupportURLJump (const char* url);
void SmartFeedsUpdate (void);
void SmartFeedNewitems (struct feed * smart_feed);
bool SmartFeedExists (const char * smartfeed);
void SmartFeedNewitems (struct feed* smart_feed);
bool SmartFeedExists (const char* smartfeed);
void DrawProgressBar (int numobjects, int titlestrlen);
void clearLine (int line, clear_line how);

View File

@ -25,8 +25,8 @@ static bool saverestore = false;
static struct newsitem* copy = NULL;
static struct newsitem* firstcopy = NULL;
static const xmlChar* dcNs = (const xmlChar*) "http://purl.org/dc/elements/1.1/";
static const xmlChar* snowNs = (const xmlChar*) "http://snownews.kcore.de/ns/1.0/";
static const xmlChar* dcNs = (const xmlChar *) "http://purl.org/dc/elements/1.1/";
static const xmlChar* snowNs = (const xmlChar *) "http://snownews.kcore.de/ns/1.0/";
//----------------------------------------------------------------------
@ -38,82 +38,82 @@ static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr no
static void free_feed (struct feed* feed)
{
free (feed->title);
free (feed->link);
free (feed->description);
if (feed->items) {
while (feed->items->next) {
feed->items = feed->items->next;
free (feed->items->prev->data->title);
free (feed->items->prev->data->link);
free (feed->items->prev->data->description);
free (feed->items->prev->data->hash);
free (feed->items->prev->data);
free (feed->items->prev);
}
free (feed->items->data->title);
free (feed->items->data->link);
free (feed->items->data->description);
free (feed->items->data->hash);
free (feed->items->data);
free (feed->items);
free (feed->title);
free (feed->link);
free (feed->description);
if (feed->items) {
while (feed->items->next) {
feed->items = feed->items->next;
free (feed->items->prev->data->title);
free (feed->items->prev->data->link);
free (feed->items->prev->data->description);
free (feed->items->prev->data->hash);
free (feed->items->prev->data);
free (feed->items->prev);
}
feed->items = NULL;
feed->title = NULL;
feed->link= NULL;
feed->description = NULL;
free (feed->items->data->title);
free (feed->items->data->link);
free (feed->items->data->description);
free (feed->items->data->hash);
free (feed->items->data);
free (feed->items);
}
feed->items = NULL;
feed->title = NULL;
feed->link = NULL;
feed->description = NULL;
}
// Called during parsing, if we look for a <channel> element
// The function returns a new struct for the newsfeed.
static void parse_rdf10_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node) {
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar*)"title") == 0) {
feed->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen(feed->title) > 1 && feed->title[strlen(feed->title)-1] == '\n')
feed->title[strlen(feed->title)-1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*)"link") == 0) {
feed->link = (char*) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen(feed->link) > 1 && feed->link[strlen(feed->link)-1] == '\n')
feed->link[strlen(feed->link)-1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*)"description") == 0) {
feed->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
}
static void parse_rdf10_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
feed->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
feed->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
feed->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
}
}
}
static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar*)"title") == 0) {
feed->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen(feed->title) > 1 && feed->title[strlen(feed->title)-1] == '\n')
feed->title[strlen(feed->title)-1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*)"link") == 0) {
feed->link = (char*) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen(feed->link) > 1 && feed->link[strlen(feed->link)-1] == '\n')
feed->link[strlen(feed->link)-1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*)"description") == 0) {
feed->description = (char*) xmlNodeListGetString(doc, cur->children, 1);
CleanupString (feed->description, 0);
} else if (xmlStrcmp (cur->name, (const xmlChar*)"item") == 0)
parse_rdf10_item(feed, doc, cur->children);
}
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
feed->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
feed->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
feed->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
} else if (xmlStrcmp (cur->name, (const xmlChar *) "item") == 0)
parse_rdf10_item (feed, doc, cur->children);
}
}
// This function is called every time we hit an <item>. As parameter it
@ -121,232 +121,224 @@ static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr no
// XML Document handle and the current element, both come directly from
// the libxml.
static void parse_rdf10_item (struct feed *feed, xmlDocPtr doc, xmlNodePtr node)
static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Reserve memory for a new news item
struct newsitem* item = calloc(1, sizeof (struct newsitem));
item->data = calloc (1, sizeof (struct newsdata));
item->data->parent = feed;
// Reserve memory for a new news item
struct newsitem* item = calloc (1, sizeof (struct newsitem));
item->data = calloc (1, sizeof (struct newsdata));
item->data->parent = feed;
char *guid = NULL;
char* guid = NULL;
// Go through all the tags in the <item> tag and extract the information.
// same procedure as in the parse_channel () function
for (xmlNodePtr cur = node; cur != NULL; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
// Basic RSS
// Title
if (xmlStrcmp(cur->name, (const xmlChar*)"title") == 0) {
item->data->title = (char *)xmlNodeListGetString(doc, cur->children, 1);
CleanupString (item->data->title, 1);
// Remove trailing newline
if (item->data->title != NULL) {
if (strlen(item->data->title) > 1) {
if (item->data->title[strlen(item->data->title)-1] == '\n')
item->data->title[strlen(item->data->title)-1] = '\0';
}
}
// link
} else if (xmlStrcmp(cur->name, (const xmlChar*)"link") == 0) {
item->data->link = (char *)xmlNodeListGetString(doc, cur->children, 1);
// Remove trailing newline
if (item->data->link != NULL) {
if (strlen(item->data->link) > 1) {
if (item->data->link[strlen(item->data->link)-1] == '\n')
item->data->link[strlen(item->data->link)-1] = '\0';
}
}
// Description
} else if (xmlStrcmp(cur->name, (const xmlChar*)"description") == 0) {
item->data->description = (char *)xmlNodeListGetString(doc, cur->children, 1);
CleanupString (item->data->description, 0);
// Userland extensions (No namespace!)
// guid
} else if (xmlStrcmp(cur->name, (const xmlChar*)"guid") == 0) {
guid = (char*) xmlNodeListGetString(doc, cur->children, 1);
// pubDate
} else if (xmlStrcmp(cur->name, (const xmlChar*)"pubDate") == 0) {
xmlChar* date_str = xmlNodeListGetString(doc, cur->children, 1);
item->data->date = pubDateToUnix((const char*) date_str);
xmlFree (date_str);
// Dublin Core
// dc:date
} else if (cur->ns && xmlStrcmp(cur->ns->href, dcNs) == 0
&& xmlStrcmp(cur->name, (const xmlChar*)"date") == 0) {
xmlChar* date_str = xmlNodeListGetString(doc, cur->children, 1);
item->data->date = ISODateToUnix((const char*) date_str);
xmlFree (date_str);
// Internal usage
// Obsolete/backware compat/migration code
} else if (xmlStrcmp(cur->name, (const xmlChar*)"readstatus") == 0) {
// Will cause memory leak otherwise, xmlNodeListGetString must be freed.
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char*)readstatusstring);
xmlFree (readstatusstring);
// Using snow namespace
} else if (cur->ns && xmlStrcmp(cur->ns->href, snowNs) == 0
&& xmlStrcmp(cur->name, (const xmlChar*)"readstatus") == 0) {
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char *)readstatusstring);
xmlFree (readstatusstring);
} else if (cur->ns && xmlStrcmp(cur->ns->href, snowNs) == 0
&& xmlStrcmp(cur->name, (const xmlChar*)"hash") == 0) {
item->data->hash = (char*) xmlNodeListGetString (doc, cur->children, 1);
} else if (cur->ns && xmlStrcmp(cur->ns->href, snowNs) == 0
&& xmlStrcmp(cur->name, (const xmlChar*)"date") == 0) {
xmlChar* date_str = xmlNodeListGetString(doc, cur->children, 1);
item->data->date = atol ((const char*) date_str);
xmlFree (date_str);
// Go through all the tags in the <item> tag and extract the information.
// same procedure as in the parse_channel() function
for (xmlNodePtr cur = node; cur != NULL; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
// Basic RSS
// Title
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
item->data->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->title, 1);
// Remove trailing newline
if (item->data->title != NULL) {
if (strlen (item->data->title) > 1) {
if (item->data->title[strlen (item->data->title) - 1] == '\n')
item->data->title[strlen (item->data->title) - 1] = '\0';
}
}
// If we have loaded the hash from disk cache, don't regenerate it.
// <guid> is not saved in the cache, thus we would generate a different
// hash than the one from the live feed.
if (item->data->hash == NULL) {
const char* hashitems[] = { item->data->title, item->data->link, guid, NULL };
item->data->hash = genItemHash (hashitems, 3);
xmlFree (guid);
}
// If saverestore == true, restore readstatus.
if (saverestore) {
for (struct newsitem* i = firstcopy; i; i = i->next) {
if (strcmp(item->data->hash, i->data->hash) == 0) {
item->data->readstatus = i->data->readstatus;
break;
}
}
// link
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
item->data->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
// Remove trailing newline
if (item->data->link != NULL) {
if (strlen (item->data->link) > 1) {
if (item->data->link[strlen (item->data->link) - 1] == '\n')
item->data->link[strlen (item->data->link) - 1] = '\0';
}
}
// Description
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
item->data->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->description, 0);
// Userland extensions (No namespace!)
// guid
} else if (xmlStrcmp (cur->name, (const xmlChar *) "guid") == 0) {
guid = (char *) xmlNodeListGetString (doc, cur->children, 1);
// pubDate
} else if (xmlStrcmp (cur->name, (const xmlChar *) "pubDate") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = pubDateToUnix ((const char *) date_str);
xmlFree (date_str);
// Dublin Core
// dc:date
} else if (cur->ns && xmlStrcmp (cur->ns->href, dcNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "date") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = ISODateToUnix ((const char *) date_str);
xmlFree (date_str);
// Internal usage
// Obsolete/backware compat/migration code
} else if (xmlStrcmp (cur->name, (const xmlChar *) "readstatus") == 0) {
// Will cause memory leak otherwise, xmlNodeListGetString must be freed.
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char *) readstatusstring);
xmlFree (readstatusstring);
// Using snow namespace
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "readstatus") == 0) {
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char *) readstatusstring);
xmlFree (readstatusstring);
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "hash") == 0) {
item->data->hash = (char *) xmlNodeListGetString (doc, cur->children, 1);
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "date") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = atol ((const char *) date_str);
xmlFree (date_str);
}
}
if (!feed->items)
feed->items = item;
else {
item->prev = feed->items;
while (item->prev->next)
item->prev = item->prev->next;
item->prev->next = item;
// If we have loaded the hash from disk cache, don't regenerate it.
// <guid> is not saved in the cache, thus we would generate a different
// hash than the one from the live feed.
if (item->data->hash == NULL) {
const char* hashitems[] = { item->data->title, item->data->link, guid, NULL };
item->data->hash = genItemHash (hashitems, 3);
xmlFree (guid);
}
// If saverestore == true, restore readstatus.
if (saverestore) {
for (struct newsitem * i = firstcopy; i; i = i->next) {
if (strcmp (item->data->hash, i->data->hash) == 0) {
item->data->readstatus = i->data->readstatus;
break;
}
}
}
if (!feed->items)
feed->items = item;
else {
item->prev = feed->items;
while (item->prev->next)
item->prev = item->prev->next;
item->prev->next = item;
}
}
// rrr
int DeXML (struct feed* cur_ptr) {
if (!cur_ptr->xmltext)
return -1;
int DeXML (struct feed* cur_ptr)
{
if (!cur_ptr->xmltext)
return -1;
saverestore = false;
// If cur_ptr-> items! = NULL then we can cache item->readstatus
if (cur_ptr->items != NULL) {
saverestore = true;
firstcopy = NULL;
saverestore = false;
// If cur_ptr-> items! = NULL then we can cache item->readstatus
if (cur_ptr->items != NULL) {
saverestore = true;
firstcopy = NULL;
// Copy current newsitem struct. */
for (struct newsitem* cur_item = cur_ptr->items; cur_item != NULL; cur_item = cur_item->next) {
copy = malloc (sizeof(struct newsitem));
copy->data = malloc (sizeof (struct newsdata));
copy->data->title = NULL;
copy->data->link = NULL;
copy->data->description = NULL;
copy->data->hash = NULL;
copy->data->readstatus = cur_item->data->readstatus;
if (cur_item->data->hash != NULL)
copy->data->hash = strdup (cur_item->data->hash);
// Copy current newsitem struct. */
for (struct newsitem * cur_item = cur_ptr->items; cur_item != NULL; cur_item = cur_item->next) {
copy = malloc (sizeof (struct newsitem));
copy->data = malloc (sizeof (struct newsdata));
copy->data->title = NULL;
copy->data->link = NULL;
copy->data->description = NULL;
copy->data->hash = NULL;
copy->data->readstatus = cur_item->data->readstatus;
if (cur_item->data->hash != NULL)
copy->data->hash = strdup (cur_item->data->hash);
copy->next = NULL;
if (firstcopy == NULL) {
copy->prev = NULL;
firstcopy = copy;
} else {
copy->prev = firstcopy;
while (copy->prev->next != NULL)
copy->prev = copy->prev->next;
copy->prev->next = copy;
}
}
copy->next = NULL;
if (firstcopy == NULL) {
copy->prev = NULL;
firstcopy = copy;
} else {
copy->prev = firstcopy;
while (copy->prev->next != NULL)
copy->prev = copy->prev->next;
copy->prev->next = copy;
}
}
}
// xmlRecoverMemory:
// parse an XML in-memory document and build a tree.
// In case the document is not Well Formed, a tree is built anyway.
xmlDocPtr doc = xmlRecoverMemory (cur_ptr->xmltext, strlen (cur_ptr->xmltext));
// xmlRecoverMemory:
// parse an XML in-memory document and build a tree.
// In case the document is not Well Formed, a tree is built anyway.
xmlDocPtr doc = xmlRecoverMemory(cur_ptr->xmltext, strlen(cur_ptr->xmltext));
if (doc == NULL)
return 2;
if (doc == NULL)
return 2;
// Find the root element (in our case, it should read "<RDF: RDF>").
// The RDF: prefix is ignored for now until the Jaguar
// Find out how to read that exactly (jau).
xmlNodePtr cur = xmlDocGetRootElement(doc);
if (!cur) {
xmlFreeDoc (doc);
return 2;
// Find the root element (in our case, it should read "<RDF: RDF>").
// The RDF: prefix is ignored for now until the Jaguar
// Find out how to read that exactly (jau).
xmlNodePtr cur = xmlDocGetRootElement (doc);
if (!cur) {
xmlFreeDoc (doc);
return 2;
}
// Check if the element really is called <RDF>
if (xmlStrcmp (cur->name, (const xmlChar *) "RDF") == 0) {
// Now we go through all the elements in the document. This loop however,
// only the highest level elements work (HTML would only be HEAD and
// BODY), so do not wander entire structure down through. The functions
// are responsible for this, which we then call in the loop itself.
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (c->name, (const xmlChar *) "channel") == 0)
parse_rdf10_channel (cur_ptr, doc, c->children);
if (xmlStrcmp (c->name, (const xmlChar *) "item") == 0)
parse_rdf10_item (cur_ptr, doc, c->children);
// Last-Modified is only used when reading from internal feeds (disk cache).
if (c->ns && (xmlStrcmp (c->ns->href, snowNs) == 0) && (xmlStrcmp (c->name, (const xmlChar *) "lastmodified") == 0))
cur_ptr->lastmodified = (char *) xmlNodeListGetString (doc, c->children, 1);
}
// Check if the element really is called <RDF>
if (xmlStrcmp(cur->name, (const xmlChar*)"RDF") == 0) {
// Now we go through all the elements in the document. This loop however,
// only the highest level elements work (HTML would only be HEAD and
// BODY), so do not wander entire structure down through. The functions
// are responsible for this, which we then call in the loop itself.
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(c->name, (const xmlChar*)"channel") == 0)
parse_rdf10_channel(cur_ptr, doc, c->children);
if (xmlStrcmp(c->name, (const xmlChar*)"item") == 0)
parse_rdf10_item(cur_ptr, doc, c->children);
// Last-Modified is only used when reading from internal feeds (disk cache).
if (c->ns && (xmlStrcmp(c->ns->href, snowNs) == 0) &&
(xmlStrcmp(c->name, (const xmlChar*)"lastmodified") == 0))
cur_ptr->lastmodified = (char *)xmlNodeListGetString(doc, c->children, 1);
}
} else if (xmlStrcmp(cur->name, (const xmlChar*)"rss") == 0) {
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(c->name, (const xmlChar*)"channel") == 0)
parse_rdf20_channel(cur_ptr, doc, c->children);
}
} else {
xmlFreeDoc(doc);
return 3;
} else if (xmlStrcmp (cur->name, (const xmlChar *) "rss") == 0) {
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (c->name, (const xmlChar *) "channel") == 0)
parse_rdf20_channel (cur_ptr, doc, c->children);
}
} else {
xmlFreeDoc (doc);
return 3;
}
xmlFreeDoc(doc);
xmlFreeDoc (doc);
if (saverestore) {
// free struct newsitem *copy.
while (firstcopy->next != NULL) {
firstcopy = firstcopy->next;
free (firstcopy->prev->data->hash);
free (firstcopy->prev->data);
free (firstcopy->prev);
}
free (firstcopy->data->hash);
free (firstcopy->data);
free (firstcopy);
if (saverestore) {
// free struct newsitem *copy.
while (firstcopy->next != NULL) {
firstcopy = firstcopy->next;
free (firstcopy->prev->data->hash);
free (firstcopy->prev->data);
free (firstcopy->prev);
}
free (firstcopy->data->hash);
free (firstcopy->data);
free (firstcopy);
}
if (cur_ptr->original != NULL)
free (cur_ptr->original);
if (cur_ptr->original != NULL)
free (cur_ptr->original);
// Set -> title to something if it's a NULL pointer to avoid crash with strdup below.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
char* converted = iconvert (cur_ptr->title);
if (converted) {
free (cur_ptr->title);
cur_ptr->title = converted;
}
cur_ptr->original = strdup (cur_ptr->title);
// Set -> title to something if it's a NULL pointer to avoid crash with strdup below.
if (cur_ptr->title == NULL)
cur_ptr->title = strdup (cur_ptr->feedurl);
char* converted = iconvert (cur_ptr->title);
if (converted) {
free (cur_ptr->title);
cur_ptr->title = converted;
}
cur_ptr->original = strdup (cur_ptr->title);
// Restore custom title.
if (cur_ptr->custom_title) {
free (cur_ptr->title);
cur_ptr->title = strdup (cur_ptr->custom_title);
}
return 0;
// Restore custom title.
if (cur_ptr->custom_title) {
free (cur_ptr->title);
cur_ptr->title = strdup (cur_ptr->custom_title);
}
return 0;
}

View File

@ -18,104 +18,104 @@
#include <zlib.h>
struct gzip_header {
unsigned char magic[2];
unsigned char method;
unsigned char flags;
unsigned char mtime[4];
unsigned char xfl;
unsigned char os;
unsigned char magic[2];
unsigned char method;
unsigned char flags;
unsigned char mtime[4];
unsigned char xfl;
unsigned char os;
};
struct gzip_footer {
unsigned char crc32[4];
unsigned char size[4];
unsigned char crc32[4];
unsigned char size[4];
};
static int jg_zlib_uncompress(void const *in_buf, unsigned in_size, void **out_buf_ptr, unsigned* out_size, bool gzip)
static int jg_zlib_uncompress (void const* in_buf, unsigned in_size, void** out_buf_ptr, unsigned* out_size, bool gzip)
{
// Prepare the stream structure.
z_stream stream = {};
// Prepare the stream structure.
z_stream stream = { };
stream.next_in = (void*) in_buf;
stream.avail_in = in_size;
stream.next_in = (void *) in_buf;
stream.avail_in = in_size;
unsigned char tmp_buf [BUFSIZ];
stream.next_out = tmp_buf;
stream.avail_out = sizeof tmp_buf;
unsigned char tmp_buf[BUFSIZ];
stream.next_out = tmp_buf;
stream.avail_out = sizeof tmp_buf;
if (out_size)
*out_size = 0;
if (out_size)
*out_size = 0;
int result = inflateInit2 (&stream, gzip ? MAX_WBITS + 32 : -MAX_WBITS);
if (result)
return JG_ZLIB_ERROR_OLDVERSION;
int result = inflateInit2 (&stream, gzip ? MAX_WBITS + 32 : -MAX_WBITS);
if (result)
return JG_ZLIB_ERROR_OLDVERSION;
char *out_buf = NULL;
unsigned out_buf_bytes = 0;
do {
// Should be Z_FINISH?
result = inflate(&stream, Z_NO_FLUSH);
switch (result) {
case Z_BUF_ERROR:
if (stream.avail_in == 0)
goto DONE; // zlib bug
// fallthrough
case Z_ERRNO:
case Z_NEED_DICT:
case Z_MEM_ERROR:
case Z_DATA_ERROR:
case Z_VERSION_ERROR:
inflateEnd(&stream);
free(out_buf);
return JG_ZLIB_ERROR_UNCOMPRESS;
}
if (stream.avail_out < sizeof(tmp_buf)) {
// Add the new uncompressed data to our output buffer.
unsigned new_bytes = sizeof(tmp_buf) - stream.avail_out;
out_buf = realloc(out_buf, out_buf_bytes + new_bytes);
memcpy(out_buf + out_buf_bytes, tmp_buf, new_bytes);
out_buf_bytes += new_bytes;
stream.next_out = tmp_buf;
stream.avail_out = sizeof(tmp_buf);
} else {
// For some reason, inflate() didn't write out a single byte.
inflateEnd(&stream);
free(out_buf);
return JG_ZLIB_ERROR_NODATA;
}
} while (result != Z_STREAM_END);
DONE:
inflateEnd (&stream);
char* out_buf = NULL;
unsigned out_buf_bytes = 0;
do {
// Should be Z_FINISH?
result = inflate (&stream, Z_NO_FLUSH);
switch (result) {
case Z_BUF_ERROR:
if (stream.avail_in == 0)
goto DONE; // zlib bug
// fallthrough
case Z_ERRNO:
case Z_NEED_DICT:
case Z_MEM_ERROR:
case Z_DATA_ERROR:
case Z_VERSION_ERROR:
inflateEnd (&stream);
free (out_buf);
return JG_ZLIB_ERROR_UNCOMPRESS;
}
if (stream.avail_out < sizeof (tmp_buf)) {
// Add the new uncompressed data to our output buffer.
unsigned new_bytes = sizeof (tmp_buf) - stream.avail_out;
out_buf = realloc (out_buf, out_buf_bytes + new_bytes);
memcpy (out_buf + out_buf_bytes, tmp_buf, new_bytes);
out_buf_bytes += new_bytes;
stream.next_out = tmp_buf;
stream.avail_out = sizeof (tmp_buf);
} else {
// For some reason, inflate() didn't write out a single byte.
inflateEnd (&stream);
free (out_buf);
return JG_ZLIB_ERROR_NODATA;
}
} while (result != Z_STREAM_END);
DONE:
inflateEnd (&stream);
// Null-terminate the output buffer so it can be handled like a string.
out_buf = realloc (out_buf, out_buf_bytes + 1);
out_buf[out_buf_bytes] = 0;
// Null-terminate the output buffer so it can be handled like a string.
out_buf = realloc (out_buf, out_buf_bytes + 1);
out_buf[out_buf_bytes] = 0;
// The returned size does NOT include the additionall null byte!
if (out_size)
*out_size = out_buf_bytes;
*out_buf_ptr = out_buf;
return 0;
// The returned size does NOT include the additionall null byte!
if (out_size)
*out_size = out_buf_bytes;
*out_buf_ptr = out_buf;
return 0;
}
// Decompressed gzip,deflate compressed data. This is what the webservers usually send.
int jg_gzip_uncompress (const void* in_buf, unsigned in_size, void** out_buf_ptr, unsigned* out_size)
{
if (out_size)
*out_size = 0;
if (out_size)
*out_size = 0;
const struct gzip_header* header = in_buf;
if ((header->magic[0] != 0x1F) || (header->magic[1] != 0x8B))
return JG_ZLIB_ERROR_BAD_MAGIC;
if (header->method != 8)
return JG_ZLIB_ERROR_BAD_METHOD;
if (header->flags != 0 && header->flags != 8)
return JG_ZLIB_ERROR_BAD_FLAGS;
const struct gzip_header* header = in_buf;
if ((header->magic[0] != 0x1F) || (header->magic[1] != 0x8B))
return JG_ZLIB_ERROR_BAD_MAGIC;
if (header->method != 8)
return JG_ZLIB_ERROR_BAD_METHOD;
if (header->flags != 0 && header->flags != 8)
return JG_ZLIB_ERROR_BAD_FLAGS;
unsigned offset = sizeof(*header);
if (header->flags & 8) // skip the file name
while (offset < in_size)
if (((char *)in_buf)[offset++] == 0)
break;
return jg_zlib_uncompress ((char*)in_buf + offset, in_size - offset - 8, out_buf_ptr, out_size, 0);
unsigned offset = sizeof (*header);
if (header->flags & 8) // skip the file name
while (offset < in_size)
if (((char *) in_buf)[offset++] == 0)
break;
return jg_zlib_uncompress ((char *) in_buf + offset, in_size - offset - 8, out_buf_ptr, out_size, 0);
}

View File

@ -18,12 +18,12 @@
#include "config.h"
enum JG_ZLIB_ERROR {
JG_ZLIB_ERROR_OLDVERSION = -1,
JG_ZLIB_ERROR_UNCOMPRESS = -2,
JG_ZLIB_ERROR_NODATA = -3,
JG_ZLIB_ERROR_BAD_MAGIC = -4,
JG_ZLIB_ERROR_BAD_METHOD = -5,
JG_ZLIB_ERROR_BAD_FLAGS = -6
JG_ZLIB_ERROR_OLDVERSION = -1,
JG_ZLIB_ERROR_UNCOMPRESS = -2,
JG_ZLIB_ERROR_NODATA = -3,
JG_ZLIB_ERROR_BAD_MAGIC = -4,
JG_ZLIB_ERROR_BAD_METHOD = -5,
JG_ZLIB_ERROR_BAD_FLAGS = -6
};
int jg_gzip_uncompress(const void* in_buf, unsigned in_size, void** out_buf_ptr, unsigned* out_size);
int jg_gzip_uncompress (const void* in_buf, unsigned in_size, void** out_buf_ptr, unsigned* out_size);