OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Network2IRC.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 #include "C4Include.h"
18 #include "network/C4Network2IRC.h"
19 
20 #include "game/C4Application.h"
21 #include "config/C4Config.h"
22 #include "C4Version.h"
24 #include "gui/C4Gui.h" // for clearly visi
25 
26 #include <cctype> // for isdigit
27 
28 // Helper for IRC command parameter parsing
29 StdStrBuf ircExtractPar(const char **ppPar)
30 {
31  // No parameter left?
32  if (!ppPar || !*ppPar || !**ppPar)
33  return StdStrBuf("");
34  // Last parameter?
35  StdStrBuf Result;
36  if (**ppPar == ':')
37  {
38  // Reference everything after the double-colon
39  Result.Ref(*ppPar + 1);
40  *ppPar = nullptr;
41  }
42  else
43  {
44  // Copy until next space (or end of string)
45  Result.CopyUntil(*ppPar, ' ');
46  // Go over parameters
47  *ppPar += Result.getLength();
48  if (**ppPar == ' ')
49  (*ppPar)++;
50  else
51  *ppPar = nullptr;
52  }
53  // Done
54  return Result;
55 }
56 
57 // *** C4Network2IRCUser
58 
60  : Name(szName)
61 {
62 
63 }
64 
65 // *** C4Network2IRCChannel
66 
68  : Name(szName), pUsers(nullptr), fReceivingUsers(false)
69 {
70 
71 }
72 
74 {
75  ClearUsers();
76 }
77 
79 {
80  for (C4Network2IRCUser *pUser = pUsers; pUser; pUser = pUser->Next)
81  if (SEqual(pUser->getName(), szName))
82  return pUser;
83  return nullptr;
84 }
85 
86 void C4Network2IRCChannel::OnUsers(const char *szUsers, const char *szPrefixes)
87 {
88  // Find actual prefixes
89  szPrefixes = SSearch(szPrefixes, ")");
90  // Reconstructs the list
91  if (!fReceivingUsers)
92  ClearUsers();
93  while (szUsers && *szUsers)
94  {
95  // Get user name
96  StdStrBuf PrefixedName = ircExtractPar(&szUsers);
97  // Remove prefix(es)
98  const char *szName = PrefixedName.getData();
99  if (szPrefixes)
100  while (strchr(szPrefixes, *szName))
101  szName++;
102  // Copy prefix
103  StdStrBuf Prefix;
104  Prefix.Copy(PrefixedName.getData(), szName - PrefixedName.getData());
105  // Add user
106  AddUser(szName)->SetPrefix(Prefix.getData());
107  }
108  // Set flag the user list won't get cleared again until OnUsersEnd is called
109  fReceivingUsers = true;
110 }
111 
112 void C4Network2IRCChannel::OnUsersEnd()
113 {
114  // Reset flag
115  fReceivingUsers = false;
116 }
117 
118 void C4Network2IRCChannel::OnTopic(const char *szTopic)
119 {
120  Topic = szTopic;
121 }
122 
123 void C4Network2IRCChannel::OnKick(const char *szUser, const char *szComment)
124 {
125  // Remove named user from channel list
126  C4Network2IRCUser *pUser = getUser(szUser);
127  if (pUser) DeleteUser(pUser);
128 }
129 
130 void C4Network2IRCChannel::OnPart(const char *szUser, const char *szComment)
131 {
132  // Remove named user from channel list
133  C4Network2IRCUser *pUser = getUser(szUser);
134  if (pUser) DeleteUser(pUser);
135 }
136 
137 void C4Network2IRCChannel::OnJoin(const char *szUser)
138 {
139  // Add user (do not set prefix)
140  if (!getUser(szUser))
141  AddUser(szUser);
142 }
143 
144 C4Network2IRCUser *C4Network2IRCChannel::AddUser(const char *szName)
145 {
146  // Check if the user already exists
147  C4Network2IRCUser *pUser = getUser(szName);
148  if (pUser) return pUser;
149  // Add to list
150  pUser = new C4Network2IRCUser(szName);
151  pUser->Next = pUsers;
152  pUsers = pUser;
153  return pUser;
154 }
155 
156 void C4Network2IRCChannel::DeleteUser(C4Network2IRCUser *pUser)
157 {
158  // Unlink
159  if (pUser == pUsers)
160  pUsers = pUser->Next;
161  else
162  {
163  C4Network2IRCUser *pPrev = pUsers;
164  while (pPrev && pPrev->Next != pUser)
165  pPrev = pPrev->Next;
166  if (pPrev)
167  pPrev->Next = pUser->Next;
168  }
169  // Delete
170  delete pUser;
171 }
172 
173 void C4Network2IRCChannel::ClearUsers()
174 {
175  while (pUsers)
176  DeleteUser(pUsers);
177 }
178 
179 
180 // *** C4Network2IRCClient
181 // Created statically in C4Application.cpp, refer by &Application.IRCClient
182 
184  : fConnecting(false), fConnected(false),
185  pChannels(nullptr),
186  pLog(nullptr), pLogEnd(nullptr), iLogLength(0), iUnreadLogLength(0),
187  pNotify(nullptr)
188 {
189 
190 }
191 
193 {
194  Close();
195 }
196 
197 void C4Network2IRCClient::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
198 {
199  // Enlarge buffer
200  int iSize = rPacket.getSize(),
201  iPos = rOutBuf.getSize();
202  rOutBuf.Grow(iSize + 2);
203  // Write packet
204  rOutBuf.Write(rPacket, iPos);
205  // Terminate
206  uint8_t *pPos = getMBufPtr<uint8_t>(rOutBuf, iPos + iSize);
207  *pPos = '\r'; *(pPos + 1) = '\n';
208 }
209 
210 size_t C4Network2IRCClient::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
211 {
212  // Find line separation
213  const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
214  if (!pSep)
215  return 0;
216  // Check if it's actually correct separation (rarely the case)
217  int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
218  iLength = iSize - 1;
219  if (iLength && *(pSep - 1) == '\r')
220  iLength--;
221  // Copy the line
222  StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
223  // Ignore prefix
224  const char *pMsg = Buf.getData();
225  StdStrBuf Prefix;
226  if (*pMsg == ':')
227  {
228  Prefix.CopyUntil(pMsg + 1, ' ');
229  pMsg += Prefix.getLength() + 1;
230  }
231  // Strip whitespace
232  while (*pMsg == ' ')
233  pMsg++;
234  // Ignore empty message
235  if (!*pMsg)
236  return iSize;
237  // Get command
238  StdStrBuf Cmd; Cmd.CopyUntil(pMsg, ' ');
239  // Precess command
240  const char *szParameters = SSearch(pMsg, " ");
241  OnCommand(Prefix.getData(), Cmd.getData(), szParameters ? szParameters : "");
242  // Consume the line
243  return iSize;
244 }
245 
246 bool C4Network2IRCClient::OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO)
247 {
248  // Security checks
249  if (!fConnecting || fConnected || AddrConnect != ServerAddr) return false;
250  CStdLock Lock(&CSec);
251  // Save connection data
252  fConnected = true;
253  fConnecting = false;
254  C4Network2IRCClient::PeerAddr = AddrPeer;
255  // Send welcome message
256  if (!Password.isNull())
257  Send("PASS", Password.getData());
258  Send("NICK", Nick.getData());
259  Send("USER", FormatString("clonk x x :%s", RealName.getLength() ? RealName.getData() : " ").getData());
260  // Okay
261  return true;
262 }
263 
264 void C4Network2IRCClient::OnDisconn(const C4NetIO::addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason)
265 {
266  fConnected = false;
267  // Show a message with the reason
268  PushMessage(MSG_Status, "", Nick.getData(), FormatString(LoadResStr("IDS_MSG_DISCONNECTEDFROMSERVER"), szReason).getData());
269 }
270 
271 void C4Network2IRCClient::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
272 {
273  // Won't get called
274 }
275 
277 {
278  return pChannels;
279 }
280 
282 {
283  return pPrevChan ? pPrevChan->Next : pChannels;
284 }
285 
287 {
288  for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
289  if (SEqualNoCase(pChan->getName(), szName))
290  return pChan;
291  return nullptr;
292 }
293 
295 {
296  // Clear log
297  while (iLogLength)
298  PopMessage();
299 }
300 
302 {
303  // set read marker to last message
304  pLogLastRead = pLogEnd;
305  iUnreadLogLength = 0;
306  // message buffer is smaller for messages already read: Remove old ones
307  while (iLogLength > C4NetIRCMaxReadLogLength)
308  PopMessage();
309 }
310 
311 bool C4Network2IRCClient::Connect(const char *szServer, const char *szNick, const char *szRealName, const char *szPassword, const char *szAutoJoin)
312 {
313  // Already connected? Close connection
314  if (fConnecting || fConnected)
315  Close();
316  // Initialize
318  if (!Init())
319  return false;
320  // Resolve address
321  ServerAddr.SetAddress(StdStrBuf(szServer));
322  if (ServerAddr.IsNull())
323  { SetError("Could no resolve server address!"); return false; }
324  ServerAddr.SetDefaultPort(6666);
325  // Set connection data
326  Nick = szNick; RealName = szRealName;
327  Password = szPassword; AutoJoin = szAutoJoin;
328  // Truncate password
329  if (Password.getLength() > 31)
330  Password.SetLength(31);
331  // Start connecting
332  if (!C4NetIOTCP::Connect(ServerAddr))
333  return false;
334  // Reset status data
335  Prefixes = "(ov)@+";
336  // Okay, let's wait for the connection.
337  fConnecting = true;
338  return true;
339 }
340 
342 {
343  // Close network
345  // Save & Clear channels
346  if(pChannels) // Don't override empty
347  {
348  // It's somewhat weird to loop backward through a singly linked list, but it's necessary to keep the order
349  StdStrBuf chanstr;
350  C4Network2IRCChannel * pChan = pChannels;
351  while(pChan->Next)
352  pChan = pChan->Next;
353  chanstr.Append(pChan->getName());
354  while (pChan != pChannels)
355  {
356  C4Network2IRCChannel * pChanPrev = pChannels;
357  while(pChanPrev->Next != pChan)
358  pChanPrev = pChanPrev->Next;
359  pChan = pChanPrev;
360  chanstr.Append(",");
361  chanstr.Append(pChan->getName());
362  }
363  strncpy(Config.IRC.Channel, chanstr.getData(), sizeof(Config.IRC.Channel)-1);
364  }
365  while (pChannels)
366  DeleteChannel(pChannels);
367  // Clear log
368  ClearMessageLog();
369  // Reset flags
370  fConnected = fConnecting = false;
371  return true;
372 }
373 
374 bool C4Network2IRCClient::Send(const char *szCommand, const char *szParameters)
375 {
376  if (!fConnected)
377  { SetError("not connected"); return false; }
378  // Create message
379  StdStrBuf Msg;
380  if (szParameters)
381  Msg.Format("%s %s", szCommand, szParameters);
382  else
383  Msg.Ref(szCommand);
384  // Send
385  return C4NetIOTCP::Send(C4NetIOPacket(Msg.getData(), Msg.getLength(), false, PeerAddr));
386 }
387 
388 bool C4Network2IRCClient::Quit(const char *szReason)
389 {
390  if (!Send("QUIT", FormatString(":%s", szReason).getData()))
391  return false;
392  // Must be last message
393  return Close();
394 }
395 
396 bool C4Network2IRCClient::Join(const char *szChannel)
397 {
398  // Newbie limitation: can only join channels beginning with #clonk
400  if (!SWildcardMatchEx(szChannel, "#*clonk*"))
401  {
402  const char* message = LoadResStr("IDS_ERR_CHANNELNOTALLOWED");
403  PushMessage(MSG_Status, "", "", message);
404  SetError("Joining this channel not allowed");
405  Application.InteractiveThread.ThreadPostAsync<bool>(std::bind(&C4GUI::Screen::ShowMessage, ::pGUI, message, LoadResStr("IDS_DLG_CHAT"), C4GUI::Ico_Error, static_cast<int32_t* const &>(0)));
406  return false;
407  }
408  return Send("JOIN", szChannel);
409 }
410 
411 bool C4Network2IRCClient::Part(const char *szChannel)
412 {
413  return Send("PART", szChannel);
414 }
415 
416 bool C4Network2IRCClient::Message(const char *szTarget, const char *szText)
417 {
418  if (!Send("PRIVMSG", FormatString("%s :%s", szTarget, szText).getData()))
419  return false;
420  PushMessage(MSG_Message, Nick.getData(), szTarget, szText);
421  return true;
422 }
423 
424 bool C4Network2IRCClient::Notice(const char *szTarget, const char *szText)
425 {
426  if (!Send("NOTICE", FormatString("%s :%s", szTarget, szText).getData()))
427  return false;
428  PushMessage(MSG_Notice, Nick.getData(), szTarget, szText);
429  return true;
430 }
431 
432 bool C4Network2IRCClient::Action(const char *szTarget, const char *szText)
433 {
434  if (!Send("PRIVMSG", FormatString("%s :\1ACTION %s\1", szTarget, szText).getData()))
435  return false;
436  PushMessage(MSG_Action, Nick.getData(), szTarget, szText);
437  return true;
438 }
439 
440 bool C4Network2IRCClient::ChangeNick(const char *szNewNick)
441 {
442  return Send("NICK", szNewNick);
443 }
444 
445 bool C4Network2IRCClient::RegisterNick(const char *szPassword, const char *szMail)
446 {
447  return Send("PRIVMSG", FormatString("NickServ :REGISTER %s %s", szPassword, szMail).getData());
448 }
449 
450 void C4Network2IRCClient::OnCommand(const char *szSender, const char *szCommand, const char *szParameters)
451 {
452  CStdLock Lock(&CSec);
453  // Numeric command?
454  if (isdigit((unsigned char)*szCommand) && SLen(szCommand) == 3)
455  {
456  OnNumericCommand(szSender, atoi(szCommand), szParameters);
457  return;
458  }
459  // Sender's nick
460  StdStrBuf SenderNick;
461  if (szSender) SenderNick.CopyUntil(szSender, '!');
462  // Ping?
463  if (SEqualNoCase(szCommand, "PING"))
464  Send("PONG", szParameters);
465  // Message?
466  if (SEqualNoCase(szCommand, "NOTICE") || SEqualNoCase(szCommand, "PRIVMSG"))
467  {
468  // Get target
469  StdStrBuf Target = ircExtractPar(&szParameters);
470  // Get text
471  StdStrBuf Text = ircExtractPar(&szParameters);
472  // Process message
473  OnMessage(SEqualNoCase(szCommand, "NOTICE"), szSender, Target.getData(), Text.getData());
474  }
475  // Channel join?
476  if (SEqualNoCase(szCommand, "JOIN"))
477  {
478  // Get channel
479  StdStrBuf Channel = ircExtractPar(&szParameters);
480  C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
481  // Add user
482  pChan->OnJoin(SenderNick.getData());
483  // Myself?
484  if (SenderNick == Nick)
485  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_YOUHAVEJOINEDCHANNEL"), Channel.getData()).getData());
486  else
487  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_HASJOINEDTHECHANNEL"), SenderNick.getData()).getData());
488  }
489  // Channel part?
490  if (SEqualNoCase(szCommand, "PART"))
491  {
492  // Get channel
493  StdStrBuf Channel = ircExtractPar(&szParameters);
494  C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
495  // Get message
496  StdStrBuf Comment = ircExtractPar(&szParameters);
497  // Remove user
498  pChan->OnPart(SenderNick.getData(), Comment.getData());
499  // Myself?
500  if (SenderNick == Nick)
501  {
502  DeleteChannel(pChan);
503  PushMessage(MSG_Status, szSender, Nick.getData(), FormatString(LoadResStr("IDS_MSG_YOUHAVELEFTCHANNEL"), Channel.getData(), Comment.getData()).getData());
504  }
505  else
506  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_HASLEFTTHECHANNEL"), SenderNick.getData(), Comment.getData()).getData());
507  }
508  // Kick?
509  if (SEqualNoCase(szCommand, "KICK"))
510  {
511  // Get channel
512  StdStrBuf Channel = ircExtractPar(&szParameters);
513  C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
514  // Get kicked user
515  StdStrBuf Kicked = ircExtractPar(&szParameters);
516  // Get message
517  StdStrBuf Comment = ircExtractPar(&szParameters);
518  // Remove user
519  pChan->OnKick(Kicked.getData(), Comment.getData());
520  // Myself?
521  if (Kicked == Nick)
522  {
523  DeleteChannel(pChan);
524  PushMessage(MSG_Status, szSender, Nick.getData(), FormatString(LoadResStr("IDS_MSG_YOUWEREKICKEDFROMCHANNEL"), Channel.getData(), Comment.getData()).getData());
525  }
526  else
527  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_WASKICKEDFROMTHECHANNEL"), Kicked.getData(), Comment.getData()).getData());
528  }
529  // Quit?
530  if (SEqualNoCase(szCommand, "QUIT"))
531  {
532  // Get comment
533  StdStrBuf Comment = ircExtractPar(&szParameters);
534  // Format status message
535  StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_HASDISCONNECTED"), SenderNick.getData(), Comment.getData());
536  // Remove him from all channels
537  for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
538  if (pChan->getUser(SenderNick.getData()))
539  {
540  pChan->OnPart(SenderNick.getData(), "Quit");
541  PushMessage(MSG_Status, szSender, pChan->getName(), Message.getData());
542  }
543  }
544  // Topic change?
545  if (SEqualNoCase(szCommand, "TOPIC"))
546  {
547  // Get channel and topic
548  StdStrBuf Channel = ircExtractPar(&szParameters);
549  StdStrBuf Topic = ircExtractPar(&szParameters);
550  // Set topic
551  AddChannel(Channel.getData())->OnTopic(Topic.getData());
552  // Message
553  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_CHANGESTHETOPICTO"), SenderNick.getData(), Topic.getData()).getData());
554  }
555  // Mode?
556  if (SEqualNoCase(szCommand, "MODE"))
557  {
558  // Get all data
559  StdStrBuf Channel = ircExtractPar(&szParameters);
560  StdStrBuf Flags = ircExtractPar(&szParameters);
561  StdStrBuf What = ircExtractPar(&szParameters);
562  // Make sure it's a channel
563  C4Network2IRCChannel *pChan = getChannel(Channel.getData());
564  if (pChan)
565  // Ask for names, because user prefixes might be out of sync
566  Send("NAMES", Channel.getData());
567  // Show Message
568  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_SETSMODE"), SenderNick.getData(), Flags.getData(), What.getData()).getData());
569  }
570  // Error?
571  if (SEqualNoCase(szCommand, "ERROR"))
572  {
573  // Get message
574  StdStrBuf Message = ircExtractPar(&szParameters);
575  // Push it
576  PushMessage(MSG_Server, szSender, Nick.getData(), Message.getData());
577  }
578  // Nickchange?
579  if (SEqualNoCase(szCommand, "NICK"))
580  {
581  // Get new nick
582  StdStrBuf NewNick = ircExtractPar(&szParameters);
583  // Format status message
584  StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_ISNOWKNOWNAS"), SenderNick.getData(), NewNick.getData());
585  // Rename on all channels
586  for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
587  if (pChan->getUser(SenderNick.getData()))
588  {
589  pChan->OnPart(SenderNick.getData(), "Nickchange");
590  pChan->OnJoin(NewNick.getData());
591  PushMessage(MSG_Status, szSender, pChan->getName(), Message.getData());
592  }
593  // Self?
594  if (SenderNick == Nick)
595  Nick = NewNick;
596  }
597 }
598 
599 void C4Network2IRCClient::OnNumericCommand(const char *szSender, int iCommand, const char *szParameters)
600 {
601  bool fShowMessage = true;
602  // Get target
603  StdStrBuf Target = ircExtractPar(&szParameters);
604  // Handle command
605  switch (iCommand)
606  {
607 
608  case 433: // Nickname already in use
609  {
610  StdStrBuf DesiredNick = ircExtractPar(&szParameters);
611  // Automatically try to choose a new one
612  DesiredNick.AppendChar('_');
613  Send("NICK", DesiredNick.getData());
614  break;
615  }
616 
617  case 376: // End of MOTD
618  case 422: // MOTD missing
619  // Let's take this a sign that the connection is established.
620  OnConnected();
621  break;
622 
623  case 331: // No topic set
624  case 332: // Topic notify / change
625  {
626  // Get Channel name and topic
627  StdStrBuf Channel = ircExtractPar(&szParameters);
628  StdStrBuf Topic = (iCommand == 332 ? ircExtractPar(&szParameters) : StdStrBuf(""));
629  // Set it
630  AddChannel(Channel.getData())->OnTopic(Topic.getData());
631  // Log
632  if (Topic.getLength())
633  PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_TOPICIN"), Channel.getData(), Topic.getData()).getData());
634  }
635  break;
636 
637  case 333: // Last topic change
638  fShowMessage = false; // ignore
639  break;
640 
641  case 353: // Names in channel
642  {
643  // Get Channel name and name list
644  StdStrBuf Junk = ircExtractPar(&szParameters); // ??!
645  StdStrBuf Channel = ircExtractPar(&szParameters);
646  StdStrBuf Names = ircExtractPar(&szParameters);
647  // Set it
648  AddChannel(Channel.getData())->OnUsers(Names.getData(), Prefixes.getData());
649  fShowMessage = false;
650  }
651  break;
652 
653  case 366: // End of names list
654  {
655  // Get Channel name
656  StdStrBuf Channel = ircExtractPar(&szParameters);
657  // Finish
658  AddChannel(Channel.getData())->OnUsersEnd();
659  fShowMessage = false;
660  // Notify
661  if (pNotify) pNotify->PushEvent(Ev_IRC_Message, this);
662  }
663  break;
664 
665  case 4: // Server version
666  fShowMessage = false; // ignore
667  break;
668 
669  case 5: // Server support string
670  {
671  while (szParameters && *szParameters)
672  {
673  // Get support-token.
674  StdStrBuf Token = ircExtractPar(&szParameters);
675  StdStrBuf Parameter; Parameter.CopyUntil(Token.getData(), '=');
676  // Check if it's interesting and safe data if so.
677  if (SEqualNoCase(Parameter.getData(), "PREFIX"))
678  Prefixes.Copy(SSearch(Token.getData(), "="));
679  }
680  fShowMessage = false;
681  }
682  break;
683 
684  }
685  // Show embedded message, if any?
686  if (fShowMessage)
687  {
688  // Check if first parameter is some sort of channel name
689  C4Network2IRCChannel *pChannel = nullptr;
690  if (szParameters && *szParameters && *szParameters != ':')
691  pChannel = getChannel(ircExtractPar(&szParameters).getData());
692  // Go over other parameters
693  const char *pMsg = szParameters;
694  while (pMsg && *pMsg && *pMsg != ':')
695  pMsg = SSearch(pMsg, " ");
696  // Show it
697  if (pMsg && *pMsg)
698  {
699  if (!pChannel)
700  PushMessage(MSG_Server, szSender, Nick.getData(), pMsg + 1);
701  else
702  PushMessage(MSG_Status, szSender, pChannel->getName(), pMsg + 1);
703  }
704  }
705 }
706 
707 void C4Network2IRCClient::OnConnected()
708 {
709 
710  // Set flag
711  fConnected = true;
712 
713  // Try to join channel(s)
714  if (AutoJoin.getLength())
715  Join(AutoJoin.getData());
716 
717 }
718 
719 void C4Network2IRCClient::OnMessage(bool fNotice, const char *szSender, const char *szTarget, const char *szText)
720 {
721  // CTCP tagged data?
722  const char X_DELIM = '\001';
723  if (szText[0] == X_DELIM)
724  {
725  // Process messages (it's very rarely more than one, but the spec allows it)
726  const char *pMsg = szText + 1;
727  while (*pMsg)
728  {
729  // Find end
730  const char *pEnd = strchr(pMsg, X_DELIM);
731  if (!pEnd) pEnd = pMsg + SLen(pMsg);
732  // Copy CTCP query/reply, get tag
733  StdStrBuf CTCP; CTCP.Copy(pMsg, pEnd - pMsg);
734  StdStrBuf Tag; Tag.CopyUntil(CTCP.getData(), ' ');
735  const char *szData = SSearch(CTCP.getData(), " ");
736  StdStrBuf Sender; Sender.CopyUntil(szSender, '!');
737  // Process
738  if (SEqualNoCase(Tag.getData(), "ACTION"))
739  PushMessage(MSG_Action, szSender, szTarget, szData ? szData : "");
740  if (SEqualNoCase(Tag.getData(), "FINGER") && !fNotice)
741  {
742  StdStrBuf Answer;
743  Answer = LoadResStr("IDS_PRC_UNREGUSER"); //ToDo: Anser sth. else
744 
745  Send("NOTICE", FormatString("%s :%cFINGER %s%c",
746  Sender.getData(), X_DELIM,
747  Answer.getData(),
748  X_DELIM).getData());
749  }
750  if (SEqualNoCase(Tag.getData(), "VERSION") && !fNotice)
751  Send("NOTICE", FormatString("%s :%cVERSION " C4ENGINECAPTION ":" C4VERSION ":" C4_OS "%c",
752  Sender.getData(), X_DELIM, X_DELIM).getData());
753  if (SEqualNoCase(Tag.getData(), "PING") && !fNotice)
754  Send("NOTICE", FormatString("%s :%cPING %s%c",
755  Sender.getData(), X_DELIM, szData, X_DELIM).getData());
756  // Get next message
757  pMsg = pEnd;
758  if (*pMsg == X_DELIM) pMsg++;
759  }
760  }
761 
762  // Standard message (not CTCP tagged): Push
763  else
764  PushMessage(fNotice ? MSG_Notice : MSG_Message, szSender, szTarget, szText);
765 
766 }
767 
768 void C4Network2IRCClient::PopMessage()
769 {
770  if (!iLogLength) return;
771  // Unlink message
772  C4Network2IRCMessage *pMsg = pLog;
773  pLog = pMsg->Next;
774  if (!pLog) pLogEnd = nullptr;
775  if (pLogLastRead == pMsg) pLogLastRead = nullptr;
776  // Delete it
777  delete pMsg;
778  iLogLength--;
779  if (iUnreadLogLength > iLogLength) iUnreadLogLength = iLogLength;
780 }
781 
782 void C4Network2IRCClient::PushMessage(C4Network2IRCMessageType eType, const char *szSource, const char *szTarget, const char *szText)
783 {
784  // Create message
785  C4Network2IRCMessage *pMsg = new C4Network2IRCMessage(eType, szSource, szTarget, szText);
786  // Add to list
787  if (pLogEnd)
788  {
789  pLogEnd->Next = pMsg;
790  }
791  else
792  {
793  pLog = pMsg;
794  }
795  pLogEnd = pMsg;
796  // Count
797  iLogLength++;
798  iUnreadLogLength++;
799  while (iLogLength > C4NetIRCMaxLogLength)
800  PopMessage();
801  // Notify
802  if (pNotify)
803  pNotify->PushEvent(Ev_IRC_Message, this);
804 }
805 
806 C4Network2IRCChannel *C4Network2IRCClient::AddChannel(const char *szName)
807 {
808  // Already exists?
809  C4Network2IRCChannel *pChan = getChannel(szName);
810  if (pChan) return pChan;
811  // Create new, insert
812  pChan = new C4Network2IRCChannel(szName);
813  pChan->Next = pChannels;
814  pChannels = pChan;
815  return pChan;
816 }
817 
818 void C4Network2IRCClient::DeleteChannel(C4Network2IRCChannel *pChannel)
819 {
820  // Unlink
821  if (pChannel == pChannels)
822  pChannels = pChannel->Next;
823  else
824  {
825  C4Network2IRCChannel *pPrev = pChannels;
826  while (pPrev && pPrev->Next != pChannel)
827  pPrev = pPrev->Next;
828  if (pPrev)
829  pPrev->Next = pChannel->Next;
830  }
831  // Delete
832  delete pChannel;
833 }
const char * getData() const
Definition: StdBuf.h:450
C4Network2IRCUser * getUser(const char *szName) const
const void * getData() const
Definition: StdBuf.h:107
C4Config Config
Definition: C4Config.cpp:837
Definition: StdBuf.h:37
bool Send(const char *szCommand, const char *szParameters=nullptr)
const int C4NetIRCMaxLogLength
Definition: C4Network2IRC.h:31
bool RegisterNick(const char *szPassword, const char *szMail)
void SetError(const char *strnError, bool fSockErr=false)
Definition: C4NetIO.cpp:754
C4Network2IRCMessageType
Definition: C4Network2IRC.h:22
const char * SSearch(const char *szString, const char *szIndex)
Definition: Standard.cpp:340
bool ThreadPostAsync(Functor function)
virtual bool Connect(const addr_t &addr)
Definition: C4NetIO.cpp:1167
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:184
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:181
bool PushEvent(C4InteractiveEventType eEventType, void *pData=nullptr)
void Grow(size_t iGrow)
Definition: StdBuf.h:179
size_t SLen(const char *sptr)
Definition: Standard.h:78
bool Action(const char *szTarget, const char *szText)
void SetAddress(const sockaddr *addr)
Definition: C4NetIO.cpp:370
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:97
C4Network2IRCChannel * getNextChannel(C4Network2IRCChannel *pPrevChan) const
C4Network2IRCChannel * getChannel(const char *szName) const
size_t getSize() const
Definition: StdBuf.h:109
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void AppendChar(char cChar)
Definition: StdBuf.h:596
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1194
const char * getName() const
C4Network2IRCChannel * getFirstChannel() const
bool Connect(const char *szServer, const char *szNick, const char *szRealName, const char *szPassword=nullptr, const char *szChannel=nullptr)
C4Network2IRCUser(const char *szName)
StdStrBuf ircExtractPar(const char **ppPar)
virtual void SetCallback(CBClass *pnCallback)
Definition: C4NetIO.h:487
bool Part(const char *szChannel)
virtual bool Close()
Definition: C4NetIO.cpp:870
bool SWildcardMatchEx(const char *szString, const char *szWildcard)
Definition: Standard.cpp:606
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
C4ConfigIRC IRC
Definition: C4Config.h:258
bool ChangeNick(const char *szNewNick)
bool Message(const char *szTarget, const char *szText)
bool Quit(const char *szReason)
void CopyUntil(const char *szString, char cUntil)
Definition: StdBuf.h:621
virtual bool Send(const C4NetIOPacket &rPacket)
Definition: C4NetIO.cpp:1266
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
void Write(const void *pnData, size_t inSize, size_t iAt=0)
Definition: StdBuf.h:161
void Ref(const char *pnData)
Definition: StdBuf.h:463
bool isNull() const
Definition: StdBuf.h:449
#define C4_OS
char Channel[CFG_MaxString+1]
Definition: C4Config.h:209
bool Join(const char *szChannel)
const int C4NetIRCMaxReadLogLength
Definition: C4Network2IRC.h:32
virtual bool Init(uint16_t iPort=addr_t::IPPORT_NONE)
Definition: C4NetIO.cpp:824
bool IsNull() const
Definition: C4NetIO.cpp:509
size_t getLength() const
Definition: StdBuf.h:453
void SetLength(size_t iLength)
Definition: StdBuf.h:517
void SetDefaultPort(uint16_t port)
Definition: C4NetIO.cpp:542
bool Notice(const char *szTarget, const char *szText)
int32_t AllowAllChannels
Definition: C4Config.h:211
void Copy()
Definition: StdBuf.h:475
C4Network2IRCChannel(const char *szName)
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
int iSize
Definition: TstC4NetIO.cpp:35
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277