summary refs log blame commit diff stats
path: root/plugins/sysinfo/xsys.c
blob: 98bc9017aa95c7b8ef3f82fb082ae58725e5e679 (plain) (tree)
1
2
3
4
  


                                       
















                                                                       
                                                                            





                   
                 
 
                           


                  
 
                                      
                         

                                                  
                                                   
                                                                                                                          
 

                                                               
                              
                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
 


                               
                                                        


   
                      
 
                                                          






                                     
                                                         


                        

 
          
                                          
 
                            
                           







                                     
                           
                        
                     





                          
 
                                                                         






                                                            
                                                                     
                                       


















                                                                                    
         
                                                                      
                                       

         
                            
         
                                 

                         
 
                 
         
                                                                                                           
         
            
         
                                                                                                           
         
 
                                              

                                                            
 

                                                             
         
                                                                         
                                       
         







                                                                                             
         
                                                                   
                                       
         
 


                                                           
 


                                    
                                                                      
                                       
         
 


















                                                           
 




                                                           
                                                                        
         
                                                                         
                                       
         

                                                                  
         
                                                          
                 
                                                    
                         


















                                                                                                                              
 


                                                           
 
                     
         
                                                         
         
            
         
                                                   

         
                               

 
          
                                     
 



                           

                                                  
         
                                                                     
                                       

         
                                                                  
                                             
        

                     
                                                        


            
                                                  

         
                               








                                         
                                                                          
                                       





                                              
                                                      


            
                                                
         
                               














                                                                    
                                                                      
                                       




















                                                                                                                     
                                                        

            
         
                                                  
         
 
                               












                                                             
                                                                         
                                       


                                                               
                                                                         
                                       






                                                                                                                                                      
                                                        


            
                                                  

         
                               











                                               
                                                                 
                                               





                                                
                                                                 
                                               





                                       
                                                                   
                                       





                                               
                                                        
         
            
         
                                                  

         
                               

 
          








                                                   
                                                                               
                                       














                                                                          
                                                        


            
                                                  

         
                               



                                        

                          

                                        
         
                                                                          
                                       
         
 

                                               

                     
                                                       

            
         
                                                 
         
 
                               
















                                                             
                                                               
         
            
         
                                                         

         
                               

 
          
                                         
 





                           
 
                                                                        
         
                                                                         
                                       
         
 
























                                                                                                                              
 


                                                 
         
                                                        
         
            
         
                                                  
         
 
                               
 
 

                                                           

                            


                                      
        
                             
         
                                                                                                         
                                       
         

                                                                     
         
                                                                       
                                       
         
 


                           
                                                                                                                                       
                                                          

                                                   
                                                     
         
                                                         
         
            
         
                                                   

         
                               

 

                                                             
 
                              
                      






                                        
                                    
 
                             
         
                                                                                                           
                                       
         

                                                                    
         
                                                                       
                                       
         



                                                                        
         
                                                                       
                                       
         
 

                                                 

                              

                                   
                                            
         





                                           

                                   
                                            
         
            




                                                                                                                             
                                                          

                                                       
                                                     
         
                                                           


            
                                                     

         
                               

 






                         

                                                           



                                   

                                                                         






                                           


                                                                    

 
          
                                                           
 
                          
                         
                   

                           
                                                               
         
                                                                            
                                       
         
 
                                                     
         
                             

         
                                                  
         
                                                  
                                       



                                                       
                                       




                                                          
                                                                          
                                               


                                                            

                                                                                             






                                                                                                                 

                                                                                                 


                            
                                                                                  



                                                                 

                                                                                             


                    
                                                                                                                       
                                               

                 
                                       



                                                        
                                                                                            
                                       
         
                                                     
         
                                            
                                       
         
                                                         
         
                                                
                                       
         
                                                      
         
                                             
                                       
         
                                                      
         
                                             
                                       
         
                                                       
         
                                              
                                       
         
                                                      
         
                                             
                                       
         
                                                        
         
                                               
                                       
         
                                                           
         
                                                  
                                       
         
                                                         
         
                                                
                                       
         
                                                   
         
                                                 
                                       
         

            
                                                  
                                       
         
 

   
                                                                                                                             






                                  


                                                                                                            

                                                     
                                                                   
         
                                                                          

         
                                                                   
         
                                                                          

         
                                                             
         
                                                                            

         

                                                                                    



                 
                            
 

                                                                        

                 
/*
 * SysInfo - sysinfo plugin for HexChat
 * Copyright (c) 2012 Berke Viktor.
 *
 * xsys.c - main functions for X-Sys 2
 * by mikeshoup
 * Copyright (C) 2003, 2004, 2005 Michael Shoup
 * Copyright (C) 2005, 2006, 2007 Tony Vroon
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>

#include "hexchat-plugin.h"
#include "parse.h"
#include "match.h"
#include "xsys.h"

#define DEFAULT_FORMAT "%B%1:%B %2 **"
#define DEFAULT_PERCENT 1
#define DEFAULT_PCIIDS "/usr/share/hwdata/pci.ids"

static hexchat_plugin *ph;	/* plugin handle */
static int error_printed = 0;	/* semaphore, make sure not to print the same error more than once during one execution */

static char name[] = "SysInfo";
static char desc[] = "Display info about your hardware and OS";
static char version[] = "3.0";
static char sysinfo_help[] = "SysInfo Usage:\n  /SYSINFO [OS|DISTRO|CPU|RAM|DISK|VGA|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n  /SYSINFO LIST, print current settings\n  /SYSINFO SET <variable>, update given setting\n  /SYSINFO RESET, reset settings to defaults\n  /NETDATA <iface>, show transmitted data on given interface\n  /NETSTREAM <iface>, show current bandwidth on given interface\n";

void
sysinfo_get_pciids (char* dest)
{
	hexchat_pluginpref_get_str (ph, "pciids", dest);
}

int
sysinfo_get_percent ()
{
	return hexchat_pluginpref_get_int (ph, "percent");
}

void
sysinfo_print_error (const char* msg)
{
	if (!error_printed)
	{
		hexchat_printf (ph, "%s\t%s", name, msg);
	}
	error_printed++;
	
}

static int
print_summary (int announce, char* format)
{
	char sysinfo[bsize];
	char buffer[bsize];
	char cpu_model[bsize];
	char cpu_cache[bsize];
	char cpu_vendor[bsize];
	char os_host[bsize];
	char os_user[bsize];
	char os_kernel[bsize];
	unsigned long long mem_total;
	unsigned long long mem_free;
	unsigned int count;
	double cpu_freq;
	int giga = 0;
	int weeks;
	int days;
	int hours;
	int minutes;
	int seconds;
	sysinfo[0] = '\0';

	snprintf (buffer, bsize, "%s", hexchat_get_info (ph, "version"));
	format_output ("HexChat", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (sysinfo));

	/* BEGIN OS PARSING */
	if (xs_parse_os (os_user, os_host, os_kernel) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_os()", name);
		return HEXCHAT_EAT_ALL;
	}

	snprintf (buffer, bsize, "%s", os_kernel);
	format_output ("OS", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (sysinfo));

	/* BEGIN DISTRO PARSING */
        if (xs_parse_distro (buffer) != 0)
        {
		strncpy (buffer, "Unknown", bsize);
	}

	format_output ("Distro", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (sysinfo));	

	/* BEGIN CPU PARSING */
	if (xs_parse_cpu (cpu_model, cpu_vendor, &cpu_freq, cpu_cache, &count) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_cpu()", name);
		return HEXCHAT_EAT_ALL;
	}

	if (cpu_freq > 1000)
	{
		cpu_freq /= 1000;
		giga = 1;
	}

	if (giga)
	{
		snprintf (buffer, bsize, "%d x %s (%s) @ %.2fGHz", count, cpu_model, cpu_vendor, cpu_freq);
	}
	else
	{
		snprintf (buffer, bsize, "%d x %s (%s) @ %.0fMHz", count, cpu_model, cpu_vendor, cpu_freq);
	}

	format_output ("CPU", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (sysinfo));

	/* BEGIN MEMORY PARSING */
	if (xs_parse_meminfo (&mem_total, &mem_free, 0) == 1)
	{
		hexchat_printf (ph, "%s\tERROR in parse_meminfo!", name);
		return HEXCHAT_EAT_ALL;
	}

	snprintf (buffer, bsize, "%s", pretty_freespace ("Physical", &mem_free, &mem_total));
	format_output ("RAM", buffer, format);	
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (sysinfo));

	/* BEGIN DISK PARSING */
	if (xs_parse_df (NULL, buffer))
	{
		hexchat_printf (ph, "%s\tERROR in parse_df", name);
		return HEXCHAT_EAT_ALL;
	}

	format_output ("Disk", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (buffer));

	/* BEGIN VIDEO PARSING */
	if (xs_parse_video (buffer))
	{
		hexchat_printf (ph, "%s\tERROR in parse_video", name);
		return HEXCHAT_EAT_ALL;
	}

	format_output ("VGA", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (buffer));

	/* BEGIN SOUND PARSING */
	if (xs_parse_sound (buffer))
	{
		strncpy (buffer, "Not present", bsize);
	}

	format_output ("Sound", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (buffer));

	/* BEGIN ETHERNET PARSING */
	if (xs_parse_ether (buffer))
	{
		strncpy (buffer, "None found", bsize);
	}

	format_output ("Ethernet", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (buffer));

	/* BEGIN UPTIME PARSING */
	if (xs_parse_uptime (&weeks, &days, &hours, &minutes, &seconds))
	{
		hexchat_printf (ph, "%s\tERROR in parse_uptime()", name);
		return HEXCHAT_EAT_ALL;
	}

	if (minutes != 0 || hours != 0 || days != 0 || weeks != 0)
	{
		if (hours != 0 || days != 0 || weeks != 0)
		{
			if (days  !=0 || weeks != 0)
			{
				if (weeks != 0)
				{
					snprintf (buffer, bsize, "%dw %dd %dh %dm %ds", weeks, days, hours, minutes, seconds);
				}
				else
				{
					snprintf (buffer, bsize, "%dd %dh %dm %ds", days, hours, minutes, seconds);
				}
			}
			else
			{
				snprintf (buffer, bsize, "%dh %dm %ds", hours, minutes, seconds);
			}
		}
		else
		{
			snprintf (buffer, bsize, "%dm %ds", minutes, seconds);
		}
	}

        format_output ("Uptime", buffer, format);
	strcat (sysinfo, "\017 ");
	strncat (sysinfo, buffer, bsize - strlen (buffer));

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", sysinfo);
	}
	else
	{
		hexchat_printf (ph, "%s", sysinfo);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_os (int announce, char* format)
{
	char buffer[bsize];
	char user[bsize];
	char host[bsize];
	char kernel[bsize];

	if (xs_parse_os (user, host, kernel) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_os()", name);
		return HEXCHAT_EAT_ALL;
	}

	snprintf (buffer, bsize, "%s@%s, %s", user, host, kernel);
	format_output ("OS", buffer, format);
	
	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", buffer);
	}
	else
	{
		hexchat_printf (ph, "%s", buffer);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_distro (int announce, char* format)
{
	char name[bsize];

	if (xs_parse_distro (name) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_distro()!", name);
		return HEXCHAT_EAT_ALL;
	}

	format_output("Distro", name, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", name);
	}
	else
	{
		hexchat_printf (ph, "%s", name);
	}
	return HEXCHAT_EAT_ALL;
}

static int
print_cpu (int announce, char* format)
{
	char model[bsize];
	char vendor[bsize];
	char cache[bsize];
	char buffer[bsize];
	unsigned int count;
	double freq;
	int giga = 0;

	if (xs_parse_cpu (model, vendor, &freq, cache, &count) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_cpu()", name);
		return HEXCHAT_EAT_ALL;
	}

	if (freq > 1000)
	{
		freq /= 1000;
		giga = 1;
	}

	if (giga)
	{
		snprintf (buffer, bsize, "%d x %s (%s) @ %.2fGHz w/ %s L2 Cache", count, model, vendor, freq, cache);
	}
	else
	{
		snprintf (buffer, bsize, "%d x %s (%s) @ %.0fMHz w/ %s L2 Cache", count, model, vendor, freq, cache);
	}

	format_output ("CPU", buffer, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", buffer);
	}
	else
	{
		hexchat_printf (ph, "%s", buffer);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_ram (int announce, char* format)
{
	unsigned long long mem_total;
	unsigned long long mem_free;
	unsigned long long swap_total;
	unsigned long long swap_free;
	char string[bsize];

	if (xs_parse_meminfo (&mem_total, &mem_free, 0) == 1)
	{
		hexchat_printf (ph, "%s\tERROR in parse_meminfo!", name);
		return HEXCHAT_EAT_ALL;
	}
	if (xs_parse_meminfo (&swap_total, &swap_free, 1) == 1)
	{
		hexchat_printf (ph, "%s\tERROR in parse_meminfo!", name);
		return HEXCHAT_EAT_ALL;
	}

	snprintf (string, bsize, "%s - %s", pretty_freespace ("Physical", &mem_free, &mem_total), pretty_freespace ("Swap", &swap_free, &swap_total));
	format_output ("RAM", string, format);
	
	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", string);
	}
	else
	{
		hexchat_printf (ph, "%s", string);
	}
	
	return HEXCHAT_EAT_ALL;
}

static int
print_disk (int announce, char* format)
{
	char string[bsize] = {0,};

#if 0
	if (*word == '\0')
	{
		if (xs_parse_df (NULL, string))
		{
			hexchat_printf (ph, "ERROR in parse_df");
			return HEXCHAT_EAT_ALL;
		}
	}
	else
	{
		if (xs_parse_df (*word, string))
		{
			hexchat_printf (ph, "ERROR in parse_df");
			return HEXCHAT_EAT_ALL;
		}
	}
#endif

	if (xs_parse_df (NULL, string))
	{
		hexchat_printf (ph, "%s\tERROR in parse_df", name);
		return HEXCHAT_EAT_ALL;
	}

	format_output ("Disk", string, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", string);
	}
	else
	{
		hexchat_printf (ph, "%s", string);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_vga (int announce, char* format)
{
	char vid_card[bsize];
	char agp_bridge[bsize];
	char buffer[bsize];
	int ret;

	if ((ret = xs_parse_video (vid_card)) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_video! %d", name, ret);
		return HEXCHAT_EAT_ALL;
	}

	if (xs_parse_agpbridge (agp_bridge) != 0)
	{
		snprintf (buffer, bsize, "%s", vid_card);
	}
	else
	{
		snprintf (buffer, bsize, "%s @ %s", vid_card, agp_bridge);
	}

	format_output ("VGA", buffer, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", buffer);
	}
	else
	{
		hexchat_printf (ph, "%s", buffer);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_sound (int announce, char* format)
{
	char sound[bsize];

	if (xs_parse_sound (sound) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_asound()!", name);
		return HEXCHAT_EAT_ALL;
	}

	format_output ("Sound", sound, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", sound);
	}
	else
	{
		hexchat_printf (ph, "%s", sound);
	}

	return HEXCHAT_EAT_ALL;
}


static int
print_ethernet (int announce, char* format)
{
	char ethernet_card[bsize];

	if (xs_parse_ether (ethernet_card))
	{
		strncpy (ethernet_card, "None found", bsize);
	}

	format_output ("Ethernet", ethernet_card, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", ethernet_card);
	}
	else
	{
		hexchat_printf (ph, "%s", ethernet_card);
	}

	return HEXCHAT_EAT_ALL;
}

static int
print_uptime (int announce, char* format)
{
	char buffer[bsize];
	int weeks;
	int days;
	int hours;
	int minutes;
	int seconds;

	if (xs_parse_uptime (&weeks, &days, &hours, &minutes, &seconds))
	{
		hexchat_printf (ph, "%s\tERROR in parse_uptime()", name);
		return HEXCHAT_EAT_ALL;
	}

	if (minutes != 0 || hours != 0 || days != 0 || weeks != 0)
	{
		if (hours != 0 || days != 0 || weeks != 0)
		{
			if (days  !=0 || weeks != 0)
			{
				if (weeks != 0)
				{
					snprintf (buffer, bsize, "%dw %dd %dh %dm %ds", weeks, days, hours, minutes, seconds);
				}
				else
				{
					snprintf (buffer, bsize, "%dd %dh %dm %ds", days, hours, minutes, seconds);
				}
			}
			else
			{
				snprintf (buffer, bsize, "%dh %dm %ds", hours, minutes, seconds);
			}
		}
		else
		{
			snprintf (buffer, bsize, "%dm %ds", minutes, seconds);
		}
	}

	format_output ("Uptime", buffer, format);

	if (announce)
	{
		hexchat_commandf (ph, "SAY %s", buffer);
	}
	else
	{
		hexchat_printf (ph, "%s", buffer);
	}

	return HEXCHAT_EAT_ALL;
}

static int
netdata_cb (char *word[], char *word_eol[], void *userdata)
{
	char netdata[bsize];
	char format[bsize];
	unsigned long long bytes_recv;
	unsigned long long bytes_sent;
	
	if (*word[2] == '\0')
	{
		hexchat_printf (ph, "%s\tYou must specify a network device (e.g. /NETDATA eth0)!", name);
		return HEXCHAT_EAT_ALL;
	}

	if (xs_parse_netdev (word[2], &bytes_recv, &bytes_sent) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_netdev", name);
		return HEXCHAT_EAT_ALL;
	}

	bytes_recv /= 1024;
	bytes_sent /= 1024;
	
	snprintf (netdata, bsize, "%s: %.1f MB Recieved, %.1f MB Sent", word[2], (double)bytes_recv/1024.0, (double)bytes_sent/1024.0);
	hexchat_pluginpref_get_str (ph, "format", format);
	format_output ("Netdata", netdata, format);

	if (hexchat_list_int (ph, NULL, "type") >= 2)
	{
		hexchat_commandf (ph, "SAY %s", netdata);
	}
	else
	{
		hexchat_printf (ph, "%s", netdata);
	}
	
	return HEXCHAT_EAT_ALL;
}

static int
netstream_cb (char *word[], char *word_eol[], void *userdata)
{
	char netstream[bsize];
	char mag_r[5];
	char mag_s[5];
	char format[bsize];
	unsigned long long bytes_recv;
	unsigned long long bytes_sent;
	unsigned long long bytes_recv_p;
	unsigned long long bytes_sent_p;

	struct timespec ts = {1, 0};

	if (*word[2] == '\0')
	{
		hexchat_printf (ph, "%s\tYou must specify a network device (e.g. /NETSTREAM eth0)!", name);
		return HEXCHAT_EAT_ALL;
	}

	if (xs_parse_netdev(word[2], &bytes_recv, &bytes_sent) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_netdev", name);
		return HEXCHAT_EAT_ALL;
	}

	while (nanosleep (&ts, &ts) < 0);

	if (xs_parse_netdev(word[2], &bytes_recv_p, &bytes_sent_p) != 0)
	{
		hexchat_printf (ph, "%s\tERROR in parse_netdev", name);
		return HEXCHAT_EAT_ALL;
	}

	bytes_recv = (bytes_recv_p - bytes_recv);
	bytes_sent = (bytes_sent_p - bytes_sent);

	if (bytes_recv > 1024)
	{
		bytes_recv /= 1024;
		snprintf (mag_r, 5, "KB/s");
	}
	else
	{
		snprintf (mag_r, 5, "B/s");
	}

	if (bytes_sent > 1024)
	{
		bytes_sent /= 1024;
		snprintf (mag_s, 5, "KB/s");
	}
	else
	{
		snprintf (mag_s, 5, "B/s");
	}

	snprintf (netstream, bsize, "%s: Receiving %llu %s, Sending %llu %s", word[2], bytes_recv, mag_r, bytes_sent, mag_s);
	hexchat_pluginpref_get_str (ph, "format", format);
	format_output ("Netstream", netstream, format);

	if (hexchat_list_int (ph, NULL, "type") >= 2)
	{
		hexchat_commandf (ph, "SAY %s", netstream);
	}
	else
	{
		hexchat_printf (ph, "%s", netstream);
	}

	return HEXCHAT_EAT_ALL;
}

static void
list_settings ()
{
	char list[512];
	char buffer[512];
	char* token;

	hexchat_pluginpref_list (ph, list);
	hexchat_printf (ph, "%s\tCurrent Settings:", name);
	token = strtok (list, ",");

	while (token != NULL)
	{
		hexchat_pluginpref_get_str (ph, token, buffer);
		hexchat_printf (ph, "%s\t%s: %s\n", name, token, buffer);
		token = strtok (NULL, ",");
	}
}

static void
reset_settings ()
{
	hexchat_pluginpref_set_str (ph, "pciids", DEFAULT_PCIIDS);
	hexchat_pluginpref_set_str (ph, "format", DEFAULT_FORMAT);
	hexchat_pluginpref_set_int (ph, "percent", DEFAULT_PERCENT);
}

static int
sysinfo_cb (char *word[], char *word_eol[], void *userdata)
{
	error_printed = 0;
	int announce = 0;
	int buffer;
	char format[bsize];

	if (!hexchat_pluginpref_get_str (ph, "format", format))
	{
		hexchat_printf (ph, "%s\tError reading config file!", name);
		return HEXCHAT_EAT_ALL;
	}

	if (hexchat_list_int (ph, NULL, "type") >= 2)
	{
		announce = 1;
	}

	if (!g_ascii_strcasecmp ("HELP", word[2]))
	{
		hexchat_printf (ph, sysinfo_help);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("LIST", word[2]))
	{
		list_settings ();
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("SET", word[2]))
	{
		if (!g_ascii_strcasecmp ("", word_eol[4]))
		{
			hexchat_printf (ph, "%s\tEnter a value!\n", name);
			return HEXCHAT_EAT_ALL;
		}
		if (!g_ascii_strcasecmp ("format", word[3]))
		{
			hexchat_pluginpref_set_str (ph, "format", word_eol[4]);
			hexchat_printf (ph, "%s\tformat is set to: %s\n", name, word_eol[4]);
		}
		else if (!g_ascii_strcasecmp ("percent", word[3]))
		{
			buffer = atoi (word[4]);	/* don't use word_eol, numbers must not contain spaces */

			if (buffer > 0 && buffer < INT_MAX)
			{
				hexchat_pluginpref_set_int (ph, "percent", buffer);
				hexchat_printf (ph, "%s\tpercent is set to: %d\n", name, buffer);
			}
			else
			{
				hexchat_printf (ph, "%s\tInvalid input!\n", name);
			}
		}
		else if (!g_ascii_strcasecmp ("pciids", word[3]))
		{
			hexchat_pluginpref_set_str (ph, "pciids", word_eol[4]);
			hexchat_printf (ph, "%s\tpciids is set to: %s\n", name, word_eol[4]);
		}
		else
		{
			hexchat_printf (ph, "%s\tInvalid variable name! Use 'pciids', 'format' or 'percent'!\n", name);
			return HEXCHAT_EAT_ALL;
		}

		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("RESET", word[2]))
	{
		reset_settings ();
		hexchat_printf (ph, "%s\tSettings have been restored to defaults.\n", name);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("OS", word[2]))
	{
		print_os (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("DISTRO", word[2]))
	{
		print_distro (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("CPU", word[2]))
	{
		print_cpu (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("RAM", word[2]))
	{
		print_ram (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("DISK", word[2]))
	{
		print_disk (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("VGA", word[2]))
	{
		print_vga (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("SOUND", word[2]))
	{
		print_sound (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("ETHERNET", word[2]))
	{
		print_ethernet (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("UPTIME", word[2]))
	{
		print_uptime (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else if (!g_ascii_strcasecmp ("", word[2]))
	{
		print_summary (announce, format);
		return HEXCHAT_EAT_ALL;
	}
	else
	{
		hexchat_printf (ph, sysinfo_help);
		return HEXCHAT_EAT_ALL;
	}
}

int
hexchat_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
{
	ph = plugin_handle;
	*plugin_name    = name;
	*plugin_desc    = desc;
	*plugin_version = version;
	char buffer[bsize];

	hexchat_hook_command (ph, "SYSINFO",	HEXCHAT_PRI_NORM,	sysinfo_cb,	sysinfo_help, NULL);
	hexchat_hook_command (ph, "NETDATA",	HEXCHAT_PRI_NORM,	netdata_cb,	NULL, NULL);
	hexchat_hook_command (ph, "NETSTREAM",	HEXCHAT_PRI_NORM,	netstream_cb,	NULL, NULL);

	/* this is required for the very first run */
	if (hexchat_pluginpref_get_str (ph, "pciids", buffer) == 0)
	{
		hexchat_pluginpref_set_str (ph, "pciids", DEFAULT_PCIIDS);
	}

	if (hexchat_pluginpref_get_str (ph, "format", buffer) == 0)
	{
		hexchat_pluginpref_set_str (ph, "format", DEFAULT_FORMAT);
	}

	if (hexchat_pluginpref_get_int (ph, "percent") == -1)
	{
		hexchat_pluginpref_set_int (ph, "percent", DEFAULT_PERCENT);
	}

	hexchat_command (ph, "MENU ADD \"Window/Display System Info\" \"SYSINFO\"");
	hexchat_printf (ph, "%s plugin loaded\n", name);
	return 1;
}

int
hexchat_plugin_deinit (void)
{
	hexchat_command (ph, "MENU DEL \"Window/Display System Info\"");
	hexchat_printf (ph, "%s plugin unloaded\n", name);
	return 1;
}
_data->timestamp); break; case 307: /* dalnet version */ case 320: /* :is an identified user */ if (!serv->skip_next_whois) EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_ID, whois_sess, word[4], word_eol[5] + 1, NULL, NULL, 0, tags_data->timestamp); break; case 321: if (!fe_is_chanwindow (sess->server)) EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANLISTHEAD, serv->server_session, NULL, NULL, NULL, NULL, 0, tags_data->timestamp); break; case 322: if (fe_is_chanwindow (sess->server)) { fe_add_chan_list (sess->server, word[4], word[5], word_eol[6] + 1); } else { PrintTextTimeStampf (serv->server_session, tags_data->timestamp, "%-16s %-7d %s\017\n", word[4], atoi (word[5]), word_eol[6] + 1); } break; case 323: if (!fe_is_chanwindow (sess->server)) EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0, tags_data->timestamp); else fe_chan_list_end (sess->server); break; case 324: sess = find_channel (serv, word[4]); if (!sess) sess = serv->server_session; if (sess->ignore_mode) sess->ignore_mode = FALSE; else EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODES, sess, word[4], (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5], NULL, NULL, 0, tags_data->timestamp); fe_update_mode_buttons (sess, 'c', '-'); fe_update_mode_buttons (sess, 't', '-'); fe_update_mode_buttons (sess, 'n', '-'); fe_update_mode_buttons (sess, 'i', '-'); fe_update_mode_buttons (sess, 'm', '-'); fe_update_mode_buttons (sess, 'l', '-'); fe_update_mode_buttons (sess, 'k', '-'); handle_mode (serv, word, word_eol, "", TRUE, tags_data); break; case 328: /* channel url */ sess = find_channel (serv, word[4]); if (sess) { EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANURL, sess, word[4], (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5], NULL, NULL, 0, tags_data->timestamp); } break; case 329: sess = find_channel (serv, word[4]); if (sess) { if (sess->ignore_date) sess->ignore_date = FALSE; else channel_date (sess, word[4], (word[5][0] == ':') ? word[5] + 1 : word[5], tags_data); } break; case 330: if (!serv->skip_next_whois) EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_AUTH, whois_sess, word[4], word_eol[6] + 1, word[5], NULL, 0, tags_data->timestamp); inbound_user_info (sess, NULL, NULL, NULL, NULL, word[4], NULL, word[5], 0xff, tags_data); break; case 332: inbound_topic (serv, word[4], (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5], tags_data); break; case 333: inbound_topictime (serv, word[4], word[5], atol (word[6]), tags_data); break; #if 0 case 338: /* Undernet Real user@host, Real IP */ EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_REALHOST, sess, word[4], word[5], word[6], (word_eol[7][0]==':') ? word_eol[7]+1 : word_eol[7], 0, tags_data->timestamp); break; #endif case 341: /* INVITE ACK */ EMIT_SIGNAL_TIMESTAMP (XP_TE_UINVITE, sess, word[4], word[5], serv->servername, NULL, 0, tags_data->timestamp); break; case 352: /* WHO */ { unsigned int away = 0; session *who_sess = find_channel (serv, word[4]); if (*word[9] == 'G') away = 1; inbound_user_info (sess, word[4], word[5], word[6], word[7], word[8], word_eol[11], NULL, away, tags_data); /* try to show only user initiated whos */ if (!who_sess || !who_sess->doing_who) EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0, tags_data->timestamp); } break; case 354: /* undernet WHOX: used as a reply for irc_away_status */ { unsigned int away = 0; session *who_sess; /* irc_away_status and irc_user_list sends out a "152" */ if (!strcmp (word[4], "152")) { who_sess = find_channel (serv, word[5]); if (*word[10] == 'G') away = 1; /* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */ inbound_user_info (sess, word[5], word[6], word[7], word[8], word[9], word_eol[12]+1, word[11], away, tags_data); /* try to show only user initiated whos */ if (!who_sess || !who_sess->doing_who) EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0, tags_data->timestamp); } else goto def; } break; case 315: /* END OF WHO */ { session *who_sess; who_sess = find_channel (serv, word[4]); if (who_sess) { if (!who_sess->doing_who) EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0, tags_data->timestamp); who_sess->doing_who = FALSE; } else { if (!serv->doing_dns) EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0, tags_data->timestamp); serv->doing_dns = FALSE; } } break; case 346: /* +I-list entry */ if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 346, tags_data)) goto def; break; case 347: /* end of invite list */ if (!fe_ban_list_end (sess, 347)) goto def; break; case 348: /* +e-list entry */ if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 348, tags_data)) goto def; break; case 349: /* end of exemption list */ sess = find_channel (serv, word[4]); if (!sess) goto def; if (!fe_ban_list_end (sess, 349)) goto def; break; case 353: /* NAMES */ inbound_nameslist (serv, word[5], (word_eol[6][0] == ':') ? word_eol[6] + 1 : word_eol[6], tags_data); break; case 366: if (!inbound_nameslist_end (serv, word[4], tags_data)) goto def; break; case 367: /* banlist entry */ if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 367, tags_data)) goto def; break; case 368: sess = find_channel (serv, word[4]); if (!sess) goto def; if (!fe_ban_list_end (sess, 368)) goto def; break; case 369: /* WHOWAS end */ case 406: /* WHOWAS error */ EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, whois_sess, text, word[1], word[2], NULL, 0, tags_data->timestamp); serv->inside_whois = 0; break; case 372: /* motd text */ case 375: /* motd start */ if (!prefs.hex_irc_skip_motd || serv->motd_skipped) EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTD, serv->server_session, text, NULL, NULL, NULL, 0, tags_data->timestamp); break; case 376: /* end of motd */ case 422: /* motd file is missing */ inbound_login_end (sess, text, tags_data); break; case 432: /* erroneous nickname */ if (serv->end_of_motd) { goto def; } inbound_next_nick (sess, word[4], 1, tags_data); break; case 433: /* nickname in use */ if (serv->end_of_motd) { goto def; } inbound_next_nick (sess, word[4], 0, tags_data); break; case 437: if (serv->end_of_motd || is_channel (serv, word[4])) goto def; inbound_next_nick (sess, word[4], 0, tags_data); break; case 471: EMIT_SIGNAL_TIMESTAMP (XP_TE_USERLIMIT, sess, word[4], NULL, NULL, NULL, 0, tags_data->timestamp); break; case 473: EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITE, sess, word[4], NULL, NULL, NULL, 0, tags_data->timestamp); break; case 474: EMIT_SIGNAL_TIMESTAMP (XP_TE_BANNED, sess, word[4], NULL, NULL, NULL, 0, tags_data->timestamp); break; case 475: EMIT_SIGNAL_TIMESTAMP (XP_TE_KEYWORD, sess, word[4], NULL, NULL, NULL, 0, tags_data->timestamp); break; case 601: notify_set_offline (serv, word[4], FALSE, tags_data); break; case 605: notify_set_offline (serv, word[4], TRUE, tags_data); break; case 600: case 604: notify_set_online (serv, word[4], tags_data); break; case 728: /* +q-list entry */ /* NOTE: FREENODE returns these results inconsistent with e.g. +b */ /* Who else has imlemented MODE_QUIET, I wonder? */ if (!inbound_banlist (sess, atol (word[8]), word[4], word[6], word[7], 728, tags_data)) goto def; break; case 729: /* end of quiet list */ if (!fe_ban_list_end (sess, 729)) goto def; break; case 730: /* RPL_MONONLINE */ notify_set_online_list (serv, word[4] + 1, tags_data); break; case 731: /* RPL_MONOFFLINE */ notify_set_offline_list (serv, word[4] + 1, FALSE, tags_data); break; case 900: /* successful SASL 'logged in as ' */ EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, word_eol[6]+1, word[1], word[2], NULL, 0, tags_data->timestamp); break; case 904: /* failed SASL auth */ inbound_sasl_error (serv); case 903: /* successful SASL auth */ case 905: /* failed SASL auth */ case 906: /* aborted */ case 907: /* attempting to re-auth after a successful auth */ EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLRESPONSE, serv->server_session, word[1], word[2], word[3], ++word_eol[4], 0, tags_data->timestamp); if (!serv->sent_capend) { serv->sent_capend = TRUE; tcp_send_len (serv, "CAP END\r\n", 9); } break; case 908: /* Supported SASL Mechs */ /* ignored for now, SASL 3.2 is a better solution and we only do PLAIN atm */ break; default: if (serv->inside_whois && word[4][0]) { /* some unknown WHOIS reply, ircd coders make them up weekly */ if (!serv->skip_next_whois) EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_SPECIAL, whois_sess, word[4], (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5], word[2], NULL, 0, tags_data->timestamp); return; } def: { session *sess; if (is_channel (serv, word[4])) { sess = find_channel (serv, word[4]); if (!sess) sess = serv->server_session; } else if ((sess=find_dialog (serv,word[4]))) /* user with an open dialog */ ; else sess=serv->server_session; EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, sess, text, word[1], word[2], NULL, 0, tags_data->timestamp); } } } /* handle named messages that starts with a ':' */ static void process_named_msg (session *sess, char *type, char *word[], char *word_eol[], const message_tags_data *tags_data) { server *serv = sess->server; char ip[128], nick[NICKLEN]; char *text, *ex; int len = strlen (type); /* fill in the "ip" and "nick" buffers */ ex = strchr (word[1], '!'); if (!ex) /* no '!', must be a server message */ { safe_strcpy (ip, word[1], sizeof (ip)); safe_strcpy (nick, word[1], sizeof (nick)); } else { safe_strcpy (ip, ex + 1, sizeof (ip)); ex[0] = 0; safe_strcpy (nick, word[1], sizeof (nick)); ex[0] = '!'; } if (len == 4) { guint32 t; t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]); /* this should compile to a bunch of: CMP.L, JE ... nice & fast */ switch (t) { case WORDL('J','O','I','N'): { char *chan = word[3]; char *account = word[4]; char *realname = word_eol[5]; if (account && strcmp (account, "*") == 0) account = NULL; if (realname && *realname == ':') realname++; if (*chan == ':') chan++; if (!serv->p_cmp (nick, serv->nick)) inbound_ujoin (serv, chan, nick, ip, tags_data); else inbound_join (serv, chan, nick, ip, account, realname, tags_data); } return; case WORDL('K','I','C','K'): { char *kicked = word[4]; char *reason = word_eol[5]; if (*kicked) { if (*reason == ':') reason++; if (!strcmp (kicked, serv->nick)) inbound_ukick (serv, word[3], nick, reason, tags_data); else inbound_kick (serv, word[3], kicked, nick, reason, tags_data); } } return; case WORDL('K','I','L','L'): { char *reason = word_eol[4]; if (*reason == ':') reason++; EMIT_SIGNAL_TIMESTAMP (XP_TE_KILL, sess, nick, reason, NULL, NULL, 0, tags_data->timestamp); } return; case WORDL('M','O','D','E'): handle_mode (serv, word, word_eol, nick, FALSE, tags_data); /* modes.c */ return; case WORDL('N','I','C','K'): inbound_newnick (serv, nick, (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3], FALSE, tags_data); return; case WORDL('P','A','R','T'): { char *chan = word[3]; char *reason = word_eol[4]; if (*chan == ':') chan++; if (*reason == ':') reason++; if (!strcmp (nick, serv->nick)) inbound_upart (serv, chan, ip, reason, tags_data); else inbound_part (serv, chan, nick, ip, reason, tags_data); } return; case WORDL('P', 'I', 'N', 'G'): tcp_sendf (sess->server, "PONG %s\r\n", word_eol[3]); return; case WORDL('P','O','N','G'): inbound_ping_reply (serv->server_session, (word[4][0] == ':') ? word[4] + 1 : word[4], word[3], tags_data); return; case WORDL('Q','U','I','T'): inbound_quit (serv, nick, ip, (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3], tags_data); return; case WORDL('A','W','A','Y'): inbound_away_notify (serv, nick, (word_eol[3][0] == ':') ? word_eol[3] + 1 : NULL, tags_data); return; } goto garbage; } else if (len >= 5) { guint32 t; t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]); /* this should compile to a bunch of: CMP.L, JE ... nice & fast */ switch (t) { case WORDL('A','C','C','O'): inbound_account (serv, nick, word[3], tags_data); return; case WORDL('A', 'U', 'T', 'H'): inbound_sasl_authenticate (sess->server, word_eol[3]); return; case WORDL('C', 'H', 'G', 'H'): inbound_user_info (sess, NULL, word[3], word[4], NULL, nick, NULL, NULL, 0xff, tags_data); return; case WORDL('I','N','V','I'): if (ignore_check (word[1], IG_INVI)) return; if (word[4][0] == ':') EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4] + 1, nick, serv->servername, NULL, 0, tags_data->timestamp); else EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4], nick, serv->servername, NULL, 0, tags_data->timestamp); return; case WORDL('N','O','T','I'): { int id = FALSE; /* identified */ text = word_eol[4]; if (*text == ':') { text++; } #ifdef USE_OPENSSL /* QuakeNet CHALLENGE upon our request */ if (serv->loginmethod == LOGIN_CHALLENGEAUTH && !serv->p_cmp (word[1], CHALLENGEAUTH_FULLHOST) && !strncmp (text, "CHALLENGE ", 10) && *serv->password) { char *response; ircnet *net = serv->network; char *user = net && net->user ? net->user : prefs.hex_irc_user_name; response = challengeauth_response (user, serv->password, word[5]); tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n", CHALLENGEAUTH_NICK, user, response, CHALLENGEAUTH_ALGO); g_free (response); return; /* omit the CHALLENGE <hash> ALGOS message */ } #endif if (serv->have_idmsg) { if (*text == '+') { id = TRUE; text++; } else if (*text == '-') text++; } if (!ignore_check (word[1], IG_NOTI)) inbound_notice (serv, word[3], nick, text, ip, id, tags_data); } return; case WORDL('P','R','I','V'): { char *to = word[3]; int len; int id = FALSE; /* identified */ if (*to) { /* Handle limited channel messages, for now no special event */ if (strchr (serv->chantypes, to[0]) == NULL && strchr (serv->nick_prefixes, to[0]) != NULL) to++; text = word_eol[4]; if (*text == ':') text++; if (serv->have_idmsg) { if (*text == '+') { id = TRUE; text++; } else if (*text == '-') text++; } len = strlen (text); if (text[0] == 1 && text[len - 1] == 1) /* ctcp */ { text[len - 1] = 0; text++; if (g_ascii_strncasecmp (text, "ACTION", 6) != 0) flood_check (nick, ip, serv, sess, 0); if (g_ascii_strncasecmp (text, "DCC ", 4) == 0) /* redo this with handle_quotes TRUE */ process_data_init (word[1], word_eol[1], word, word_eol, TRUE, FALSE); ctcp_handle (sess, to, nick, ip, text, word, word_eol, id, tags_data); } else { if (is_channel (serv, to)) { if (ignore_check (word[1], IG_CHAN)) return; inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id, tags_data); } else { if (ignore_check (word[1], IG_PRIV)) return; inbound_privmsg (serv, nick, ip, text, id, tags_data); } } } } return; case WORDL('T','O','P','I'): inbound_topicnew (serv, nick, word[3], (word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4], tags_data); return; case WORDL('W','A','L','L'): text = word_eol[3]; if (*text == ':') text++; EMIT_SIGNAL_TIMESTAMP (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0, tags_data->timestamp); return; } } else if (len == 3) { guint32 t; t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]); switch (t) { case WORDL('C','A','P','\0'): if (strncasecmp (word[4], "ACK", 3) == 0) { inbound_cap_ack (serv, word[1], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); } else if (strncasecmp (word[4], "LS", 2) == 0 || strncasecmp (word[4], "NEW", 3) == 0) { inbound_cap_ls (serv, word[1], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); } else if (strncasecmp (word[4], "NAK", 3) == 0) { inbound_cap_nak (serv, tags_data); } else if (strncasecmp (word[4], "LIST", 4) == 0) { inbound_cap_list (serv, word[1], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); } else if (strncasecmp (word[4], "DEL", 3) == 0) { inbound_cap_del (serv, word[1], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); } return; } } garbage: /* unknown message */ PrintTextTimeStampf (sess, tags_data->timestamp, "GARBAGE: %s\n", word_eol[1]); } /* handle named messages that DON'T start with a ':' */ static void process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[], const message_tags_data *tags_data) { sess = sess->server->server_session; if (!strncmp (buf, "PING ", 5)) { tcp_sendf (sess->server, "PONG %s\r\n", buf + 5); return; } if (!strncmp (buf, "ERROR", 5)) { EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVERERROR, sess, buf + 7, NULL, NULL, NULL, 0, tags_data->timestamp); return; } if (!strncmp (buf, "NOTICE ", 7)) { buf = word_eol[3]; if (*buf == ':') buf++; EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVNOTICE, sess, buf, sess->server->servername, NULL, NULL, 0, tags_data->timestamp); return; } if (!strncmp (buf, "AUTHENTICATE", 12)) { inbound_sasl_authenticate (sess->server, word_eol[2]); return; } EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0, tags_data->timestamp); } /* Returns the timezone offset. This should be the same as the variable * "timezone" in time.h, but *BSD doesn't have it. */ static time_t get_timezone (void) { struct tm tm_utc, tm_local; time_t t, time_utc, time_local; time (&t); /* gmtime() and localtime() are thread-safe on windows. * on other systems we should use {gmtime,localtime}_r(). */ #if WIN32 tm_utc = *gmtime (&t); tm_local = *localtime (&t); #else gmtime_r (&t, &tm_utc); localtime_r (&t, &tm_local); #endif time_utc = mktime (&tm_utc); time_local = mktime (&tm_local); return time_utc - time_local; } /* Handle time-server tags. * * Sets tags_data->timestamp to the correct time (in unix time). * This received time is always in UTC. * * See http://ircv3.atheme.org/extensions/server-time-3.2 */ static void handle_message_tag_time (const char *time, message_tags_data *tags_data) { /* The time format defined in the ircv3.2 specification is * YYYY-MM-DDThh:mm:ss.sssZ * but znc simply sends a unix time (with 3 decimal places for miliseconds) * so we might as well support both. */ if (!*time) return; if (time[strlen (time) - 1] == 'Z') { /* as defined in the specification */ struct tm t; int z; /* we ignore the milisecond part */ z = sscanf (time, "%d-%d-%dT%d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec); if (z != 6) return; t.tm_year -= 1900; t.tm_mon -= 1; t.tm_isdst = 0; /* day light saving time */ tags_data->timestamp = mktime (&t); if (tags_data->timestamp < 0) { tags_data->timestamp = 0; return; } /* get rid of the local time (mktime() receives a local calendar time) */ tags_data->timestamp -= get_timezone(); } else { /* znc */ long long int t; /* we ignore the milisecond part */ if (sscanf (time, "%lld", &t) != 1) return; tags_data->timestamp = (time_t) t; } } /* Handle message tags. * * See http://ircv3.atheme.org/specification/message-tags-3.2 */ static void handle_message_tags (server *serv, const char *tags_str, message_tags_data *tags_data) { char **tags; int i; /* FIXME We might want to avoid the allocation overhead here since * this might be called for every message from the server. */ tags = g_strsplit (tags_str, ";", 0); for (i=0; tags[i]; i++) { char *key = tags[i]; char *value = strchr (tags[i], '='); if (!value) continue; *value = '\0'; value++; if (serv->have_server_time && !strcmp (key, "time")) handle_message_tag_time (value, tags_data); } g_strfreev (tags); } /* irc_inline() - 1 single line received from serv */ static void irc_inline (server *serv, char *buf, int len) { session *sess, *tmp; char *type, *text; char *word[PDIWORDS+1]; char *word_eol[PDIWORDS+1]; char *pdibuf; message_tags_data tags_data = MESSAGE_TAGS_DATA_INIT; pdibuf = g_malloc (len + 1); sess = serv->front_session; /* Python relies on this */ word[PDIWORDS] = NULL; word_eol[PDIWORDS] = NULL; if (*buf == '@') { char *tags = buf + 1; /* skip the '@' */ char *sep = strchr (buf, ' '); if (!sep) goto xit; *sep = '\0'; buf = sep + 1; handle_message_tags(serv, tags, &tags_data); } url_check_line (buf); /* split line into words and words_to_end_of_line */ process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE); if (buf[0] == ':') { /* find a context for this message */ if (is_channel (serv, word[3])) { tmp = find_channel (serv, word[3]); if (tmp) sess = tmp; } /* for server messages, the 2nd word is the "message type" */ type = word[2]; word[0] = type; word_eol[1] = buf; /* keep the ":" for plugins */ if (plugin_emit_server (sess, type, word, word_eol, tags_data.timestamp)) goto xit; word[1]++; word_eol[1] = buf + 1; /* but not for HexChat internally */ } else { word[0] = type = word[1]; if (plugin_emit_server (sess, type, word, word_eol, tags_data.timestamp)) goto xit; } if (buf[0] != ':') { process_named_servermsg (sess, buf, word[0], word_eol, &tags_data); goto xit; } /* see if the second word is a numeric */ if (isdigit ((unsigned char) word[2][0])) { text = word_eol[4]; if (*text == ':') text++; process_numeric (sess, atoi (word[2]), word, word_eol, text, &tags_data); } else { process_named_msg (sess, type, word, word_eol, &tags_data); } xit: g_free (pdibuf); } void proto_fill_her_up (server *serv) { serv->p_inline = irc_inline; serv->p_invite = irc_invite; serv->p_cycle = irc_cycle; serv->p_ctcp = irc_ctcp; serv->p_nctcp = irc_nctcp; serv->p_quit = irc_quit; serv->p_kick = irc_kick; serv->p_part = irc_part; serv->p_ns_identify = irc_ns_identify; serv->p_ns_ghost = irc_ns_ghost; serv->p_join = irc_join; serv->p_join_list = irc_join_list; serv->p_login = irc_login; serv->p_join_info = irc_join_info; serv->p_mode = irc_mode; serv->p_user_list = irc_user_list; serv->p_away_status = irc_away_status; /*serv->p_get_ip = irc_get_ip;*/ serv->p_whois = irc_user_whois; serv->p_get_ip = irc_user_list; serv->p_get_ip_uh = irc_userhost; serv->p_set_back = irc_set_back; serv->p_set_away = irc_set_away; serv->p_message = irc_message; serv->p_action = irc_action; serv->p_notice = irc_notice; serv->p_topic = irc_topic; serv->p_list_channels = irc_list_channels; serv->p_change_nick = irc_change_nick; serv->p_names = irc_names; serv->p_ping = irc_ping; serv->p_raw = irc_raw; serv->p_cmp = rfc_casecmp; /* can be changed by 005 in modes.c */ }