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:
parent
5b73b0dad8
commit
23a08804f8
591
about.c
591
about.c
|
@ -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;
|
||||
}
|
||||
|
|
286
categories.c
286
categories.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
16
config.h.in
16
config.h.in
|
@ -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
|
||||
|
|
977
conversions.c
977
conversions.c
File diff suppressed because it is too large
Load Diff
|
@ -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
305
cookies.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
void LoadCookies (struct feed * cur_ptr);
|
||||
void LoadCookies (struct feed* cur_ptr);
|
||||
|
|
12
dialog.h
12
dialog.h
|
@ -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
105
digcalc.c
|
@ -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);
|
||||
}
|
||||
|
|
29
digcalc.h
29
digcalc.h
|
@ -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
268
filters.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
2503
interface.c
2503
interface.c
File diff suppressed because it is too large
Load Diff
|
@ -16,5 +16,5 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
void sig_winch(int p);
|
||||
void sig_winch (int p);
|
||||
void UIMainInterface (void);
|
||||
|
|
724
io-internal.c
724
io-internal.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
385
main.c
|
@ -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
248
main.h
|
@ -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
54
md5.c
|
@ -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
12
md5.h
|
@ -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)];
|
||||
};
|
||||
};
|
||||
|
||||
|
|
293
net-support.c
293
net-support.c
|
@ -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;
|
||||
}
|
||||
|
|
81
os-support.c
81
os-support.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
777
setup.c
|
@ -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: —--\n"
|
||||
"#\n"
|
||||
"# XML defined entities are converted by default. These are:\n"
|
||||
"# &, <, >, ', "\n"
|
||||
"#\n"
|
||||
"ä%c\n"
|
||||
"ö%c\n"
|
||||
"ü%c\n"
|
||||
"Ä%c\n"
|
||||
"Ö%c\n"
|
||||
"Ü%c\n"
|
||||
"ß%c\n"
|
||||
" \n"
|
||||
"—--\n"
|
||||
"…...\n"
|
||||
"“\"\n"
|
||||
"”\"\n"
|
||||
"»\"\n"
|
||||
"«\"\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: —--\n" "#\n"
|
||||
"# XML defined entities are converted by default. These are:\n" "# &, <, >, ', "\n" "#\n" "ä%c\n" "ö%c\n" "ü%c\n" "Ä%c\n" "Ö%c\n" "Ü%c\n" "ß%c\n" " \n" "—--\n"
|
||||
"…...\n" "“\"\n" "”\"\n" "»\"\n" "«\"\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;
|
||||
}
|
||||
|
|
335
ui-support.c
335
ui-support.c
|
@ -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);
|
||||
}
|
||||
|
|
16
ui-support.h
16
ui-support.h
|
@ -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);
|
||||
|
|
530
xmlparse.c
530
xmlparse.c
|
@ -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;
|
||||
}
|
||||
|
|
160
zlib_interface.c
160
zlib_interface.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue