summaryrefslogtreecommitdiffstats
path: root/knewsticker/common/newsengine.cpp
blob: 911095d9bb4ff025dd293c4e4d4958870a125f22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*
 * newsengine.cpp
 *
 * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
 *
 * 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. For licensing and distribution details, check the
 * accompanying file 'COPYING'.
 */
#include "newsengine.h"
#include "configiface.h"
#include "configaccess.h"
#include "xmlnewsaccess.h"
#include "newsiconmgr.h"

#include <kapplication.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kprocess.h>
#include <krun.h>
#include <kstandarddirs.h>

#include <tqbuffer.h>

Article::Article(NewsSourceBase *parent, const TQString &headline,
		const KURL &address)
	: XMLNewsArticle(headline, address),
	m_parent(parent),
	m_read(false)
{
}

bool Article::operator==(const Article &other) const
{
	return headline() == other.headline() && address() == other.address();
}

void Article::open()
{
	(void) new KRun(address());
	m_read = true;
}

NewsSourceBase::NewsSourceBase(const Data &nsd, ConfigIface *config)
	: XMLNewsSource(),
	m_data(nsd),
	m_icon(TQPixmap()),
	m_cfg(dynamic_cast<ConfigAccess *>(config)),
	m_newsIconMgr(NewsIconMgr::self())
{
	connect(this, TQT_SIGNAL(loadComplete(XMLNewsSource *, bool)),
			TQT_SLOT(slotProcessArticles(XMLNewsSource *, bool)));
}

void NewsSourceBase::getIcon()
{
	connect(m_newsIconMgr, TQT_SIGNAL(gotIcon(const KURL &, const TQPixmap &)),
			this, TQT_SLOT(slotGotIcon(const KURL &, const TQPixmap &)));
	m_newsIconMgr->getIcon( KURL( m_data.icon ) );
}

TQString NewsSourceBase::newsSourceName() const
{
	if (m_cfg->customNames() || m_name.isEmpty())
		return m_data.name;
	else
		return m_name;
}

TQString NewsSourceBase::subjectText(const Subject subject)
{
	switch (subject) {
		case Arts: return i18n("Arts");
		case Business: return i18n("Business");
		case Computers: return i18n("Computers");
		case Games: return i18n("Games");
		case Health: return i18n("Health");
		case Home: return i18n("Home");
		case Recreation: return i18n("Recreation");
		case Reference: return i18n("Reference");
		case Science: return i18n("Science");
		case Shopping: return i18n("Shopping");
		case Society: return i18n("Society");
		case Sports: return i18n("Sports");
		case Misc: return i18n("Miscellaneous");
		case Magazines: return i18n("Magazines");
		default: return i18n("Unknown");
	}
}

void NewsSourceBase::slotProcessArticles(XMLNewsSource *, bool gotEm)
{
	if (!gotEm) {
		emit invalidInput(this);
		return;
	}

	Article::List oldArticles = m_articles;

	// Truncate the list of articles if necessary.
	m_articles.clear();
	XMLNewsArticle::List::ConstIterator it = XMLNewsSource::articles().begin();
	XMLNewsArticle::List::ConstIterator end = XMLNewsSource::articles().end();
	for (; it != end; ++it)
		m_articles.append(new Article(this, (*it).headline(), (*it).address()));

	// Fill the list with old articles until maxArticles is reached.
	if (m_articles.count() < m_data.maxArticles) {
		Article::List::ConstIterator oldArtIt = oldArticles.begin();
		Article::List::ConstIterator oldArtEnd = oldArticles.end();
		bool isNewArticle;
		for (; oldArtIt != oldArtEnd; ++oldArtIt) {
			isNewArticle = true;
			Article::List::ConstIterator newArtIt = m_articles.begin();
			Article::List::ConstIterator newArtEnd = m_articles.end();
			for (; newArtIt != newArtEnd; ++newArtIt) {
				Article newArt = **newArtIt;
				Article oldArt = **oldArtIt;
				if (newArt == oldArt)
					isNewArticle = false;
			}

			if (isNewArticle)
				m_articles.append(*oldArtIt);
			if (m_articles.count() == m_data.maxArticles)
				break;
		}
	} else
		while (m_articles.count() > m_data.maxArticles)
			m_articles.remove(m_articles.fromLast());

	// Copy the read flag of known articles
	Article::List::ConstIterator oldArtIt = oldArticles.begin();
	Article::List::ConstIterator oldArtEnd = oldArticles.end();
	for (; oldArtIt != oldArtEnd; ++oldArtIt) {
		Article::List::Iterator newArtIt = m_articles.begin();
		Article::List::Iterator newArtEnd = m_articles.end();
		for (; newArtIt != newArtEnd; ++newArtIt)
			if (**oldArtIt == **newArtIt)
				(*newArtIt)->setRead((*oldArtIt)->read());
	}

	emit newNewsAvailable(this, oldArticles != m_articles);
}

Article::Ptr NewsSourceBase::article(const TQString &headline)
{
	Article::List::ConstIterator it = m_articles.begin();
	Article::List::ConstIterator end = m_articles.end();
	for (; it != end; ++it)
		if ((*it)->headline() == headline)
			return *it;

	return 0L;
}

void NewsSourceBase::slotGotIcon(const KURL &url, const TQPixmap &pixmap)
{
	if (url.url() == m_data.icon) {
		m_icon = pixmap;

		disconnect(m_newsIconMgr, TQT_SIGNAL(gotIcon(const KURL &, const TQPixmap &)),
			this, TQT_SLOT(slotGotIcon(const KURL &, const TQPixmap &)));
	}
}

SourceFileNewsSource::SourceFileNewsSource(const NewsSourceBase::Data &nsd,
		ConfigIface *config)
	: NewsSourceBase(nsd, config)
{
}

void SourceFileNewsSource::retrieveNews()
{
	loadFrom(KURL( m_data.sourceFile ));
}

ProgramNewsSource::ProgramNewsSource(const NewsSourceBase::Data &nsd,
		ConfigIface *config) : NewsSourceBase(nsd, config),
	m_program(new KProcess()),
	m_programOutput(0)
{
	connect(m_program, TQT_SIGNAL(processExited(KProcess *)),
			TQT_SLOT(slotProgramExited(KProcess *)));
	connect(m_program, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
			TQT_SLOT(slotGotProgramOutput(KProcess *, char *, int)));

	m_data.sourceFile = KURL(m_data.sourceFile).encodedPathAndQuery();
}

ProgramNewsSource::~ProgramNewsSource()
{
	delete m_program;
	delete m_programOutput;
}

void ProgramNewsSource::retrieveNews()
{
	m_programOutput = new TQBuffer;
	m_programOutput->open(IO_WriteOnly);

	*m_program << m_data.sourceFile;
	m_program->start(KProcess::NotifyOnExit, KProcess::Stdout);
}

void ProgramNewsSource::slotGotProgramOutput(KProcess *, char *data, int length)
{
	m_programOutput->writeBlock(data, length);
}

void ProgramNewsSource::slotProgramExited(KProcess *proc)
{
	bool okSoFar = true;

	TQString errorMsg;

	if (!proc->normalExit()) {
		errorMsg = i18n("<p>The program '%1' was terminated abnormally.<br>This can"
				" happen if it receives the SIGKILL signal.</p>");
		okSoFar = false;
	} else {
		ErrorCode error = static_cast<ErrorCode>(proc->exitStatus());
		if (error != NOERR) {
			errorMsg = errorMessage(error).arg(m_data.sourceFile);
			okSoFar = false;
		}
	}

	if (!okSoFar) {
		TQString output = TQString(m_programOutput->buffer());
		if (!output.isEmpty()) {
			output = TQString::fromLatin1("\"") + output + TQString::fromLatin1("\"");
			errorMsg += i18n("<p>Program output:<br>%1<br>").arg(output);
		}
		KMessageBox::detailedError(0, i18n("An error occurred while updating the"
					" news source '%1'.").arg(newsSourceName()), errorMsg,
				i18n("KNewsTicker Error"));
	}

	processData(m_programOutput->buffer(), okSoFar);

	delete m_programOutput;
	m_programOutput = 0;
}

TQString ProgramNewsSource::errorMessage(const ErrorCode errorCode)
{
	switch (errorCode) {
		case EPERM: return i18n("The program '%1' could not be started at all.");
		case ENOENT: return i18n("The program '%1' tried to read or write a file or"
				" directory which could not be found.");
		case EIO: return i18n("An error occurred while the program '%1' tried to"
				" read or write data.");
		case E2BIG: return i18n("The program '%1' was passed too many arguments."
				" Please adjust the command line in the configuration dialog.");
		case ENOEXEC: return i18n("An external system program upon which the"
				" program '%1' relied could not be executed.");
		case EACCESS: return i18n("The program '%1' tried to read or write a file or"
				" directory but lacks the permission to do so.");
		case ENODEV: return i18n("The program '%1' tried to access a device which"
				" was not available.");
		case ENOSPC: return i18n("There is no more space left on the device used by"
				" the program '%1'.");
		case EROFS: return i18n("The program '%1' tried to create a temporary file"
				" on a read only file system.");
		case ENOSYS: return i18n("The program '%1' tried to call a function which"
				" is not implemented or attempted to access an external resource which"
				" does not exist.");
		case ENODATA: return i18n("The program '%1' was unable to retrieve input data and"
				" was therefore unable to return any XML data.");
		case ENONET: return i18n("The program '%1' tried to access a host which is not"
				" connected to a network.");
		case EPROTO: return i18n("The program '%1' tried to access a protocol which is not"
				" implemented.");
		case EDESTADDRREQ: return i18n("The program '%1' requires you to configure a"
				" destination address to retrieve data from. Please refer to the"
				" documentation of the program for information on how to do that.");
		case ESOCKTNOSUPPORT: return i18n("The program '%1' tried to use a socket"
				" type which is not supported by this system.");
		case ENETUNREACH: return i18n("The program '%1' tried to access an unreachable"
				" network.");
		case ENETRESET: return i18n("The network the program '%1' was trying to access"
				" dropped the connection with a reset.");
		case ECONNRESET: return i18n("The connection of the program '%1' was reset by"
				" peer.");
		case ETIMEDOUT: return i18n("The connection the program '%1' was trying to"
				" establish timed out.");
		case ECONNREFUSED: return i18n("The connection the program '%1' was trying to"
				" establish was refused.");
		case EHOSTDOWN: return i18n("The host the program '%1' was trying to reach is"
				" down.");
		case EHOSTUNREACH: return i18n("The host the program '%1' was trying to reach is"
				" unreachable, no route to host.");
		case ENOEXECBIT: return i18n("KNewsTicker could not execute the program '%1'"
				" because its executable bit was not set. You can mark that program as"
				" executable by executing the following steps:<ul>"
				"<li>Open a Konqueror window and browse to the program</li>"
				"<li>Click on the file with the right mouse button, and select 'Properties'</li>"
				"<li>Open the 'Permissions' tab and make sure that the box in the column"
				" 'Exec' and the row 'User' is checked to ensure that the current user"
				" is allowed to execute that file.</li></ul>");
		case EBADREQ: return i18n("The program '%1' sent a bad request which was not"
				" understood by the server.");
		case ENOAUTH: return i18n("The program '%1' failed to issue an authorization for"
				" an area which needs some form of authorization before it can be"
				" accessed.");
		case EMUSTPAY: return i18n("The program '%1' aborted because it could not access"
				" the data without paying for it.");
		case EFORBIDDEN: return i18n("The program '%1' tried to access a forbidden"
				" source.");
		case ENOTFOUND: return i18n("The program '%1' tried to access data which"
				" could not be found.");
		case ETIMEOUT: return i18n("The HTTP request of the program '%1' timed out.");
		case ESERVERE: return i18n("A server error has been encountered. It is likely"
				" that you cannot do anything about it.");
		case EHTTPNOSUP: return i18n("The HTTP protocol version used by the program"
				" '%1' was not understood by the HTTP server or source.");
		default: return i18n("KNewsTicker was unable to detect the exact reasons for"
				" the error.");
	}
}

#include "newsengine.moc"