/* Snownews - A lightweight console RSS newsreader * $Id: io-internal.c 1055 2005-04-27 17:50:32Z kiza $ * * Copyright 2003-2004 Oliver Feiler * http://kiza.kcore.de/software/snownews/ * * io-internal.c * * Please read the file README.patching before changing any code in this file! * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "main.h" #include "conversions.h" #include "filters.h" #include "categories.h" #include "netio.h" #include "xmlparse.h" #include "ui-support.h" #include "io-internal.h" extern char *browser; struct feed * newFeedStruct (void) { struct feed *new; new = malloc(sizeof(struct feed)); new->feed = NULL; new->content_length = 0; new->title = NULL; new->link = NULL; new->description = NULL; new->lastmodified = NULL; new->lasthttpstatus = 0; new->content_type = NULL; new->netio_error = NET_ERR_OK; new->connectresult = 0; new->cookies = NULL; new->authinfo = NULL; new->servauth = NULL; new->items = NULL; new->problem = 0; new->override = NULL; new->original = NULL; new->perfeedfilter = NULL; new->execurl = 0; new->smartfeed = 0; new->feedcategories = NULL; return new; } void GetHTTPErrorString (char * errorstring, int size, int httpstatus) { switch (httpstatus) { case 400: snprintf(errorstring, size, "Bad request"); break; case 402: snprintf(errorstring, size, "Payment required"); break; case 403: snprintf(errorstring, size, "Access denied"); break; case 500: snprintf(errorstring, size, "Internal server error"); break; case 501: snprintf(errorstring, size, "Not implemented"); break; case 502: case 503: snprintf(errorstring, size, "Service unavailable"); break; default: sprintf(errorstring, "HTTP %d!", httpstatus); } } void PrintUpdateError (int suppressoutput, struct feed * cur_ptr) { netio_error_type err; char errstr[256]; char httperrstr[64]; err = cur_ptr->netio_error; if (!suppressoutput) { switch (err) { case NET_ERR_OK: break; 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: 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; } /* Must be inside if(!suppressoutput) statement! */ UIStatus(errstr, 2, 1); } printlog(cur_ptr, errstr); } /* Update given feed from server. * Reload XML document and replace in memory cur_ptr->feed with it. */ int UpdateFeed (struct feed * cur_ptr) { char *tmpname; char *freeme; 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. */ tmpname = strdup (cur_ptr->feedurl); freeme = tmpname; /* Need to make a copy, otherwise we cannot free all RAM. */ free (cur_ptr->feed); cur_ptr->feed = DownloadFeed (tmpname, cur_ptr, 0); free (freeme); /* 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->feed == NULL) { if (cur_ptr->problem == 1) PrintUpdateError (0, 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->feed == NULL) return 1; if ((DeXML (cur_ptr)) != 0) { UIStatus (_("Invalid XML! Cannot parse this feed!"), 2, 1); /* Activate feed problem flag. */ cur_ptr->problem = 1; return 1; } /* We don't need these anymore. Free the raw XML to save some memory. */ free (cur_ptr->feed); cur_ptr->feed = NULL; return 0; } int UpdateAllFeeds (void) { struct feed *cur_ptr; for (cur_ptr = first_ptr; cur_ptr != NULL; cur_ptr = cur_ptr->next_ptr) { if ((UpdateFeed (cur_ptr)) != 0) continue; } return 0; } /* Load feed from disk. And call UpdateFeed if neccessary. */ int LoadFeed (struct feed * cur_ptr) { int len = 0; /* Internal usage for realloc. */ int retval; char filebuf[4096]; /* File I/O block buffer. */ char file[512]; char *hashme; /* Hashed filename. */ char tmp[1024]; FILE *cache; /* Smart feeds are generated in the fly. */ if (cur_ptr->smartfeed == 1) return 0; hashme = Hashify(cur_ptr->feedurl); snprintf (file, sizeof(file), "%s/.snownews/cache/%s", getenv("HOME"), hashme); free (hashme); cache = fopen (file, "r"); if (cache == NULL) { snprintf (tmp, sizeof(tmp), _("Cache for %s is toast. Reloading from server..."), cur_ptr->feedurl); UIStatus (tmp, 0, 0); if ((UpdateFeed (cur_ptr)) != 0) return 1; return 0; } /* Read complete cachefile. */ while (!feof(cache)) { /* Use binary read, UTF-8 data! */ retval = fread (filebuf, 1, sizeof(filebuf), cache); if (retval == 0) break; cur_ptr->feed = realloc (cur_ptr->feed, len+retval + 1); memcpy (cur_ptr->feed+len, filebuf, retval); len += retval; if (retval != 4096) break; } cur_ptr->content_length = len; if (cur_ptr->feed == NULL) return 1; cur_ptr->feed[len] = '\0'; fclose (cache); /* After loading DeXMLize the mess. */ if ((DeXML (cur_ptr)) != 0) { /* If loading the feed from the disk fails, try downloading from the net. */ if ((UpdateFeed (cur_ptr)) != 0) { /* And if that fails as well, just continue without this feed. */ snprintf (tmp, sizeof(tmp), _("Could not load %s!"), cur_ptr->feedurl); UIStatus (tmp, 2, 1); } } free (cur_ptr->feed); cur_ptr->feed = NULL; return 0; } int LoadAllFeeds (int numfeeds) { float numobjperfeed = 0; /* Number of "progress bar" objects to draw per feed. */ int count = 1; int numobjects = 0; int oldnumobjects = 0; int titlestrlen; struct feed *cur_ptr; UIStatus (_("Loading cache ["), 0, 0); titlestrlen = strlen (_("Loading cache [")); if (numfeeds > 0) numobjperfeed = (COLS-titlestrlen-2)/(double) numfeeds; for (cur_ptr = first_ptr; cur_ptr != NULL; cur_ptr = cur_ptr->next_ptr) { /* Progress bar */ numobjects = (count * numobjperfeed) - 2; if (numobjects < 1) numobjects = 1; if (numobjects > oldnumobjects) { DrawProgressBar(numobjects, titlestrlen); oldnumobjects = numobjects; } if ((LoadFeed (cur_ptr)) != 0) { continue; } count++; } return 0; } /* Write in memory structures to disk cache. * Usually called before program exit. */ void WriteCache (void) { char file[512]; /* File locations. */ char *hashme; /* Cache file name. */ char readstatus[2]; char snowdate[32]; char syscall[512]; FILE *configfile; FILE *cache; struct feed *cur_ptr; struct newsitem *item; struct stat filetest; struct feedcategories *category; char *encoded; float numobjperfeed = 0; /* Number of "progress bar" objects to draw per feed. */ int count = 1; int numobjects = 0; int oldnumobjects = 0; int numfeeds = 0; int titlestrlen; UIStatus (_("Saving settings ["), 0, 0); titlestrlen = strlen (_("Saving settings [")); snprintf (file, sizeof(file), "%s/.snownews/browser", getenv("HOME")); configfile = fopen (file, "w+"); if (configfile == NULL) { MainQuit (_("Save settings (browser)"), strerror(errno)); } fputs (browser, configfile); fclose (configfile); snprintf (file, sizeof(file), "%s/.snownews/urls", getenv("HOME")); /* Make a backup of urls. This approach is really broken! */ if ((stat (file, &filetest)) != -1) { if ((filetest.st_mode & S_IFREG) == S_IFREG) { snprintf (syscall, sizeof(file), "cp -f %s/.snownews/urls %s/.snownews/urls.bak", getenv("HOME"), getenv("HOME")); system (syscall); } } configfile = fopen (file, "w+"); if (configfile == NULL) { MainQuit (_("Save settings (urls)"), strerror(errno)); } /* Unoptimized vs. KISS */ for (cur_ptr = first_ptr; cur_ptr != NULL; cur_ptr = cur_ptr->next_ptr) numfeeds++; if (numfeeds > 0) numobjperfeed = (COLS-titlestrlen-2)/(double) numfeeds; for (cur_ptr = first_ptr; cur_ptr != NULL; cur_ptr = cur_ptr->next_ptr) { /* Progress bar */ numobjects = (count * numobjperfeed) - 2; if (numobjects < 1) numobjects = 1; if (numobjects > oldnumobjects) { DrawProgressBar(numobjects, titlestrlen); oldnumobjects = numobjects; } count++; fputs (cur_ptr->feedurl, configfile); fputc ('|', configfile); if (cur_ptr->override != NULL) fputs (cur_ptr->title, configfile); fputc ('|', configfile); if (cur_ptr->feedcategories != NULL) { for (category = cur_ptr->feedcategories; category != NULL; category = category->next_ptr) { fputs (category->name, configfile); /* Only add a colon of we run the loop again! */ if (category->next_ptr != NULL) fputc (',', configfile); } } fputc ('|', configfile); if (cur_ptr->perfeedfilter != NULL) fputs (cur_ptr->perfeedfilter, configfile); fputc ('\n', configfile); /* Add newline character. */ /* Discard smart feeds from cache. */ if (cur_ptr->smartfeed) continue; /* * Write cache. */ hashme = Hashify(cur_ptr->feedurl); snprintf (file, sizeof(file), "%s/.snownews/cache/%s", getenv("HOME"), hashme); free (hashme); cache = fopen (file, "w+"); if (cache == NULL) MainQuit (_("Writing cache file"), strerror(errno)); fputs ("\n\n\n\n", cache); if (cur_ptr->lastmodified != NULL) { fputs ("", cache); fputs (cur_ptr->lastmodified, cache); fputs ("\n", cache); } fputs ("feedurl); fputs (encoded, cache); free (encoded); fputs ("\">\n", cache); if (cur_ptr->original != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)cur_ptr->original); fputs (encoded, cache); free (encoded); } else if (cur_ptr->title != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)cur_ptr->title); fputs (encoded, cache); free (encoded); } fputs ("\n", cache); if (cur_ptr->link != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)cur_ptr->link); fputs (encoded, cache); free (encoded); } fputs ("\n", cache); if (cur_ptr->description != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)cur_ptr->description); fputs (encoded, cache); free (encoded); } fputs ("\n\n\n", cache); for (item = cur_ptr->items; item != NULL; item = item->next_ptr) { fputs ("data->link != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->link); fputs (encoded, cache); free (encoded); } fputs ("\">\n", cache); if (item->data->title != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->title); fputs (encoded, cache); free (encoded); } fputs ("\n", cache); if (item->data->link != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->link); fputs (encoded, cache); free (encoded); } fputs ("\n", cache); if (item->data->description != NULL) { encoded = (char *)xmlEncodeEntitiesReentrant (NULL, (xmlChar *)item->data->description); fputs (encoded, cache); free (encoded); } fputs ("\n", cache); snprintf (readstatus, sizeof(readstatus), "%d", item->data->readstatus); fputs (readstatus, cache); fputs ("\n", cache); if (item->data->hash != NULL) { fputs (item->data->hash, cache); } fputs ("\n", cache); snprintf (snowdate, sizeof(snowdate), "%d", item->data->date); fputs (snowdate, cache); fputs ("\n\n\n", cache); } fputs ("", cache); fclose (cache); } fclose (configfile); return; } void printlog (struct feed * feed, const char * text) { char *timestring; time_t t; t = time(NULL); timestring = ctime(&t); timestring[strlen(timestring)-1] = 0; fprintf (stderr, "%s: (%s) %s\n", timestring, feed->feedurl, text); }