1 package net
.rizon
.acid
.core
;
3 import java
.io
.BufferedWriter
;
5 import java
.io
.FileWriter
;
6 import java
.io
.IOException
;
7 import java
.net
.UnknownHostException
;
8 import java
.sql
.PreparedStatement
;
9 import java
.sql
.ResultSet
;
10 import java
.sql
.SQLException
;
11 import java
.text
.SimpleDateFormat
;
12 import java
.util
.Arrays
;
13 import java
.util
.Calendar
;
14 import java
.util
.Date
;
15 import java
.util
.List
;
16 import java
.util
.logging
.Level
;
18 import net
.rizon
.acid
.conf
.Client
;
19 import net
.rizon
.acid
.conf
.Config
;
20 import net
.rizon
.acid
.sql
.SQL
;
21 import net
.rizon
.acid
.util
.Blowfish
;
22 import net
.rizon
.acid
.util
.ClassLoader
;
23 import net
.rizon
.acid
.util
.Util
;
25 public class Acidictive
extends AcidCore
27 private static final Logger log
= Logger
.getLogger(Acidictive
.class.getName());
28 public static Config conf
;
29 public static long startTime
;
30 public static int syncTime
;
31 public static SQL acidcore_sql
;
32 public static String bold
= "\ 2";
34 public static String loaderBase
= "commands";
35 public static ClassLoader loader
; // loader for the core commands
37 public static String
getVersion()
39 return "acid4 " + Version
.getAttribute("revision");
42 public static void main(String
[] args
)
46 log
.log(Level
.INFO
, getVersion() + " (" + Version
.getFullVersion() + ") starting up...");
50 conf
= (Config
) Config
.load("acidictive.yml", Config
.class);
54 log
.log(Level
.CONFIG
, "Unable to start due to configuration problems", ex
);
58 AcidCore
.start(conf
.uplink
.host
, conf
.uplink
.port
, conf
.serverinfo
.name
, conf
.serverinfo
.description
, conf
.uplink
.pass
, conf
.serverinfo
.id
, conf
.uplink
.ssl
);
60 acidcore_sql
= SQL
.getConnection("acidcore");
61 if (acidcore_sql
== null)
63 log
.log(Level
.SEVERE
, "Unable to get connection for `acidcore`");
67 loader
= new ClassLoader(loaderBase
);
68 if (conf
.clients
!= null)
69 for (Client c
: conf
.clients
)
70 new AcidUser(null, c
);
72 if (conf
.plugins
!= null)
73 for (String s
: conf
.plugins
)
77 Plugin p
= Plugin
.loadPlugin(s
);
78 log
.log(Level
.INFO
, "Loaded plugin " + p
.getName());
82 log
.log(Level
.SEVERE
, "Unable to load plugin " + s
, ex
);
91 catch (UnknownHostException ex
)
93 log
.log(Level
.SEVERE
, "Unable to connect to uplink - unknown host");
96 catch (IOException ex
)
98 log
.log(Level
.SEVERE
, "Unable to connect to uplink", ex
);
105 public static void onStart()
107 startTime
= new Date().getTime();
109 for (String s
: User
.getUsers())
111 User u
= User
.findUser(s
);
112 if (!(u
instanceof AcidUser
))
115 ((AcidUser
) u
).introduce();
121 public static void onNick(User user
)
123 for (Event e
: Event
.getEvents())
124 e
.onUserConnect(user
);
127 public static void onNickChange(User user
, String oldnick
)
129 for (Event e
: Event
.getEvents())
130 e
.onNickChange(user
, oldnick
);
133 public static void onEOB(Server server
)
135 for (Event e
: Event
.getEvents())
139 public static void onJoin(String channel
, User
[] users
)
141 for (Event e
: Event
.getEvents())
142 e
.onJoin(channel
, users
);
145 public static void onPart(String parter
, String channel
)
147 for (Event e
: Event
.getEvents())
148 e
.onPart(parter
, channel
);
151 public static void onQuit(User user
, String msg
)
153 for (Event e
: Event
.getEvents())
157 public static void onServer(Server server
)
159 for (Event e
: Event
.getEvents())
160 e
.onServerLink(server
);
163 public static void onPrivmsg(String creator
, String recipient
, String msg
)
167 User x
= User
.findUser(creator
);
169 boolean stop
= false;
170 for (Event e
: Event
.getEvents())
171 stop
= stop
|| e
.onPrivmsg(creator
, recipient
, msg
);
175 // All channel commands start with .
176 if (recipient
.startsWith("#") && !msg
.startsWith(conf
.general
.command_prefix
))
179 if (x
.getIdentNick().isEmpty())
181 // Only lookup flags for people who are +o and +r
184 // However +o and -r can do some commands (identify!)
185 if (!x
.getSU().isEmpty())
189 // Try to save a certfp if possible
190 //if (!x.getFlags().isEmpty())
191 // Access.addCertFP(x, true);
193 // If +o-r, try to auth via cert; in case services are gone
194 else if (!x
.getCertFP().isEmpty())
196 // There might also be a CertFP for said user to use, check!
199 PreparedStatement ps
= Acidictive
.acidcore_sql
.prepare("SELECT `certfp` FROM `access` WHERE `user` = ?");
200 ps
.setString(1, x
.getSU());
201 ResultSet rs
= Acidictive
.acidcore_sql
.executeQuery(ps
);
202 if (rs
.next() && !rs
.getString("certfp").isEmpty())
204 if (rs
.getString("certfp").equalsIgnoreCase(x
.getCertFP()))
210 for (Event e
: Event
.getEvents())
211 e
.onCommandCertFPMismatch(x
, rs
.getString("certfp"));
215 catch (SQLException ex
)
217 log
.log(Level
.WARNING
, "Unable to load certfp access", ex
);
225 // Not destined for anything else, must be a command.
226 String
[] parts
= msg
.split("\\s+");
227 // strip leading "." if needed
228 String command
= (parts
[0].startsWith(conf
.general
.command_prefix
) ? parts
[0].substring(conf
.general
.command_prefix
.length()) : parts
[0]).toLowerCase();
229 String
[] args
= Arrays
.copyOfRange(parts
, 1, parts
.length
);
233 net
.rizon
.acid
.conf
.Command confCommand
= null;
234 if (recipient
.startsWith("#"))
236 c
= Channel
.findChannel(recipient
);
241 for (String s
: c
.getUsers())
243 User u
= User
.findUser(s
);
244 if (u
instanceof AcidUser
)
246 AcidUser t
= (AcidUser
) u
;
247 net
.rizon
.acid
.conf
.Command cmd
= t
.findConfCommand(command
);
262 int i
= recipient
.indexOf('@');
264 recipient
= recipient
.substring(0, i
);
266 User targ
= User
.findUser(recipient
);
267 if (targ
== null || !(targ
instanceof AcidUser
))
270 to
= (AcidUser
) targ
;
271 confCommand
= to
.findConfCommand(command
);
274 if (confCommand
== null)
278 // If the conf specifies a plugin, use that
279 if (confCommand
.plugin
!= null)
280 p
= Plugin
.findPlugin(confCommand
.plugin
);
281 // Otherwise use the plugin of the target, if any
284 // Use our loader if there is no plugin
285 ClassLoader cl
= p
!= null ? p
.loader
: loader
;
286 Class
<?
> commandClass
= cl
.loadClass(confCommand
.clazz
);
287 if (commandClass
== null)
290 Command cmd
= (Command
) commandClass
.newInstance();
292 if (recipient
.startsWith("#"))
294 // In the proper channels commands can go without access checks. (ish)
295 if (!confCommand
.allowsChannel(recipient
))
300 // Now that we're in the right channel check for just being logged in OR +o OR spoof
301 if (x
.getIdentNick().isEmpty() && !x
.hasMode("o") && !x
.getIP().equals("0"))
306 // Message to me, check priv
309 // Non priv commands can be used by any oper
310 if (confCommand
.privilege
== null)
312 log
.log(Level
.INFO
, "Denied access to " + confCommand
.name
+ " to " + x
+ " [command has no privilege]");
313 // Commands with no priv can not be executed in PM
317 // Explicitly requires no privilege
318 if (confCommand
.privilege
.equals("none"))
320 else if (x
.hasFlags(confCommand
.privilege
) == false)
322 log
.log(Level
.INFO
, "Denied access to " + confCommand
.name
+ " to " + x
+ " [user has no priv]");
327 if (args
.length
< cmd
.GetMinArgs())
329 reply(x
, to
, c
, "Syntax error for \2" +
330 confCommand
.name
+ "\2. Available commands are:");
331 cmd
.onHelp(x
, to
, c
);
335 if (args
.length
> cmd
.GetMaxArgs())
337 String last
= arrayFormat(args
, cmd
.GetMaxArgs() - 1, args
.length
- 1);
338 String
[] parsed_args
= new String
[cmd
.GetMaxArgs()];
339 for (int i
= 0; i
< cmd
.GetMaxArgs() - 1; ++i
)
340 parsed_args
[i
] = args
[i
];
341 parsed_args
[cmd
.GetMaxArgs() - 1] = last
;
347 cmd
.Run(x
, to
, c
, args
);
349 /* log SUCCESSFUL privmsg command usages as well
350 * except help because that's just silly and nobody cares.
352 if (!command
.equalsIgnoreCase("help")
353 && !recipient
.startsWith("#"))
354 privmsg(conf
.getChannelNamed("cmdlogchan"), x
.getNick() + ": " + msg
);
358 log
.log(Level
.WARNING
, "Error running command " + confCommand
.name
, ex
);
363 log
.log(Level
.SEVERE
, "Error processing PRIVMSG: " + msg
, e
);
367 public static void onCtcp(String creator
, String recipient
, String msg
)
369 boolean stop
= false;
371 if (msg
.startsWith("\1VERSION"))
373 User u
= User
.findUser(recipient
);
374 if (u
instanceof AcidUser
)
376 AcidUser au
= (AcidUser
) u
;
377 if (au
.client
.version
!= null)
378 Acidictive
.notice(au
.getUID(), creator
, "\1VERSION " + au
.client
.version
+ "\1");
382 for (Event e
: Event
.getEvents())
384 stop
= stop
|| e
.onCTCP(creator
, recipient
, msg
);
390 public static void onCtcpReply(User source
, String target
, String message
)
392 for (Event e
: Event
.getEvents())
393 e
.onCTCPReply(source
, target
, message
);
396 public static void onNotice(String creator
, String recipient
, String msg
)
398 if (creator
.equalsIgnoreCase("nickserv") && msg
.matches("^.*registered\\s+and\\s+protected.*$"))
400 AcidUser au
= (AcidUser
) User
.findUser(recipient
);
401 if (au
!= null && !au
.getNSPass().isEmpty())
402 Protocol
.privmsg(recipient
, creator
, "IDENTIFY " + au
.getNSPass());
406 boolean stop
= false;
407 for (Event e
: Event
.getEvents())
409 stop
= stop
|| e
.onNotice(creator
, recipient
, msg
);
415 public static void onDie(String msg
)
417 log
.log(Level
.INFO
, msg
);
420 public static void onSync()
422 syncTime
= (int) new Date().getTime() - (int) startTime
;
423 log
.log(Level
.INFO
, "Synced in " + (double) syncTime
/ 1000 + " seconds.");
425 for (Event e
: Event
.getEvents())
429 public static void onKill(final String killer
, User user
, final String reason
)
431 for (Event e
: Event
.getEvents())
432 e
.onKill(killer
, user
, reason
);
435 public static int getUptimeTS()
437 return getTS() - (int) ((double) startTime
/ 1000);
440 public static String
getUptime()
442 return Util
.formatTime(getUptimeTS());
445 public static void onKick(String kicker
, User victim
, String channel
, String reason
)
447 for (Event e
: Event
.getEvents())
448 e
.onKick(kicker
, victim
, channel
, reason
);
450 if (victim
.getServer() == AcidCore
.me
)
452 AcidUser au
= (AcidUser
) victim
;
453 au
.joinChan(channel
);
457 public static void onMode(String creator
, String recipient
, String modes
)
461 public static void onChanMode(String creator
, Channel chan
, String modes
)
463 String
[] x
= modes
.split("\\s+");
469 for (int i
= 0; i
< x
[0].length(); i
++)
471 m
= x
[0].substring(i
, i
+ 1);
475 else if (m
.equals("-"))
477 else if (m
.matches("(b|I|e)"))
479 else if (m
.equals("k"))
482 chan
.setKey(x
[whatnick
]);
489 chan
.setMode(m
.charAt(0));
491 chan
.unsetMode(m
.charAt(0));
493 else if (m
.contains("l"))
497 chan
.setLimit(Integer
.parseInt(x
[whatnick
]));
504 chan
.setMode(m
.charAt(0));
506 chan
.unsetMode(m
.charAt(0));
508 else if (m
.matches("(v|h|o|a|q)"))
511 chan
.setMode(User
.toName(x
[whatnick
]), m
);
513 chan
.remMode(User
.toName(x
[whatnick
]), m
);
520 chan
.setMode(m
.charAt(0));
522 chan
.unsetMode(m
.charAt(0));
527 for (Event e
: Event
.getEvents())
528 e
.onChanModes(creator
, chan
, modes
);
531 public static void setMode(String source
, String target
, String modes
)
533 Channel c
= Channel
.findChannel(target
);
535 onChanMode(source
, c
, modes
);
537 Protocol
.mode(source
, target
, modes
);
540 // The only calling instance, KKill, already checks if user exists
541 public static void skill(User u
, String msg
)
543 Protocol
.kill(u
.getUID(), msg
);
545 onQuit(u
, "Killed (" + me
.getName() + " (" + msg
+ "))");
548 public static void onSquit(Server server
)
550 for (Event e
: Event
.getEvents())
551 e
.onServerDelink(server
);
554 public static void privmsg(final Channel recipient
, String message
)
556 String crypto_pass
= Acidictive
.conf
.getCryptoPass(recipient
.getName());
557 if (crypto_pass
!= null)
559 Blowfish bf
= new Blowfish(crypto_pass
);
560 message
= bf
.Encrypt(message
);
561 log
.log(Level
.FINE
, "Blowfish encrypted:" + message
);
564 Protocol
.privmsg(conf
.general
.control
, recipient
.getName(), message
);
567 public static void privmsg(final User recipient
, String message
)
569 Protocol
.privmsg(conf
.general
.control
, recipient
.getUID(), message
);
572 //@Deprecated This should be deprecated however I need the warnings to stfu atm in eclipse
573 public static void privmsg(final String recipient
, String message
)
575 privmsg(conf
.general
.control
, recipient
, message
);
578 public static void privmsg(String source
, String target
, String message
)
580 if (target
.startsWith("#"))
582 String crypto_pass
= Acidictive
.conf
.getCryptoPass(target
);
583 if (crypto_pass
!= null)
585 Blowfish bf
= new Blowfish(crypto_pass
);
586 message
= bf
.Encrypt(message
);
587 log
.log(Level
.FINE
, "Blowfish encrypted:" + message
);
590 Protocol
.privmsg(source
, target
, message
);
593 public static void notice(final Channel recipient
, final String message
)
595 Protocol
.notice(conf
.general
.control
, recipient
.getName(), message
);
598 public static void notice(final User recipient
, final String message
)
600 Protocol
.notice(conf
.general
.control
, recipient
.getUID(), message
);
603 public static void notice(final String source
, final String recipient
, final String message
)
605 Protocol
.notice(source
, recipient
, message
);
609 public static void notice(final String recipient
, final String message
)
611 Protocol
.notice(conf
.general
.control
, recipient
, message
);
615 public static void sNotice(final String recipient
, final String message
)
617 Protocol
.notice(me
.getSID(), recipient
, message
);
620 static SimpleDateFormat formatter
= new SimpleDateFormat("yyyyMMdd");
622 public static void log(String str
)
624 Calendar now
= Calendar
.getInstance();
625 File f
= new File("logs//acidictive.log." + formatter
.format(now
.getTime()));
628 String date
= "[" + new Date().toString() + "]: ";
629 BufferedWriter print
= createWriter(f
, true);
632 print
.write(date
+ str
);
639 log
.log(Level
.WARNING
, "Unable to write to logfile", e
);
644 public static BufferedWriter
createWriter(File f
, boolean append
)
646 // delete the file if we are told not to append
649 // check if the folder exists, create if it doesn't
650 if (f
.getParentFile() != null && !f
.getParentFile().exists())
651 f
.getParentFile().mkdir();
652 // check if the file exists, create if it doesn't
661 log
.log(Level
.WARNING
, "Unable to create file", e
);
664 // all successful, let's create and return the BufferedWriter
667 return new BufferedWriter(new FileWriter(f
.getPath(), append
));
671 log
.log(Level
.WARNING
, e
.getMessage(), e
);
676 public static void reply(String source
, String target
, String msg
)
678 if (!target
.startsWith("#"))
679 // Reply to PMs in notice, reply to channel in privmsg
680 /* TODO: How the hell do we avoid using notice(String,String)?
681 * Thinking of adding a "Target" interface to make this at least
686 privmsg(target
, msg
);
689 public static void reply(User source
, AcidUser to
, Channel c
, String message
)
692 privmsg(to
.getNick(), c
.getName(), message
);
694 notice(to
.getNick(), source
.getNick(), message
);
697 public static void loadClients(Plugin pkg
, List
<Client
> clients
)
699 for (Client c
: clients
)
701 User u
= User
.findUser(c
.nick
);
704 if (u
instanceof AcidUser
)
706 AcidUser au
= (AcidUser
) u
;
708 // update config and plugin
715 AcidUser au
= new AcidUser(pkg
, c
);