34 #include "roster.h" |
34 #include "roster.h" |
35 |
35 |
36 typedef struct { |
36 typedef struct { |
37 gchar *jid; |
37 gchar *jid; |
38 GString *input; |
38 GString *input; |
|
39 gchar *subject; |
|
40 guint seq; |
39 guint source; |
41 guint source; |
40 GIOChannel *channel; |
42 GIOChannel *channel; |
41 } cmd_cb_t; |
43 } cmd_cb_t; |
42 |
44 |
43 static GSList *cmd_channels = NULL; |
45 static GSList *cmd_channels = NULL; |
|
46 |
|
47 static gboolean is_room (const gchar *jid) |
|
48 { |
|
49 return roster_find (jid, jidsearch, ROSTER_TYPE_ROOM) ? TRUE : FALSE; |
|
50 } |
|
51 |
|
52 static gboolean cmd_send_msg (const gchar *to, const gchar *subject, const gchar *body) |
|
53 { |
|
54 gboolean room = is_room (to); |
|
55 gpointer xep184 = NULL; |
|
56 gint crypted; |
|
57 |
|
58 xmpp_send_msg (to, body, room ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER, room ? NULL : subject, FALSE /* ? */, &crypted, LM_MESSAGE_SUB_TYPE_NOT_SET, &xep184); |
|
59 |
|
60 if (crypted == -1) { |
|
61 scr_LogPrint (LPRINT_LOGNORM, "cmd: Encryption error. Message not sent."); |
|
62 return FALSE; |
|
63 } |
|
64 |
|
65 if (!room) |
|
66 hk_message_out (to, subject, 0, body, crypted, xep184); |
|
67 |
|
68 return TRUE; |
|
69 } |
44 |
70 |
45 static gboolean cmd_reader (GIOChannel *channel, GIOCondition condition, gpointer data) |
71 static gboolean cmd_reader (GIOChannel *channel, GIOCondition condition, gpointer data) |
46 { |
72 { |
47 cmd_cb_t *cb = (cmd_cb_t *) data; |
73 cmd_cb_t *cb = (cmd_cb_t *) data; |
48 |
74 |
49 if (condition & (G_IO_IN|G_IO_PRI)) { |
75 if (condition & (G_IO_IN|G_IO_PRI)) { |
50 GIOStatus chstat; |
76 GIOStatus chstat; |
51 static gchar buf[HBB_BLOCKSIZE]; |
77 static gchar buf[HBB_BLOCKSIZE]; |
52 gsize endpos; |
78 gsize endpos; |
53 |
79 GError *error = NULL; |
54 chstat = g_io_channel_read_chars (channel, buf, HBB_BLOCKSIZE, &endpos, NULL); |
80 |
|
81 chstat = g_io_channel_read_chars (channel, buf, HBB_BLOCKSIZE, &endpos, &error); |
|
82 |
|
83 if (error) { |
|
84 scr_LogPrint (LPRINT_DEBUG, "cmd: Reading error: %s.", error -> message); |
|
85 g_clear_error (&error); |
|
86 } |
55 |
87 |
56 if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) { |
88 if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) { |
57 cb->source = 0; |
89 cb->source = 0; |
58 return FALSE; // XXX |
90 return FALSE; // XXX |
59 } |
91 } |
60 |
92 |
61 if (endpos) { |
93 if (endpos) { |
62 GString *input = cb->input; |
94 GString *input = cb->input; |
63 gsize bread = 0; |
95 gsize bread = 0; |
64 gsize written = 0; |
96 gsize written = 0; |
65 GError *err = NULL; |
|
66 gchar *utf8 = NULL; |
97 gchar *utf8 = NULL; |
67 |
98 |
68 g_string_append_len (input, buf, endpos); |
99 g_string_append_len (input, buf, endpos); |
69 |
100 |
70 if (!lm_connection_is_authenticated (lconnection)) { |
101 if (!lm_connection_is_authenticated (lconnection)) { |
73 } |
104 } |
74 |
105 |
75 // usual g_locale_to_utf8 seem to be unable to detect locale charset |
106 // 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, |
107 // 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 |
108 // 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); |
109 utf8 = g_convert (input->str, input->len, LocaleCharSet, "UTF-8", &bread, &written, &error); |
79 |
110 |
80 if (err && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE && bread) { |
111 if (error && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE && bread) { |
81 err = NULL; |
|
82 written = 0; |
112 written = 0; |
83 utf8 = g_convert (input->str, bread, LocaleCharSet, "UTF-8", &bread, &written, &err); |
113 g_clear_error (&error); |
|
114 utf8 = g_convert (input->str, bread, LocaleCharSet, "UTF-8", &bread, &written, &error); |
84 } |
115 } |
85 |
116 |
86 if (written) { |
117 if (written) { |
87 gsize sent = 0; |
118 gsize sent = 0; |
88 |
119 |
89 while (sent < written) { |
120 while (sent < written) { |
90 gint crypted; |
|
91 gpointer xep184 = NULL; |
|
92 gsize len = 0; |
121 gsize len = 0; |
93 gchar *bbuf = NULL; |
122 gchar *bbuf = NULL; |
94 int type = roster_find (cb->jid, jidsearch, ROSTER_TYPE_ROOM) ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER; |
123 gchar *subject = NULL; |
|
124 gboolean ret; |
95 |
125 |
96 if (written - sent > HBB_BLOCKSIZE) { |
126 if (written - sent > HBB_BLOCKSIZE) { |
97 gchar *c = utf8 + sent + HBB_BLOCKSIZE; |
127 gchar *c = utf8 + sent + HBB_BLOCKSIZE; |
98 c = g_utf8_find_prev_char (utf8 + sent, c); |
128 c = g_utf8_find_prev_char (utf8 + sent, c); |
99 |
129 |
104 |
134 |
105 len = c - utf8 - sent; |
135 len = c - utf8 - sent; |
106 bbuf = g_strndup (utf8 + sent, len); |
136 bbuf = g_strndup (utf8 + sent, len); |
107 } |
137 } |
108 |
138 |
109 // XXX add command/sequence number as title? |
139 cb -> seq += 1; |
110 xmpp_send_msg (cb->jid, len ? bbuf : (utf8 + sent), type, NULL, FALSE /* ? */, &crypted, LM_MESSAGE_SUB_TYPE_NOT_SET, &xep184); |
140 |
111 |
141 if (cb -> subject) |
112 if (crypted == -1) { |
142 subject = g_strdup_printf (cb -> subject, cb -> seq); |
113 |
143 |
|
144 if (!cmd_send_msg (cb->jid, subject, len ? bbuf : (utf8 + sent))) |
114 scr_LogPrint (LPRINT_LOGNORM, "cmd: Encryption error. Message not sent."); |
145 scr_LogPrint (LPRINT_LOGNORM, "cmd: Encryption error. Message not sent."); |
115 |
146 |
116 if (!len) |
147 if (subject) |
117 break; |
148 g_free (subject); |
118 |
|
119 g_free (bbuf); |
|
120 |
|
121 continue; |
|
122 } |
|
123 |
|
124 if (type != ROSTER_TYPE_ROOM) |
|
125 hk_message_out (cb->jid, NULL, 0, len ? bbuf : (utf8 + sent), crypted, xep184); |
|
126 |
149 |
127 if (!len) |
150 if (!len) |
128 break; |
151 break; |
129 |
152 |
130 g_free (bbuf); |
153 g_free (bbuf); |
156 { |
180 { |
157 cmd_cb_t *cb = (cmd_cb_t *) data; |
181 cmd_cb_t *cb = (cmd_cb_t *) data; |
158 |
182 |
159 cmd_channels = g_slist_remove (cmd_channels, data); |
183 cmd_channels = g_slist_remove (cmd_channels, data); |
160 |
184 |
161 // May conflict - will be called during source removal? |
185 if (cb->source) |
162 // if (cb->source) |
186 g_source_remove (cb->source); |
163 // g_source_remove (cb->source); |
|
164 if (cb->channel) |
187 if (cb->channel) |
165 g_io_channel_unref (cb->channel); |
188 g_io_channel_unref (cb->channel); |
|
189 if (cb -> subject) |
|
190 g_free (cb -> subject); |
166 g_free (cb->jid); |
191 g_free (cb->jid); |
167 g_free (cb); |
192 g_free (cb); |
168 } |
193 } |
169 |
194 |
170 static void do_cmd (char *arg) |
195 static void do_cmd (char *arg) |
208 execl (shell, shell, "-c", arg, NULL); |
234 execl (shell, shell, "-c", arg, NULL); |
209 } |
235 } |
210 } |
236 } |
211 |
237 |
212 if (res == -1) { |
238 if (res == -1) { |
213 scr_LogPrint (LPRINT_NORMAL, "Cannot fork child: %s", strerror (errno)); |
239 scr_LogPrint (LPRINT_NORMAL, "Cannot fork child: %s.", strerror (errno)); |
214 close (fd[0]); |
240 close (fd[0]); |
215 close (fd[1]); |
241 close (fd[1]); |
216 return; |
242 return; |
217 } |
243 } |
218 |
244 |
219 close (fd[1]); |
245 close (fd[1]); |
220 |
246 |
221 GIOChannel *channel = g_io_channel_unix_new (fd[0]); |
247 { |
222 |
248 GError *error = NULL; |
223 g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); |
249 |
224 g_io_channel_set_encoding (channel, NULL, NULL); |
250 channel = g_io_channel_unix_new (fd[0]); |
225 g_io_channel_set_close_on_unref (channel, TRUE); |
251 |
226 g_io_channel_set_buffered (channel, FALSE); |
252 g_io_channel_set_encoding (channel, NULL, &error); |
|
253 if (error) { |
|
254 scr_LogPrint (LPRINT_DEBUG, "cmd: Cannot unset channel encoding: %s.", error -> message); |
|
255 g_clear_error (&error); |
|
256 } |
|
257 g_io_channel_set_buffered (channel, FALSE); |
|
258 g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, &error); |
|
259 if (error) { |
|
260 scr_LogPrint (LPRINT_DEBUG, "cmd: Cannot set nonblocking flag on channel: %s.", error -> message); |
|
261 g_error_free (error); |
|
262 } |
|
263 g_io_channel_set_close_on_unref (channel, TRUE); |
|
264 } |
227 |
265 |
228 { |
266 { |
229 cmd_cb_t *cb = g_new (cmd_cb_t, 1); |
267 cmd_cb_t *cb = g_new (cmd_cb_t, 1); |
230 |
268 |
231 cb->jid = g_strdup (jid); |
269 if (settings_opt_get_int ("cmd_header")) { |
232 cb->input = g_string_new (NULL); |
270 if (is_room (jid)) { |
233 cb->channel = channel; |
271 gchar *mesg = g_strdup_printf ("$ %s", arg); |
234 cb->source = g_io_add_watch_full (channel, 0, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, |
272 gchar *utf = to_utf8 (mesg); |
235 cmd_reader, (gpointer) cb, cmd_destroy_data); |
273 g_free (mesg); |
|
274 cmd_send_msg (jid, NULL, utf); |
|
275 g_free (utf); |
|
276 cb -> subject = NULL; |
|
277 } else { |
|
278 gchar *header = g_strdup_printf ("[%%02d] $ %s", arg); |
|
279 cb -> subject = to_utf8 (header); |
|
280 g_free (header); |
|
281 } |
|
282 } |
|
283 |
|
284 cb -> jid = g_strdup (jid); |
|
285 cb -> input = g_string_new (NULL); |
|
286 cb -> seq = 0; |
|
287 cb -> channel = channel; |
|
288 cb -> source = g_io_add_watch_full (channel, 0, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, |
|
289 cmd_reader, (gpointer) cb, cmd_destroy_data); |
236 |
290 |
237 cmd_channels = g_slist_append (cmd_channels, cb); |
291 cmd_channels = g_slist_append (cmd_channels, cb); |
238 } |
292 } |
239 } |
293 } |
240 } |
294 } |