Huggle  build:^490^dce1e5c
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
hugglefeedproviderirc.cpp
1 //This program is free software: you can redistribute it and/or modify
2 //it under the terms of the GNU General Public License as published by
3 //the Free Software Foundation, either version 3 of the License, or
4 //(at your option) any later version.
5 
6 //This program is distributed in the hope that it will be useful,
7 //but WITHOUT ANY WARRANTY; without even the implied warranty of
8 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 //GNU General Public License for more details.
10 
11 #include "hugglefeedproviderirc.hpp"
12 
13 using namespace Huggle;
14 
15 HuggleFeedProviderIRC::HuggleFeedProviderIRC()
16 {
17  this->Paused = false;
18  this->Connected = false;
19  this->TcpSocket = NULL;
20  this->thread = NULL;
21 }
22 
23 HuggleFeedProviderIRC::~HuggleFeedProviderIRC()
24 {
25  while (Buffer.count() > 0)
26  {
27  Buffer.at(0)->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
28  Buffer.removeAt(0);
29  }
30  this->Stop();
31  delete this->thread;
32  delete TcpSocket;
33 }
34 
36 {
37  if (this->Connected)
38  {
39  Core::DebugLog("Attempted to start connection which was already started");
40  return false;
41  }
42  TcpSocket = new QTcpSocket(Core::Main);
43  qsrand(QTime::currentTime().msec());
44  TcpSocket->connectToHost(Configuration::IRCServer, Configuration::IRCPort);
45  if (!TcpSocket->waitForConnected())
46  {
47  /// \todo LOCALIZE ME
48  Core::Log("IRC: Connection timeout");
49  TcpSocket->close();
50  delete TcpSocket;
51  TcpSocket = NULL;
52  return false;
53  }
54  Core::Log("IRC: Successfuly connected to irc rc feed");
55  this->TcpSocket->write(QString("USER " + Configuration::IRCNick
56  + QString::number(qrand()) + " 8 * :"
57  + Configuration::IRCIdent + "\n").toUtf8());
58  this->TcpSocket->write(QString("NICK " + Configuration::IRCNick
59  + QString::number(qrand()) + Configuration::UserName.replace(" ", "")
60  + "\n").toUtf8());
61  this->TcpSocket->write(QString("JOIN " + Configuration::Project.IRCChannel + "\n").toUtf8());
62  if (this->thread != NULL)
63  {
64  delete this->thread;
65  }
66  this->thread = new HuggleFeedProviderIRC_t(TcpSocket);
67  this->thread->p = this;
68  this->thread->start();
69  this->Connected = true;
70  return true;
71 }
72 
74 {
75  return this->Connected;
76 }
77 
79 {
80  if (!this->Connected)
81  {
82  return;
83  }
84  if (this->TcpSocket == NULL)
85  {
86  throw new Exception("The pointer to TcpSocket was NULL during Stop() of irc provider");
87  }
88  this->thread->Running = false;
89  this->TcpSocket->close();
90  delete this->TcpSocket;
91  this->TcpSocket = NULL;
92  if (this->thread == NULL)
93  {
94  throw new Exception("The pointer to thread was NULL during Stop() of irc provider");
95  }
96  this->thread->Running = false;
97  this->Connected = false;
98  while (!IsStopped())
99  {
100  Core::Log("Waiting for irc feed provider to stop");
101  Sleeper::usleep(200000);
102  }
103  this->Connected = false;
104 }
105 
106 void HuggleFeedProviderIRC::InsertEdit(WikiEdit *edit)
107 {
108  if (edit == NULL)
109  {
110  throw new Exception("WikiEdit *edit must not be NULL", "void HuggleFeedProviderIRC::InsertEdit(WikiEdit *edit)");
111  }
113  Core::PreProcessEdit(edit);
114  if (Core::Main->Queue1->CurrentFilter->Matches(edit))
115  {
116  this->lock.lock();
117  if (this->Buffer.size() > Configuration::ProviderCache)
118  {
119  while (this->Buffer.size() > (Configuration::ProviderCache - 10))
120  {
121  this->Buffer.at(0)->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
122  this->Buffer.removeAt(0);
123  }
124  Core::Log("WARNING: insufficient space in irc cache, increase ProviderCache size, otherwise you will be loosing edits");
125  }
126  this->Buffer.append(edit);
127  this->lock.unlock();
128  } else
129  {
130  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
131  }
132 }
133 
134 void HuggleFeedProviderIRC::ParseEdit(QString line)
135 {
136  // skip edits if provider is disabled
137  if (Paused)
138  {
139  return;
140  }
141 
142  if (!line.contains(" PRIVMSG "))
143  {
144  return;
145  }
146 
147  line = line.mid(line.indexOf(" PRIVMSG ") + 9);
148 
149  if (!line.contains(":"))
150  {
151  Core::DebugLog("Invalid line (no:):" + line);
152  return;
153  }
154 
155  line = line.mid(line.indexOf(":") + 1);
156 
157  if (!line.contains(QString(QChar(003)) + "07"))
158  {
159  Core::DebugLog("Invalid line (no07):" + line);
160  return;
161  }
162 
163  line = line.mid(line.indexOf(QString(QChar(003)) + "07") + 3);
164 
165  if (!line.contains(QString(QChar(003)) + "14"))
166  {
167  Core::DebugLog("Invalid line (no14):" + line);
168  return;
169  }
170 
171  WikiEdit *edit = new WikiEdit();
172  edit->Page = new WikiPage(line.mid(0, line.indexOf(QString(QChar(003)) + "14")));
173  edit->RegisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
174  edit->UnregisterConsumer(HUGGLECONSUMER_WIKIEDIT);
175 
176  if (!line.contains(QString(QChar(003)) + "4 "))
177  {
178  Core::DebugLog("Invalid line (no:x4:" + line);
179  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
180  return;
181  }
182 
183  line = line.mid(line.indexOf(QString(QChar(003)) + "4 ") + 2);
184  QString flags = line.mid(0, line.indexOf(QChar(003)));
185  edit->Bot = flags.contains("B");
186  edit->NewPage = flags.contains("N");
187  edit->Minor = flags.contains("M");
188 
189  // this below looks like a nasty hack to filter out just what we need
190  // but I will later use all of these actions for something too
191  if (flags.contains("patrol"))
192  {
193  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
194  return;
195  }
196 
197  if (flags.contains("modify"))
198  {
199  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
200  return;
201  }
202 
203  if (flags.contains("reviewed"))
204  {
205  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
206  return;
207  }
208 
209  if (flags.contains("block"))
210  {
211  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
212  return;
213  }
214 
215  if (flags.contains("protect"))
216  {
217  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
218  return;
219  }
220 
221  if (flags.contains("reblock"))
222  {
223  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
224  return;
225  }
226 
227  if (flags.contains("unhelpful"))
228  {
229  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
230  return;
231  }
232 
233  if (flags.contains("helpful"))
234  {
235  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
236  return;
237  }
238 
239  if (flags.contains("approve"))
240  {
241  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
242  return;
243  }
244 
245  if (flags.contains("resolve"))
246  {
247  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
248  return;
249  }
250 
251  if (flags.contains("upload"))
252  {
253  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
254  return;
255  }
256 
257  if (flags.contains("feature"))
258  {
259  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
260  return;
261  }
262 
263  if (flags.contains("noaction"))
264  {
265  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
266  return;
267  }
268 
269  if (flags.contains("selfadd"))
270  {
271  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
272  return;
273  }
274 
275  if (flags.contains("overwrite"))
276  {
277  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
278  return;
279  }
280 
281  if (flags.contains("hit"))
282  {
283  // abuse filter hit
284  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
285  return;
286  }
287 
288  if (flags.contains("create"))
289  {
290  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
291  return;
292  }
293 
294  if (flags.contains("delete"))
295  {
296  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
297  return;
298  }
299 
300  if (flags.contains("move"))
301  {
302  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
303  return;
304  }
305 
306  if (!edit->NewPage)
307  {
308  if (!line.contains("?diff="))
309  {
310  Core::DebugLog("Invalid line (flags: " + flags + ") (no diff):" + line);
311  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
312  return;
313  }
314 
315  line = line.mid(line.indexOf("?diff=") + 6);
316 
317  if (!line.contains("&"))
318  {
319  Core::DebugLog("Invalid line (no &):" + line);
320  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
321  return;
322  }
323 
324  edit->Diff = line.mid(0, line.indexOf("&")).toInt();
325  edit->RevID = line.mid(0, line.indexOf("&")).toInt();
326  }
327 
328  if (!line.contains("oldid="))
329  {
330  Core::DebugLog("Invalid line (no oldid?):" + line);
331  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
332  return;
333  }
334 
335  line = line.mid(line.indexOf("oldid=") + 6);
336 
337  if (!line.contains(QString(QChar(003))))
338  {
339  Core::DebugLog("Invalid line (no termin):" + line);
340  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
341  return;
342  }
343 
344  edit->OldID = line.mid(0, line.indexOf(QString(QChar(003)))).toInt();
345 
346  if (!line.contains(QString(QChar(003)) + "03"))
347  {
348  Core::DebugLog("Invalid line, no user: " + line);
349  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
350  return;
351  }
352 
353  line = line.mid(line.indexOf(QString(QChar(003)) + "03") + 3);
354 
355  if (!line.contains(QString(QChar(3))))
356  {
357  Core::DebugLog("Invalid line (no termin):" + line);
358  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
359  return;
360  }
361 
362  QString name = line.mid(0, line.indexOf(QString(QChar(3))));
363 
364  if (name == "")
365  {
366  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
367  return;
368  }
369 
370  edit->User = new WikiUser(name);
371 
372  if (line.contains(QString(QChar(3)) + " ("))
373  {
374  line = line.mid(line.indexOf(QString(QChar(3)) + " (") + 3);
375  if (line.contains(")"))
376  {
377  QString xx = line.mid(0, line.indexOf(")"));
378  int size = 0;
379  if (xx.startsWith("+"))
380  {
381  xx = xx.mid(1);
382  size = xx.toInt();
383  edit->Size = size;
384  } else if (xx.startsWith("-"))
385  {
386  xx = xx.mid(1);
387  size = xx.toInt() * -1;
388  edit->Size = size;
389  }
390  }
391  }
392 
393  if (line.contains(QString(QChar(3)) + "10"))
394  {
395  line = line.mid(line.indexOf(QString(QChar(3)) + "10"));
396  if (line.contains(QString(QChar(3))))
397  {
398  edit->Summary = line.mid(0, line.indexOf(QString(QChar(3))));
399  }
400  }
401 
402  this->InsertEdit(edit);
403 }
404 
406 {
407  if (this->IsWorking())
408  {
409  return false;
410  }
411  if (this->thread != NULL)
412  {
413  if (this->thread->Running || !this->thread->IsFinished())
414  {
415  return false;
416  }
417  }
418  return true;
419 }
420 
422 {
423  return (this->Buffer.size() != 0);
424 }
425 
426 void HuggleFeedProviderIRC_t::run()
427 {
428  if (this->p == NULL)
429  {
430  this->Stopped = true;
431  throw new Exception("Pointer to parent IRC feed is NULL");
432  }
433  int ping = 2000;
434  while (this->Running && this->s->isOpen())
435  {
436  if (ping < 0)
437  {
438  this->s->write(QString("PING :" + Configuration::IRCServer).toUtf8());
439  ping = 2000;
440  }
441  QString text = QString::fromUtf8(this->s->readLine());
442  if (text == "")
443  {
444  QThread::currentThread()->usleep(2000000);
445  ping -= 100;
446  continue;
447  }
448  Core::DebugLog("IRC Input: " + text, 6);
449  p->ParseEdit(text);
450  QThread::usleep(200000);
451  ping--;
452  }
453  Core::Log("IRC: Closed connection to irc feed");
454  if (this->Running)
455  {
456  p->Connected = false;
457  }
458  this->Stopped = true;
459 }
460 
461 HuggleFeedProviderIRC_t::HuggleFeedProviderIRC_t(QTcpSocket *socket)
462 {
463  this->s = socket;
464  this->Stopped = false;
465  Running = true;
466  this->p = NULL;
467 }
468 
469 HuggleFeedProviderIRC_t::~HuggleFeedProviderIRC_t()
470 {
471  // we must not delete the socket here, that's a job of parent object
472 }
473 
474 bool HuggleFeedProviderIRC_t::IsFinished()
475 {
476  return Stopped;
477 }
478 
480 {
481  this->lock.lock();
482  if (this->Buffer.size() == 0)
483  {
484  return NULL;
485  }
486  WikiEdit *edit = this->Buffer.at(0);
487  this->Buffer.removeAt(0);
488  this->lock.unlock();
489  Core::PostProcessEdit(edit);
490  edit->UnregisterConsumer(HUGGLECONSUMER_PROVIDERIRC);
491  return edit;
492 }
493 
494 bool HuggleFeedProviderIRC::IsConnected()
495 {
496  return Connected;
497 }
int OldID
Old id.
Definition: wikiedit.hpp:103
bool IsWorking()
Return true if this feed is operational or not.
static void Log(QString Message)
Write text to terminal as well as ring log.
Definition: core.cpp:563
bool NewPage
Edit is a new page.
Definition: wikiedit.hpp:95
bool IsStopped()
Returns true in case that a provider is stopped and can be safely deleted.
void Stop()
Stop the feed engine.
static QString UserName
User name.
bool Start()
Start the feed engine.
Thread which process the IRC feed.
int RevID
Revision ID.
Definition: wikiedit.hpp:105
static void PostProcessEdit(WikiEdit *_e)
Definition: core.cpp:774
void UnregisterConsumer(const int consumer)
This function will remove a string which prevent the object from being removed.
Definition: collectable.cpp:68
void RegisterConsumer(const int consumer)
Registers a consumer.
Definition: collectable.cpp:57
bool ContainsEdit()
Check if feed is containing some edits in buffer.
static double EditCounter
Number of edits made since you logged in.
static QString IRCIdent
Ident.
Mediawiki page.
Definition: wikipage.hpp:43
static QString IRCServer
Server.
QString Summary
Summary of edit.
Definition: wikiedit.hpp:110
static void PreProcessEdit(WikiEdit *_e)
Definition: core.cpp:747
static int ProviderCache
Size of feed.
WikiEdit * RetrieveEdit()
Return a last edit from cache or NULL.
static QString IRCNick
Nick.
Every exception raised by huggle is defined by this class.
Definition: exception.hpp:20
bool Minor
Edit is a minor edit.
Definition: wikiedit.hpp:91
int Diff
Diff id.
Definition: wikiedit.hpp:99
static quint16 IRCPort
Port.
static WikiSite Project
currently selected project
WikiUser * User
User who changed the page.
Definition: wikiedit.hpp:89
static void DebugLog(QString Message, unsigned int Verbosity=1)
This log is only shown if verbosity is same or larger than requested verbosity.
Definition: core.cpp:641
int Size
Size of change of edit.
Definition: wikiedit.hpp:97
Wiki edit.
Definition: wikiedit.hpp:67
static MainWindow * Main
Pointer to main.
Definition: core.hpp:111
bool Bot
Edit is a bot edit.
Definition: wikiedit.hpp:93
WikiPage * Page
Page that was changed by edit.
Definition: wikiedit.hpp:87