cmd.c
changeset 0 72ffcc3c584e
child 4 e305d7e562c4
equal deleted inserted replaced
-1:000000000000 0:72ffcc3c584e
       
     1 /*
       
     2  * cmd.c                -- Send shell command output as messages
       
     3  *
       
     4  * Copyrigth (C) 2009      Myhailo Danylenko <isbear@ukrpost.net>
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; either version 2 of the License, or (at
       
     9  * your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but
       
    12  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       
    19  * USA
       
    20  */
       
    21 
       
    22 #include <stdlib.h>
       
    23 #include <glib.h>
       
    24 #include <gmodule.h>
       
    25 #include <unistd.h>
       
    26 #include <errno.h>
       
    27 
       
    28 #include "commands.h"
       
    29 #include "logprint.h"
       
    30 #include "utils.h"
       
    31 #include "settings.h"
       
    32 #include "hbuf.h"
       
    33 #include "xmpp.h"
       
    34 #include "roster.h"
       
    35 
       
    36 typedef struct {
       
    37 	gchar      *jid;
       
    38 	GString    *input;
       
    39 	guint       source;
       
    40 	GIOChannel *channel;
       
    41 } cmd_cb_t;
       
    42 
       
    43 static GSList  *cmd_channels = NULL;
       
    44 
       
    45 static gboolean cmd_reader (GIOChannel *channel, GIOCondition condition, gpointer data)
       
    46 {
       
    47 	cmd_cb_t *cb = (cmd_cb_t *) data;
       
    48 
       
    49 	if (condition & (G_IO_IN|G_IO_PRI)) {
       
    50 		GIOStatus    chstat;
       
    51 		static gchar buf[HBB_BLOCKSIZE];
       
    52 		gsize        endpos;
       
    53 
       
    54 		chstat = g_io_channel_read_chars (channel, buf, HBB_BLOCKSIZE, &endpos, NULL);
       
    55 
       
    56 		if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) {
       
    57 			cb->source = 0;
       
    58 			return FALSE; // XXX
       
    59 		}
       
    60 		
       
    61 		if (endpos) {
       
    62 			GString *input = cb->input;
       
    63 			gsize    bread   = 0;
       
    64 			gsize    written = 0;
       
    65 			GError  *err     = NULL;
       
    66 			gchar   *utf8    = NULL;
       
    67 
       
    68 			g_string_append_len (input, buf, endpos);
       
    69 
       
    70 			if (!lm_connection_is_authenticated (lconnection)) {
       
    71 				scr_LogPrint (LPRINT_LOGNORM, "cmd: Connection is not ready, delaying data");
       
    72 				return TRUE;
       
    73 			}
       
    74 
       
    75 			// usual g_locale_to_utf8 seem to be unable to detect locale charset
       
    76 			// maybe, proper solution will be to call setlocale on module loading,
       
    77 			// but mcabber already does this, and I do not want to mess with it
       
    78 			utf8 = g_convert (input->str, input->len, LocaleCharSet, "UTF-8", &bread, &written, &err);
       
    79 
       
    80 			if (err && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE && bread) {
       
    81 				err     = NULL;
       
    82 				written = 0;
       
    83 				utf8 = g_convert (input->str, bread, LocaleCharSet, "UTF-8", &bread, &written, &err);
       
    84 			}
       
    85 			
       
    86 			if (written) {
       
    87 				gsize sent = 0;
       
    88 
       
    89 				while (sent < written) {
       
    90 					gint      crypted;
       
    91 					gpointer  xep184  = NULL;
       
    92 					gsize     len     = 0;
       
    93 					gchar    *bbuf    = NULL;
       
    94 
       
    95 					if (written - sent > HBB_BLOCKSIZE) {
       
    96 						gchar *c = utf8 + sent + HBB_BLOCKSIZE;
       
    97 						c = g_utf8_find_prev_char (utf8 + sent, c);
       
    98 
       
    99 						if (!c) {
       
   100 							scr_LogPrint (LPRINT_LOGNORM, "cmd: Cannot determine utf8 character end! End of data chunk will be discarded!");
       
   101 							break;
       
   102 						}
       
   103 
       
   104 						len  = c - utf8 - sent;
       
   105 						bbuf = g_strndup (utf8 + sent, len);
       
   106 					}
       
   107 
       
   108 					// XXX add command/sequence number as title?
       
   109 					xmpp_send_msg (cb->jid, len ? bbuf : (utf8 + sent), ROSTER_TYPE_USER, NULL, FALSE /* ? */, &crypted, 0, &xep184);
       
   110 
       
   111 					if (crypted == -1) {
       
   112 
       
   113 						scr_LogPrint (LPRINT_LOGNORM, "cmd: Encryption error. Message not sent.");
       
   114 
       
   115 						if (!len)
       
   116 							break;
       
   117 
       
   118 						g_free (bbuf);
       
   119 
       
   120 						continue;
       
   121 					}
       
   122 
       
   123 					hk_message_out (cb->jid, NULL, 0, len ? bbuf : (utf8 + sent), crypted, xep184);
       
   124 
       
   125 					if (!len)
       
   126 						break;
       
   127 
       
   128 					g_free (bbuf);
       
   129 
       
   130 					sent += len;
       
   131 				}
       
   132 
       
   133 				g_free (utf8);
       
   134 
       
   135 				g_string_erase (input, 0, bread);
       
   136 
       
   137 			} else {
       
   138 
       
   139 				scr_LogPrint (LPRINT_LOGNORM, "cmd: Character conversion error: %s", err->message);
       
   140 				cb->source = 0;
       
   141 				return FALSE;
       
   142 			}
       
   143 		}
       
   144 
       
   145 	} else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) {
       
   146 		cb->source = 0;
       
   147 		return FALSE; // XXX
       
   148 	}
       
   149 
       
   150 	return TRUE;
       
   151 }
       
   152 
       
   153 static void cmd_destroy_data (gpointer data)
       
   154 {
       
   155 	cmd_cb_t *cb = (cmd_cb_t *) data;
       
   156 
       
   157 	cmd_channels = g_slist_remove (cmd_channels, data);
       
   158 
       
   159 	// May conflict - will be called during source removal?
       
   160 //	if (cb->source)
       
   161 //		g_source_remove (cb->source);
       
   162 	if (cb->channel)
       
   163 		g_io_channel_unref (cb->channel);
       
   164 	g_free (cb->jid);
       
   165 	g_free (cb);
       
   166 }
       
   167 
       
   168 static void do_cmd (char *arg)
       
   169 {
       
   170 	int         fd[2];
       
   171 	const char *jid = CURRENT_JID;
       
   172 
       
   173 	if (!*arg)
       
   174 		return;
       
   175 	
       
   176 	if (!jid) {
       
   177 		scr_LogPrint (LPRINT_LOGNORM, "Unsuitable buddy selected");
       
   178 		return;
       
   179 	}
       
   180 	
       
   181 	if (pipe (fd)) {
       
   182 		scr_LogPrint (LPRINT_LOGNORM, "Cannot create pipe: %s", strerror (errno));
       
   183 		return;
       
   184 	}
       
   185 
       
   186 	{
       
   187 		int res = fork ();
       
   188 
       
   189 		if (!res) {
       
   190 			
       
   191 			close (fd[0]);
       
   192 			dup2 (fd[1], STDOUT_FILENO);
       
   193 			if (settings_opt_get_int ("cmd_redirect_stderr"))
       
   194 				dup2 (fd[1], STDERR_FILENO);
       
   195 			else
       
   196 				close (STDERR_FILENO);
       
   197 			close (STDIN_FILENO);
       
   198 			close (fd[1]);
       
   199 
       
   200 			{
       
   201 				const char *shell = settings_opt_get ("cmd_shell");
       
   202 				if (!shell)
       
   203 					shell = getenv ("SHELL");
       
   204 				if (!shell)
       
   205 					shell = "sh";
       
   206 				execl (shell, shell, "-c", arg, NULL);
       
   207 			}
       
   208 		}
       
   209 
       
   210 		if (res == -1) {
       
   211 			scr_LogPrint (LPRINT_NORMAL, "Cannot fork child: %s", strerror (errno));
       
   212 			close (fd[0]);
       
   213 			close (fd[1]);
       
   214 			return;
       
   215 		}
       
   216 
       
   217 		close (fd[1]);
       
   218 
       
   219 		GIOChannel *channel = g_io_channel_unix_new (fd[0]);
       
   220 
       
   221 		g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
       
   222 		g_io_channel_set_encoding (channel, NULL, NULL);
       
   223 		g_io_channel_set_close_on_unref (channel, TRUE);
       
   224 		g_io_channel_set_buffered (channel, FALSE);
       
   225 
       
   226 		{
       
   227 			cmd_cb_t *cb = g_new (cmd_cb_t, 1);
       
   228 
       
   229 			cb->jid     = g_strdup (jid);
       
   230 			cb->input   = g_string_new (NULL);
       
   231 			cb->channel = channel;
       
   232 			cb->source  = g_io_add_watch_full (channel, 0, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
       
   233 		                                   cmd_reader, (gpointer) cb, cmd_destroy_data);
       
   234 
       
   235 			cmd_channels = g_slist_append (cmd_channels, cb);
       
   236 		}
       
   237 	}
       
   238 }
       
   239 
       
   240 const gchar *g_module_check_init(GModule *module)
       
   241 {
       
   242 	cmd_add ("cmd", "", 0, 0, do_cmd, NULL);
       
   243 
       
   244 	return NULL;
       
   245 }
       
   246 
       
   247 void g_module_unload(GModule *module)
       
   248 {
       
   249 	GSList *sel;
       
   250 
       
   251 	cmd_del ("cmd");
       
   252 
       
   253 	for (sel = cmd_channels; sel; sel = sel->next) {
       
   254 		cmd_cb_t *cb = (cmd_cb_t *) sel->data;
       
   255 		if (cb->source)
       
   256 			g_source_remove (cb->source);
       
   257 		if (cb->channel)
       
   258 			g_io_channel_unref (cb->channel);
       
   259 		g_free (cb->jid);
       
   260 		g_free (cb);
       
   261 	}
       
   262 
       
   263 	g_slist_free (cmd_channels);
       
   264 }
       
   265 
       
   266 /* vim: se ts=4 sw=4: */