public class muninmxcd {
    public static Logger logger     = Logger.getRootLogger();
    public static Properties p      = null;
    public static LinkedBlockingQueue<BasicDBObject> mongo_queue = new LinkedBlockingQueue<BasicDBObject>();
    public static LinkedBlockingQueue<BasicDBObject> mongo_essential_queue = new LinkedBlockingQueue<BasicDBObject>();
    public static LinkedBlockingQueue<BasicDBObject> mongo_check_queue = new LinkedBlockingQueue<BasicDBObject>();
    public static boolean logMore   = false;
    public static MongoClient m;
    public static DB db;
    public static DBCollection col;
    public static String version    = "1.0 <Codename: Regret>";
    public static Connection conn = null;    
    public static CopyOnWriteArrayList<MuninNode> v_munin_nodes;
    public static CopyOnWriteArrayList<MuninPlugin> v_cinterval_plugins;
    public static Scheduler sched;
    public static Scheduler sched_custom;
    public static Scheduler sched_checks;
    public static CopyOnWriteArrayList<SocketCheck> v_sockets;
    // alerting
    public static LinkedBlockingQueue<PushOverMessage> notification_pushover_queue = new LinkedBlockingQueue<PushOverMessage>();
    public static LinkedBlockingQueue<ShortTextMessage> notification_sms_queue = new LinkedBlockingQueue<ShortTextMessage>();
    public static LinkedBlockingQueue<TTSMessage> notification_tts_queue = new LinkedBlockingQueue<TTSMessage>();  
    public static LinkedBlockingQueue<ReturnServiceCheck> check_error_queue = new LinkedBlockingQueue<ReturnServiceCheck>();
    public static CopyOnWriteArrayList<Alert> v_alerts = new CopyOnWriteArrayList<>();
    // RCA
    public static CopyOnWriteArrayList<Analyzer> v_analyzer = new CopyOnWriteArrayList<>();
    public static int rcajobs_running = 0;
    public static int maxnodes = 100000;
    public static int maxchecks = 100000;
    public static int socketTimeout = 30000;
    // CHECKS
    public static CopyOnWriteArrayList<ServiceCheck> v_serviceChecks = new CopyOnWriteArrayList<>();;
    public static List<Integer> errorProcessing = new CopyOnWriteArrayList<Integer>();
    public static String[] initialArgs;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
       if(args.length < 1)
       {
           System.err.println("Usage: java -jar MuninMXcd.jar <full path to config>");
           System.exit(1);
       }
        
       try
        {
            
            initialArgs = args;
            p = new Properties();
            FileInputStream propInFile = null;
            propInFile = new FileInputStream(args[0]);
            p.loadFromXML(propInFile);            
            String logfile = p.getProperty("log.file");
            socketTimeout = Integer.parseInt(p.getProperty("socket.timeout"));
            
            PatternLayout layout = new PatternLayout("%d{ISO8601} %-5p %m%n");

            ConsoleAppender consoleAppender = new ConsoleAppender( layout );
            logger.addAppender( consoleAppender );
            FileAppender fileAppender = new FileAppender( layout, logfile, false );
            logger.addAppender( fileAppender );

            logger.info("MuninMX Collector Daemon - " + version + " starting up...");
            logger.info("Loading configuration from <"+args[0]+">"  );                    
            
            String l_strLogLevel = p.getProperty("log.level");
            // ALL | DEBUG | INFO | WARN | ERROR | FATAL | OFF:
            if(l_strLogLevel.equals("ALL"))
            {
                logger.setLevel( Level.ALL );
            } 
            else if (l_strLogLevel.equals("DEBUG"))
            {
                logger.setLevel( Level.DEBUG);
            }
            else if (l_strLogLevel.equals("INFO"))
            {
                logger.setLevel( Level.INFO);
            }
            else if (l_strLogLevel.equals("WARN"))
            {
                logger.setLevel( Level.WARN);
            }
            else if (l_strLogLevel.equals("ERROR"))
            {
                logger.setLevel( Level.ERROR);
            }            
            else if (l_strLogLevel.equals("FATAL"))
            {
                logger.setLevel( Level.FATAL);
            }            
            else
            {
                logger.setLevel( Level.OFF);
            }    
            
            if(p.getProperty("log.more") != null)
            {
                if(p.getProperty("log.more").equals("true"))
                {
                    logMore = true;
                }
            }
            
        } catch (Exception ex)
        {
            System.err.println("Failed to Init basic logging infastructure: " + ex.getLocalizedMessage());
            System.exit(1);
        }
       
       
        try
        {
            // connect to db
            logger.info("Connecting to TokuMX");
            MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
            builder.connectionsPerHost(400);
            builder.autoConnectRetry(true);
            builder.threadsAllowedToBlockForConnectionMultiplier(10);
           
            // speed up inserts, we dont care if we miss some if the shit hits the fan
            builder.writeConcern(WriteConcern.NONE);
            m = new MongoClient( new ServerAddress(p.getProperty("mongo.host")),  builder.build());
 
                        
            // connect to mysql
            connectToDatabase(p);
            
            // PreFilling Nodes, max 100 in concurrent
            logger.info("Loading initial MuninNode details. This can take a few minutes...");
            v_munin_nodes = new CopyOnWriteArrayList<>();
            v_cinterval_plugins = new CopyOnWriteArrayList<>();     
            v_sockets = new CopyOnWriteArrayList<>();
            
            java.sql.Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * FROM nodes");
            while(rs.next())
            {
                MuninNode mn = new MuninNode();
                mn.setHostname(rs.getString("hostname"));
                mn.setNodename(rs.getString("hostname"));
                mn.setNode_id(rs.getInt("id"));
                mn.setPort(rs.getInt("port"));
                mn.setUser_id(rs.getInt("user_id"));
                mn.setQueryInterval(rs.getInt("query_interval"));
                mn.setStr_via(rs.getString("via_host"));
                mn.setAuthpw(rs.getString("authpw"));
                mn.setGroup(rs.getString("groupname"));
                v_munin_nodes.add(mn);              
                logger.info("* " + mn.getHostname() + " queued for pluginfetch");
            }
            
            // launching quartz scheduler
            logger.info("Launching Scheduler");
            SchedulerFactory sf = new StdSchedulerFactory("quartz.properties");
            sched = sf.getScheduler();   
            sched.start();   
 
            // launching quartz scheduler for custom interval
            logger.info("Launching Custom Interval Scheduler");
            SchedulerFactory sfc = new StdSchedulerFactory("customquartz.properties");
            sched_custom = sfc.getScheduler();   
            sched_custom.start();
            
            // starting API server
            new Thread(new JettyLauncher()).start();
            
            int sleepTime = Integer.parseInt(p.getProperty("startup.sleeptime"));
            int startupIterations = Integer.parseInt(p.getProperty("startup.iterations"));
            // scheduling jobs
            int i = 0;
            for (MuninNode it_mn : v_munin_nodes) {
                if(i == startupIterations)
                {
                    Thread.sleep(sleepTime);
                    i = 0;
                    logger.info("Waiting "+sleepTime+"ms for new scheduling slot");
                }
                scheduleJob(it_mn);
                i++;
            }
            
            // schedule custom interval jobs
            dbScheduleAllCustomJobs();
            
            // add all alerts
            dbAddAllAlerts();
            
            // Service Checks
            logger.info("Launching Service Check Scheduler");
            SchedulerFactory sfsc = new StdSchedulerFactory("checksquartz.properties");
            sched_checks = sfsc.getScheduler();   
            sched_checks.start();             
            // load service checks from database
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT * FROM service_checks");
            while(rs.next())
            {
                Gson gson = new Gson();
                ServiceCheck tc = gson.fromJson(rs.getString("json"), ServiceCheck.class);
                tc.setCid(rs.getInt("id"));
                tc.setUser_id(rs.getInt("user_id"));
                v_serviceChecks.add(tc);
                logger.info("* " + tc.getCheckname() + " Service Check added");
            }            
            // queue service checks
            for (ServiceCheck it_sc : v_serviceChecks) {
                scheduleServiceCheck(it_sc);
            }            
            
            // starting MongoExecutor
            new Thread(new MongoExecutor()).start();
        
            // starting MongoExecutor for Package Tracking and Essential Informations
            new Thread(new MongoEssentialExecutor()).start();            

            // starting MongoExecutor for Service Checks
            new Thread(new MongoCheckExecutor()).start();            

            // starting newnodewatcher
            new Thread(new NewNodeWatcher()).start();
            
            // start pushover sending message
            new Thread(new PushOverLimiter()).start();
            
            // SMS Limiter
            new Thread(new SMSLimiter()).start();

            // TTS Limiter
            new Thread(new TTSLimiter()).start();            
            
            // start DataRetention Worker
            new Thread(new DataRetentionWorker()).start();
            
            // start Error Notify Inspector
            new Thread(new ErrorNotifyExecutor()).start();            
            
            int curTime;
            int toTime;
            int mb = 1024*1024;
            while(true)
            {
                Thread.sleep(5000);
                System.out.println("Mongo Queue Size: " + mongo_queue.size());
                System.out.println("Mongo Check Queue Size: " + mongo_check_queue.size());
                System.out.println("Mongo Essential Queue Size: " + mongo_essential_queue.size());

                Runtime runtime = Runtime.getRuntime();
                //Print used memory
                System.out.println("Used Memory:"
                    + (runtime.totalMemory() - runtime.freeMemory()) / mb);

                //Print free memory
                System.out.println("Free Memory:"
                    + runtime.freeMemory() / mb);

                //Print total available memory
                System.out.println("Total Memory:" + runtime.totalMemory() / mb);

                //Print Maximum available memory
                System.out.println("Max Memory:" + runtime.maxMemory() / mb);    
                System.out.println(" ");                
                
                if(p.getProperty("kill.sockets").equals("true"))
                {
                    System.out.println("Sockets: " + v_sockets.size());
                    // check for sockets that we can kill
                    curTime = getUnixtime();
                    for(SocketCheck sc : v_sockets)
                    {   
                        toTime = curTime - 120;
                        if(sc.getSocketCreated() < toTime)
                        {
                            if(!sc.getSocket().isClosed())
                            {
                                logger.info("timing out socket... from: " + sc.getHostname());
                                sc.closeSocket();
                                v_sockets.remove(sc);
                            }
                            else
                            {
                                v_sockets.remove(sc);
                            }
                        }
                    }
                }
                
            }
        } catch (Exception ex)
        {
            System.err.println("Something went wrong as fuck: " + ex.getLocalizedMessage());
            logger.fatal("Something went wrong as fuck. exiting: " + ex.getLocalizedMessage());
            ex.printStackTrace();
            System.exit(1);            
        }
    }
    
    
    // Establish a connection to the database
    private static boolean connectToDatabase(Properties p)
    {
        try {
            logger.info("Connecting to MySQL");
            conn =
               DriverManager.getConnection("jdbc:mysql://"+p.getProperty("mysql.host")+":"+p.getProperty("mysql.port")+"/"+p.getProperty("mysql.db")+"?" +
                                           "user="+p.getProperty("mysql.user")+"&password="+p.getProperty("mysql.pass")+"&autoReconnect=true&failOverReadOnly=false&maxReconnects=10");

            return(true);

        } catch (Exception ex) {
            // handle any errors
            logger.fatal("Error connecting to database: " + ex.getMessage());
            return(false);
        }
    }    
} public class PushOverMessage {
    private String userKey;
    private String title;
    private String message;
    
    public PushOverMessage(String p_userKey, String p_title, String p_message)
    {
        userKey = p_userKey;
        title = p_title;
        message = p_message;
    }

    /**
     * @return the userKey
     */
    public String getUserKey() {
        return userKey;
    }

    /**
     * @param userKey the userKey to set
     */
    public void setUserKey(String userKey) {
        this.userKey = userKey;
    }

    /**
     * @return the title
     */
    public String getTitle() {
        return title;
    }

    /**
     * @param title the title to set
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * @return the message
     */
    public String getMessage() {
        return message;
    }

    /**
     * @param message the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }
} public class ShortTextMessage {
    private String message;
    private String mobile;
    
    public ShortTextMessage(String p_message, String p_mobile)
    {
        message = p_message;
        mobile = p_mobile;
    }

    /**
     * @return the message
     */
    public String getMessage() {
        return message;
    }

    /**
     * @param message the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * @return the mobile
     */
    public String getMobile() {
        return mobile;
    }

    /**
     * @param mobile the mobile to set
     */
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
} public class Generic {
      public static ServiceCheck returnServiceCheck(Integer p_cid)
      {
          for (ServiceCheck l_sc : com.clavain.muninmxcd.v_serviceChecks) {
              if(l_sc.getCid().equals(p_cid))
              {
                  return l_sc;
              }
          }
          return null;

    }
      
    public static boolean checkIsProcessing(Integer cid)
    {
        boolean retval = false;
        
        if(com.clavain.muninmxcd.errorProcessing.size() < 1)
        {
            return false;
        }
        
        Iterator<Integer> it = com.clavain.muninmxcd.errorProcessing.iterator();

        while(it.hasNext())
        {
            Integer check = it.next();
            if(cid == check)
            {
                return true;
            }
        }
        return retval;
    }
    
    public static String getHumanReadableDateFromTimeStampWithTimezone(long ts,String Timezone)
    {
         Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(Timezone));
         cal.setTimeInMillis(ts * 1000);
         String retval = getConvertedCalendar(cal);
         retval = retval + " " + Timezone;
         return retval;
    }
    
    public static String getConvertedCalendar(Calendar calendar) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        sdf.setTimeZone(calendar.getTimeZone());
        return sdf.format(calendar.getTime());  
    }        
    
    public static int sendPost(String p_url, String p_data) {
        int retval = 0;
        try {
            URL obj = new URL(p_url);
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();

            //add reuqest header
            con.setRequestMethod("POST");
            con.setRequestProperty("User-Agent", "MuninMX Collector");
            con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
           
            String urlParameters = p_data;

            // Send post request
            con.setDoOutput(true);
            DataOutputStream wr = new DataOutputStream(con.getOutputStream());
            wr.writeBytes(urlParameters);
            wr.flush();
            wr.close();

            int responseCode = con.getResponseCode();
            retval = responseCode;
            com.clavain.muninmxcd.logger.info("sendPost executed to " + p_url + " Response Code : " + responseCode);

            BufferedReader in = new BufferedReader(
                    new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
        } catch (Exception ex) {
            com.clavain.muninmxcd.logger.warn("Error in sendPost " + ex.getLocalizedMessage());
        }
        return retval;
    }

    /**
     * return unix timestamp
     */
    public static int getUnixtime() {
        return (int) (System.currentTimeMillis() / 1000L);
    }

    public static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }       

    /**
     * Return a MuninNode by Nodename or Hostname
     *
     */
    public static MuninNode getMuninNode(String p_strNodename) {
        for (MuninNode l_mn : com.clavain.muninmxcd.v_munin_nodes) {
            if (l_mn.getNodename().equals(p_strNodename) || l_mn.getHostname().equals(p_strNodename)) {
                return l_mn;
            }
        }
        return null;
    }

    /**
     * Return a MuninPlugin for custom jobs by Custom ID
     *
     * @param customId
     * @return
     */
    public static MuninPlugin getMuninPluginForCustomJob(Integer customId) {
        Iterator it = com.clavain.muninmxcd.v_cinterval_plugins.iterator();
        while (it.hasNext()) {
            MuninPlugin l_mp = (MuninPlugin) it.next();
            if (l_mp.getCustomId().equals(customId)) {
                return l_mp;
            }
        }
        com.clavain.muninmxcd.logger.error("getMuninPluginForCustomJob: Cant find plugin for custom id: " + customId);
        return null;
    }

    
    public static String getNodeHostNameForMuninNode(Integer nodeId)
    {
        MuninNode mn = getMuninNode(nodeId);
        if(mn == null)
        {
            return "";
        }
        else
        {
            String r_hostname = mn.getHostname();
            return r_hostname;
        }
    }
    
    public static ArrayList<Alert> getAlertByNidPluginAndGraph(Integer p_nid,String p_strPlugin, String p_strGraph)
    {
        ArrayList<Alert> retval = new ArrayList<>();
        for (Alert l_av : com.clavain.muninmxcd.v_alerts) {
            if (l_av.getNode_id().equals(p_nid)) {      
                if(l_av.getPluginName().trim().equals(p_strPlugin) && l_av.getGraphName().trim().equals(p_strGraph))
                {
                    retval.add(l_av);
                }
            }
        }     

        return retval;

    }
    
    /**
     * Return a MuninNode by Node ID
     *
     * @param nodeId
     * @return
     */
    public static MuninNode getMuninNode(Integer nodeId) {
        Iterator it = v_munin_nodes.iterator();
        while (it.hasNext()) {
            MuninNode l_mn = (MuninNode) it.next();
            if (l_mn.getNode_id().equals(nodeId)) {
                return l_mn;
            }
        }
        com.clavain.muninmxcd.logger.warn("getMuninNode: Cant find nodeId " + nodeId);
        return null;
    }

    public static boolean isPluginIgnored(String pluginName) {
        boolean retval = false;
        if (p.getProperty("ignore.plugins") != null) {
            StringTokenizer st = new StringTokenizer(p.getProperty("ignore.plugins"), ",");
            while (st.hasMoreElements()) {
                if (st.nextElement().toString().equals(pluginName)) {
                    return true;
                }
            }
        }
        return retval;
    }

    public static long getStampFromTimeAndZone(String time, String zone) {
        long retval = 0;
        ///usr/local/bin/php /opt/pcvd/getTime.php
        List<String> commands = new ArrayList<String>();
        commands.add("/usr/bin/php");
        commands.add("/opt/muninmx/getTime.php");
        commands.add(zone);
        commands.add(time);
        try {
            ProcessBuilder pb = new ProcessBuilder(commands);
            Process p = pb.start();
            InputStream is = p.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            // print output
            String str_output = null;
            String str_parse = null;
            while ((str_output = reader.readLine()) != null) {
                //logger.info(str_output);
                str_parse = str_output;
            }
            com.clavain.muninmxcd.logger.info("getTime.php returned: " + str_parse);
            retval = Long.parseLong(str_parse);
        } catch (Exception ex) {
            com.clavain.muninmxcd.logger.error("Error in getStampFromTimeAndZone:" + ex.getLocalizedMessage());
        }
        return retval;
    }
} public class TTSMessage {
    private String message;
    private String mobile;
    
    public TTSMessage(String p_message, String p_mobile)
    {
        message = p_message;
        mobile = p_mobile;
    }

    /**
     * @return the message
     */
    public String getMessage() {
        return message;
    }

    /**
     * @param message the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * @return the mobile
     */
    public String getMobile() {
        return mobile;
    }

    /**
     * @param mobile the mobile to set
     */
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
} public static Logger logger     = Logger.getRootLogger(); public static Properties p      = null; public class Helpers {

    public static void withdrawSMSTicket(Integer user_id) {
        try {
         Connection conn = connectToDatabase(p);   
         java.sql.Statement stmt = conn.createStatement();
         stmt.executeUpdate("UPDATE users SET sms_tickets = sms_tickets -1 WHERE id = " + user_id);
         conn.close();
        } catch (Exception ex) {
            com.clavain.muninmxcd.logger.error("Error in withdrawSMS Ticket for User: " + user_id + " with: " + ex.getLocalizedMessage());
        }
    }
    
    public static void withdrawTTSTicket(Integer user_id)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();            
            stmt.executeUpdate("UPDATE users SET tts_tickets = tts_tickets -1 WHERE id = " + user_id);
            conn.close();
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in withdrawTTS Ticket for User: " + user_id + " with: " + ex.getLocalizedMessage());
        }
    }  
    
    public static int getTTSTicketCount(Integer user_id)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * from users WHERE id = " + user_id);
            while(rs.next())
            {
                return rs.getInt("tts_tickets");
            }    
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in retrieving TTSTicket Count for User: " + user_id + " with: " + ex.getLocalizedMessage());
        }
        return 0;
    }    
   
    public static int getSMSTicketCount(Integer user_id)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * from users WHERE id = " + user_id);
            while(rs.next())
            {
                return rs.getInt("sms_tickets");
            }
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in retrieving SMSTicket Count for User: " + user_id + " with: " + ex.getLocalizedMessage());
        }
        return 0;
    }      
    
    public static String getCustomerEMail(Integer user_id)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * from users WHERE id = " + user_id);
            while(rs.next())
            {
                return rs.getString("email");
            }
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in retrieving SMSTicket Count for User: " + user_id + " with: " + ex.getLocalizedMessage());
        }
        return null;        
    }  
    
    public static void updateNotificationLog(Integer cid, Integer contact_id, String msg, String msg_type)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();
            stmt.executeUpdate("INSERT INTO notification_log (cid,contact_id,msg,msg_type) VALUES ("+cid+","+contact_id+",'"+msg+"','"+msg_type+"')");
            conn.close();
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in updateNotification Log : " + ex.getLocalizedMessage());
        }   
    }
    
     public static void updateCheckNotificationLog(Integer cid, Integer contact_id, String msg, String msg_type)
    {
        try 
        {
            Connection conn = connectToDatabase(p);   
            java.sql.Statement stmt = conn.createStatement();
            stmt.executeUpdate("INSERT INTO check_notification_log (cid,contact_id,msg,msg_type) VALUES ("+cid+","+contact_id+",'"+msg+"','"+msg_type+"')");
            conn.close();
        } catch (Exception ex)
        {
            com.clavain.muninmxcd.logger.error("Error in updateCheckNotification Log : " + ex.getLocalizedMessage());
        }   
    }
    
    public static boolean removeAlert(Integer p_alertId)
    {
        boolean retval = false;
        for (Alert l_av : com.clavain.muninmxcd.v_alerts) {
            if(l_av.getAlert_id().equals(p_alertId))
            {
                retval = com.clavain.muninmxcd.v_alerts.remove(l_av);
            }
        }
        return retval;
    }
    
    public static boolean addAlert(Integer p_alertId)
    {
        return dbAddAllAlertWithId(p_alertId);
    }    
} public class ReturnServiceCheck {
    private Integer returnValue;
    private List<String> output;
    private Integer user_id;
    private Integer cid;
    private Integer checktime;
    private Integer downTimeConfirmedAt;
    private Integer lastDownTimeConfirm;
    private String checktype;
    private String checkname;
    private Integer interval;
    private Integer notifydown = 0;
    private Integer notifyagain = 0;
    private Integer notifyifup = 0;
    private Integer notifyflap = 0;
    private String json;
    
    /**
     * @return the returnValue
     */
    public Integer getReturnValue() {
        return returnValue;
    }

    /**
     * @param aReturnValue the returnValue to set
     */
    public void setReturnValue(Integer aReturnValue) {
        returnValue = aReturnValue;
    }

    /**
     * @return the output
     */
    public List<String> getOutput() {
        return output;
    }

    /**
     * @param aOutput the output to set
     */
    public void setOutput(List<String> aOutput) {
        output = aOutput;
    }
    
    public ReturnServiceCheck(Integer p_iReturnValue,List<String> p_returnOutput)
    {
        returnValue = p_iReturnValue;
        output = p_returnOutput;
    }

    /**
     * @return the user_id
     */
    public Integer getUser_id() {
        return user_id;
    }

    /**
     * @param user_id the user_id to set
     */
    public void setUser_id(Integer user_id) {
        this.user_id = user_id;
    }

    /**
     * @return the cid
     */
    public Integer getCid() {
        return cid;
    }

    /**
     * @param cid the cid to set
     */
    public void setCid(Integer cid) {
        this.cid = cid;
    }

    /**
     * @return the checktime
     */
    public Integer getChecktime() {
        return checktime;
    }

    /**
     * @param checktime the checktime to set
     */
    public void setChecktime(Integer checktime) {
        this.checktime = checktime;
    }

    /**
     * @return the downTimeConfirmedAt
     */
    public Integer getDownTimeConfirmedAt() {
        return downTimeConfirmedAt;
    }

    /**
     * @param downTimeConfirmedAt the downTimeConfirmedAt to set
     */
    public void setDownTimeConfirmedAt(Integer downTimeConfirmedAt) {
        this.downTimeConfirmedAt = downTimeConfirmedAt;
    }

    /**
     * @return the lastDownTimeConfirm
     */
    public Integer getLastDownTimeConfirm() {
        return lastDownTimeConfirm;
    }

    /**
     * @param lastDownTimeConfirm the lastDownTimeConfirm to set
     */
    public void setLastDownTimeConfirm(Integer lastDownTimeConfirm) {
        this.lastDownTimeConfirm = lastDownTimeConfirm;
    }

    /**
     * @return the checktype
     */
    public String getChecktype() {
        return checktype;
    }

    /**
     * @param checktype the checktype to set
     */
    public void setChecktype(String checktype) {
        this.checktype = checktype;
    }

    /**
     * @return the checkname
     */
    public String getCheckname() {
        return checkname;
    }

    /**
     * @param checkname the checkname to set
     */
    public void setCheckname(String checkname) {
        this.checkname = checkname;
    }

    /**
     * @return the interval
     */
    public Integer getInterval() {
        return interval;
    }

    /**
     * @param interval the interval to set
     */
    public void setInterval(Integer interval) {
        this.interval = interval;
    }

    /**
     * @return the notifydown
     */
    public Integer getNotifydown() {
        return notifydown;
    }

    /**
     * @param notifydown the notifydown to set
     */
    public void setNotifydown(Integer notifydown) {
        this.notifydown = notifydown;
    }

    /**
     * @return the notifyagain
     */
    public Integer getNotifyagain() {
        return notifyagain;
    }

    /**
     * @param notifyagain the notifyagain to set
     */
    public void setNotifyagain(Integer notifyagain) {
        this.notifyagain = notifyagain;
    }

    /**
     * @return the notifyifup
     */
    public Integer getNotifyifup() {
        return notifyifup;
    }

    /**
     * @param notifyifup the notifyifup to set
     */
    public void setNotifyifup(Integer notifyifup) {
        this.notifyifup = notifyifup;
    }

    /**
     * @return the notifyflap
     */
    public Integer getNotifyflap() {
        return notifyflap;
    }

    /**
     * @param notifyflap the notifyflap to set
     */
    public void setNotifyflap(Integer notifyflap) {
        this.notifyflap = notifyflap;
    }

    /**
     * @return the json
     */
    public String getJson() {
        return json;
    }

    /**
     * @param json the json to set
     */
    public void setJson(String json) {
        this.json = json;
    }
    
    
} public static int sendPost(String p_url, String p_data) {
    int retval = 0;
    try {
        URL obj = new URL(p_url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        //add reuqest header
        con.setRequestMethod("POST");
        con.setRequestProperty("User-Agent", "MuninMX Collector");
        con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
           
        String urlParameters = p_data;

        // Send post request
        con.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes(urlParameters);
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();
        retval = responseCode;
        com.clavain.muninmxcd.logger.info("sendPost executed to " + p_url + " Response Code : " + responseCode);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
    } catch (Exception ex) {
        com.clavain.muninmxcd.logger.warn("Error in sendPost " + ex.getLocalizedMessage());
    }
    return retval;
} public static Connection connectToDatabase(Properties p)
{
    Connection conn;
    try {
        logger.debug("Connecting to MySQL");
        conn =
           DriverManager.getConnection("jdbc:mysql://"+p.getProperty("mysql.host")+":"+p.getProperty("mysql.port")+"/"+p.getProperty("mysql.db")+"?" +
                                       "user="+p.getProperty("mysql.user")+"&password="+p.getProperty("mysql.pass")+"&autoReconnect=true&failOverReadOnly=false&maxReconnects=10");

        return(conn);

    } catch (Exception ex) {
        // handle any errors
        logger.fatal("Error connecting to database: " + ex.getMessage());
        return(null);
    }
}   public static Logger logger     = Logger.getRootLogger();
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.clavain.muninmxcd;
import com.clavain.alerts.msg.PushOverMessage;
import com.clavain.alerts.msg.ShortTextMessage;
import java.sql.ResultSet;
import java.util.Calendar;
import java.util.GregorianCalendar;
import net.pushover.client.MessagePriority;
import net.pushover.client.PushoverClient;
import net.pushover.client.PushoverException;
import net.pushover.client.PushoverMessage;
import net.pushover.client.PushoverRestClient;
import net.pushover.client.Status;
import static com.clavain.utils.Generic.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import com.clavain.alerts.msg.TTSMessage;
import java.lang.reflect.Modifier;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.SimpleEmail;
import static com.clavain.muninmxcd.logger;
import static com.clavain.muninmxcd.p;
import static com.clavain.alerts.Helpers.*;
import com.clavain.checks.ReturnServiceCheck;
import static com.clavain.utils.Generic.sendPost;
import java.sql.Connection;
import static com.clavain.utils.Database.connectToDatabase;
import java.util.*; 
import com.twilio.sdk.*; 
import com.twilio.sdk.resource.factory.*; 
import com.twilio.sdk.resource.instance.*; 
import com.twilio.sdk.resource.list.*;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import static com.clavain.muninmxcd.logger;
                                title = "This is a PingReports Alert: The Servicecheck: " + sc.getCheckname() + " with type: "+sc.getChecktype()+" is in alert state.";                            
                                logger.info("[Notifications " + cid +"] " + rs.getString("contact_name") + " Initiating TTS Call");
                                sendTTS(title, message ,rs.getString("contact_mobile_nr"),rs.getInt("user_id"));
                                updateCheckNotificationLog(cid, contact_id, "Text2Speech Call initiated to "+rs.getString("contact_mobile_nr"), "tts"); 
                            }
                            if(rs.getInt("email_active") == 1)
                            {
                                logger.info("[Check Notifications " + cid +"] " + rs.getString("contact_name") + " Sending E-Mail");
                                String ENDL = System.getProperty("line.separator");
                                message = "Service Downtime verified @ "+failTime+"."+ENDL+ENDL+"Details:" + ENDL + ENDL + sc.getOutput().get(0);
                                sendMail(title, message,rs.getString("contact_email"));
                                updateCheckNotificationLog(cid, contact_id, "E-Mail send to "+rs.getString("contact_email"), "email"); 
                            }   
                            if(rs.getInt("sms_active") == 1)
                            {
                                title = sc.getCheckname() + "("+sc.getChecktype()+")";
                                message = sc.getOutput().get(0);
                                logger.info("[Check Notifications " + cid +"] " + rs.getString("contact_name") + " Sending SMS");
                                sendSMS(title, message,rs.getString("contact_mobile_nr"),rs.getInt("user_id"));
                                updateCheckNotificationLog(cid, contact_id, "SMS send to "+rs.getString("contact_mobile_nr"), "sms"); 
                            }    
                            if(rs.getInt("pushover_active") == 1)
                            {
                                logger.info("[Check Notifications " + cid +"] " + rs.getString("contact_name") + " Sending Pushover Notification");
                                sendPushover(title, message ,rs.getString("pushover_key"));
                                updateCheckNotificationLog(cid, contact_id, "PushOver Message send to "+rs.getString("pushover_key"), "pushover"); 
                            }  

                     }
                     else
                     {
                        logger.info("[Check Notifications " + cid +"] " + rs.getString("contact_name") + " disabled notifications for this timerange - skipping contact");
                     }
                 }
             }
            
        } catch (Exception ex)
        {
            logger.error("Error in sendNotifications for CID " + cid + " : " + ex.getLocalizedMessage());
            ex.printStackTrace();
        }
    }    
      
    
    public static void sendNotifications(Alert alert) {
        Integer aid = alert.getAlert_id();

        try {
            Connection conn = connectToDatabase(com.clavain.muninmxcd.p);
            java.sql.Statement stmt = conn.createStatement();

            // SELECT alert_contacts.id as nid, contacts.* FROM `alert_contacts` LEFT JOIN contacts ON alert_contacts.contact_id = contacts.id WHERE alert_id = 1
            ResultSet rs = stmt.executeQuery("SELECT alert_contacts.id as nid, contacts.*,contacts.id AS contactId FROM `alert_contacts` LEFT JOIN contacts ON alert_contacts.contact_id = contacts.id WHERE alert_id = " + aid);
            while (rs.next()) {
                Integer contact_id = rs.getInt("contactId");
                String dayField = getScheduleFieldToCheck();
                logger.info("[Notifications " + aid + "] Found " + rs.getString("contact_name"));
                if (rs.getString(dayField).equals("disabled")) {
                    logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " disabled notifications for today - skipping contact");
                } else {
                    String splitField = rs.getString(dayField);
                    // figure out if this user got notifications enabled or disabled for the current hour and time
                    String[] hours = splitField.split(";");
                    long a = getStampFromTimeAndZone(hours[0], rs.getString("timezone"));
                    long b = getStampFromTimeAndZone(hours[1], rs.getString("timezone"));
                    long cur = (System.currentTimeMillis() / 1000L);
                    // if in range send notifications
                    if (a < cur && b > cur) {
                        String failTime = getHumanReadableDateFromTimeStampWithTimezone(alert.getLast_alert(), rs.getString("timezone"));
                        String title = "ALERT: " + alert.getHostname()+ " (" + alert.getPluginName() + ")";
                        String message = "Alert Time: " + failTime + ".   Details: " + alert.getAlertMsg();
                       
                        String json = "";
                        if (rs.getInt("callback_active") == 1) {
                            logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " Sending Callback");
                            sendCallback(alert, rs.getString("contact_callback"));
                            updateNotificationLog(aid, contact_id, "Callback executed to " + rs.getString("contact_callback"), "callback");
                        }
                        if (rs.getInt("tts_active") == 1) {
                            title = "This is a MuninMX Alert: Plugin: " + alert.getPluginName() + " on host " + alert.getHostname() + " is in alert state.";
                            logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " Initiating TTS Call");
                            sendTTS(title, message, rs.getString("contact_mobile_nr"), rs.getInt("user_id"));
                            updateNotificationLog(aid, contact_id, "Text2Speech Call initiated to " + rs.getString("contact_mobile_nr"), "tts");
                        }
                        if (rs.getInt("email_active") == 1) {
                            logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " Sending E-Mail");
                            String ENDL = System.getProperty("line.separator");
                            message = "Alert Time: " + failTime + "." + ENDL + ENDL + "Details:" + ENDL + ENDL + alert.getAlertMsg();

                            sendMail(title, message, rs.getString("contact_email"));
                            updateNotificationLog(aid, contact_id, "E-Mail send to " + rs.getString("contact_email"), "email");
                        }
                        if (rs.getInt("sms_active") == 1) {
                            title = alert.getHostname() + " reports ";
                            message = alert.getAlertMsg();

                            logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " Sending SMS");
                            sendSMS(title, message, rs.getString("contact_mobile_nr"), rs.getInt("user_id"));
                            updateNotificationLog(aid, contact_id, "SMS send to " + rs.getString("contact_mobile_nr"), "sms");
                        }
                        if (rs.getInt("pushover_active") == 1) {
                            logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " Sending Pushover Notification");
                            sendPushover(title, message, rs.getString("pushover_key"));
                            updateNotificationLog(aid, contact_id, "PushOver Message send to " + rs.getString("pushover_key"), "pushover");
                        }
                      

                    } else {
                        logger.info("[Notifications " + aid + "] " + rs.getString("contact_name") + " disabled notifications for this timerange - skipping contact");
                    }
                }
            }
        conn.close();
        } catch (Exception ex) {
            logger.error("Error in sendNotifications for CID " + aid + " : " + ex.getLocalizedMessage());
            ex.printStackTrace();
        }
    }

    private static void sendPushover(String title, String message, String userKey) {
        PushOverMessage pom = new PushOverMessage(userKey, title, message);