Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / protocols / irc / cmds.c
blob0cc5ae2231ea97bd59cf7461dd55a5b59e2cdcf9
1 /**
2 * @file cmds.c
4 * purple
6 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
25 #include "conversation.h"
26 #include "debug.h"
27 #include "notify.h"
28 #include "util.h"
30 #include "irc.h"
33 static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops);
35 int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
37 PurpleConversation *convo = purple_conversations_find_with_account(target, irc->account);
38 char *buf;
40 if (!convo)
41 return 1;
43 buf = g_strdup_printf(_("Unknown command: %s"), cmd);
44 purple_conversation_write_system_message(convo, buf, PURPLE_MESSAGE_NO_LOG);
45 g_free(buf);
47 return 1;
50 int irc_cmd_away(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
52 char *buf, *message;
54 if (args[0] && strcmp(cmd, "back")) {
55 message = purple_markup_strip_html(args[0]);
56 purple_util_chrreplace(message, '\n', ' ');
57 buf = irc_format(irc, "v:", "AWAY", message);
58 g_free(message);
59 } else {
60 buf = irc_format(irc, "v", "AWAY");
62 irc_send(irc, buf);
63 g_free(buf);
65 return 0;
68 int irc_cmd_ctcp(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
70 /* we have defined args as args[0] is target and args[1] is ctcp command */
71 char *buf;
72 GString *string;
74 /* check if we have args */
75 if (!args || !args[0] || !args[1])
76 return 0;
78 /* TODO:strip newlines or send each line as separate ctcp or something
79 * actually, this shouldn't be done here but somewhere else since irc should support escaping newlines */
81 string = g_string_new(args[1]);
82 g_string_prepend_c (string,'\001');
83 g_string_append_c (string,'\001');
84 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], string->str);
85 g_string_free(string,TRUE);
87 irc_send(irc, buf);
88 g_free(buf);
90 return 1;
93 int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
95 PurpleConnection *gc = purple_account_get_connection(irc->account);
96 char *action, *escaped, *dst, **newargs;
97 const char *src;
98 char *msg;
99 PurpleConversation *convo;
100 PurpleMessage *pmsg;
102 if (!args || !args[0] || !gc)
103 return 0;
105 convo = purple_conversations_find_with_account(target, irc->account);
107 msg = g_strdup_printf("/me %s", args[0]);
109 /* XXX: we'd prefer to keep this in conversation.c */
110 if (PURPLE_IS_IM_CONVERSATION(convo)) {
111 pmsg = purple_message_new_outgoing(
112 purple_conversation_get_name(convo), msg, 0);
114 purple_signal_emit(purple_conversations_get_handle(),
115 "sending-im-msg", irc->account, pmsg);
116 } else {
117 pmsg = purple_message_new_outgoing(NULL, msg, 0);
119 purple_signal_emit(purple_conversations_get_handle(),
120 "sending-chat-msg", irc->account, pmsg,
121 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
124 g_free(msg);
125 if (purple_message_is_empty(pmsg))
126 return 0;
127 msg = g_strdup(purple_message_get_contents(pmsg)); /* XXX: is it really necessary? */
129 if (strncmp(msg, "/me ", 4) != 0) {
130 newargs = g_new0(char *, 2);
131 newargs[0] = g_strdup(target);
132 newargs[1] = msg;
134 irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
136 g_free(newargs[0]);
137 g_free(newargs);
138 } else {
139 action = g_malloc(strlen(&msg[4]) + 10);
141 sprintf(action, "\001ACTION ");
143 src = &msg[4];
144 dst = action + 8;
145 while (*src) {
146 if (*src == '\n') {
147 if (*(src + 1) == '\0') {
148 break;
149 } else {
150 *dst++ = ' ';
151 src++;
152 continue;
155 *dst++ = *src++;
157 *dst++ = '\001';
158 *dst = '\0';
160 newargs = g_new0(char *, 2);
161 newargs[0] = g_strdup(target);
162 newargs[1] = action;
163 irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
164 g_free(newargs[0]);
165 g_free(newargs);
166 g_free(action);
169 /* XXX: we'd prefer to keep this in conversation.c */
170 if (PURPLE_IS_IM_CONVERSATION(convo)) {
171 purple_signal_emit(purple_conversations_get_handle(),
172 "sent-im-msg", irc->account, pmsg);
173 } else {
174 purple_signal_emit(purple_conversations_get_handle(),
175 "sent-chat-msg", irc->account, pmsg,
176 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
179 g_free(msg);
181 if (convo) {
182 escaped = g_markup_escape_text(args[0], -1);
183 action = g_strdup_printf("/me %s", escaped);
184 g_free(escaped);
185 if (action[strlen(action) - 1] == '\n')
186 action[strlen(action) - 1] = '\0';
187 if (PURPLE_IS_CHAT_CONVERSATION(convo))
188 purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)),
189 purple_connection_get_display_name(gc),
190 PURPLE_MESSAGE_SEND, action, time(NULL));
191 else
192 purple_conversation_write_message(convo, purple_message_new_outgoing(
193 purple_connection_get_display_name(gc), action, 0));
194 g_free(action);
197 return 1;
200 int irc_cmd_ctcp_version(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
202 char *buf;
204 if (!args || !args[0])
205 return 0;
207 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], "\001VERSION\001");
208 irc_send(irc, buf);
209 g_free(buf);
211 return 0;
214 int irc_cmd_invite(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
216 char *buf;
218 if (!args || !args[0] || !(args[1] || target))
219 return 0;
221 buf = irc_format(irc, "vnc", "INVITE", args[0], args[1] ? args[1] : target);
222 irc_send(irc, buf);
223 g_free(buf);
225 return 0;
228 int irc_cmd_join(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
230 char *buf;
232 if (!args || !args[0])
233 return 0;
235 if (args[1])
236 buf = irc_format(irc, "vcv", "JOIN", args[0], args[1]);
237 else
238 buf = irc_format(irc, "vc", "JOIN", args[0]);
239 irc_send(irc, buf);
240 g_free(buf);
242 return 0;
245 int irc_cmd_kick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
247 char *buf;
249 if (!args || !args[0])
250 return 0;
252 if (!purple_conversations_find_chat_with_account(target, irc->account))
253 return 0;
255 if (args[1])
256 buf = irc_format(irc, "vcn:", "KICK", target, args[0], args[1]);
257 else
258 buf = irc_format(irc, "vcn", "KICK", target, args[0]);
259 irc_send(irc, buf);
260 g_free(buf);
262 return 0;
265 int irc_cmd_list(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
267 purple_roomlist_show_with_account(irc->account);
269 return 0;
272 int irc_cmd_mode(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
274 PurpleConnection *gc;
275 char *buf;
277 if (!args)
278 return 0;
280 if (!strcmp(cmd, "mode")) {
281 if (!args[0] && irc_ischannel(target))
282 buf = irc_format(irc, "vc", "MODE", target);
283 else if (args[0] && (*args[0] == '+' || *args[0] == '-'))
284 buf = irc_format(irc, "vcn", "MODE", target, args[0]);
285 else if (args[0])
286 buf = irc_format(irc, "vn", "MODE", args[0]);
287 else
288 return 0;
289 } else if (!strcmp(cmd, "umode")) {
290 if (!args[0])
291 return 0;
292 gc = purple_account_get_connection(irc->account);
293 buf = irc_format(irc, "vnc", "MODE", purple_connection_get_display_name(gc), args[0]);
294 } else {
295 return 0;
298 irc_send(irc, buf);
299 g_free(buf);
301 return 0;
304 int irc_cmd_names(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
306 char *buf;
308 if (!args || (!args[0] && !irc_ischannel(target)))
309 return 0;
311 buf = irc_format(irc, "vc", "NAMES", args[0] ? args[0] : target);
312 irc_send(irc, buf);
313 g_free(buf);
315 return 0;
318 int irc_cmd_nick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
320 char *buf;
322 if (!args || !args[0])
323 return 0;
325 buf = irc_format(irc, "v:", "NICK", args[0]);
326 g_free(irc->reqnick);
327 irc->reqnick = g_strdup(args[0]);
328 irc->nickused = FALSE;
329 irc_send(irc, buf);
330 g_free(buf);
332 return 0;
335 int irc_cmd_op(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
337 char **nicks, **ops, *sign, *mode;
338 int i = 0, used = 0;
340 if (!args || !args[0] || !*args[0])
341 return 0;
343 if (!strcmp(cmd, "op")) {
344 sign = "+";
345 mode = "o";
346 } else if (!strcmp(cmd, "deop")) {
347 sign = "-";
348 mode = "o";
349 } else if (!strcmp(cmd, "voice")) {
350 sign = "+";
351 mode = "v";
352 } else if (!strcmp(cmd, "devoice")) {
353 sign = "-";
354 mode = "v";
355 } else {
356 purple_debug(PURPLE_DEBUG_ERROR, "irc", "invalid 'op' command '%s'\n", cmd);
357 return 0;
360 nicks = g_strsplit(args[0], " ", -1);
362 for (i = 0; nicks[i]; i++)
363 /* nothing */;
364 ops = g_new0(char *, i * 2 + 1);
366 for (i = 0; nicks[i]; i++) {
367 if (*nicks[i]) {
368 ops[used++] = mode;
369 ops[used++] = nicks[i];
373 irc_do_mode(irc, target, sign, ops);
374 g_free(ops);
375 g_strfreev(nicks);
377 return 0;
380 int irc_cmd_part(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
382 char *buf;
384 if (!args)
385 return 0;
387 if (args[1])
388 buf = irc_format(irc, "vc:", "PART", args[0] ? args[0] : target, args[1]);
389 else
390 buf = irc_format(irc, "vc", "PART", args[0] ? args[0] : target);
391 irc_send(irc, buf);
392 g_free(buf);
394 return 0;
397 int irc_cmd_ping(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
399 char *stamp;
400 char *buf;
402 if (args && args[0]) {
403 if (irc_ischannel(args[0]))
404 return 0;
405 stamp = g_strdup_printf("\001PING %lu\001", time(NULL));
406 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], stamp);
407 g_free(stamp);
408 } else if (target) {
409 stamp = g_strdup_printf("%s %lu", target, time(NULL));
410 buf = irc_format(irc, "v:", "PING", stamp);
411 g_free(stamp);
412 } else {
413 stamp = g_strdup_printf("%lu", time(NULL));
414 buf = irc_format(irc, "vv", "PING", stamp);
415 g_free(stamp);
417 irc_send(irc, buf);
418 g_free(buf);
420 return 0;
423 int irc_cmd_privmsg(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
425 int max_privmsg_arg_len;
426 const char *cur, *end;
427 gchar *salvaged;
428 char *msg, *buf;
430 if (!args || !args[0] || !args[1])
431 return 0;
433 max_privmsg_arg_len = IRC_MAX_MSG_SIZE - strlen(args[0]) - 64;
434 salvaged = purple_utf8_salvage(args[1]);
435 cur = salvaged;
436 end = salvaged;
437 while (*end && *cur) {
438 end = strchr(cur, '\n');
439 if (!end)
440 end = cur + strlen(cur);
441 if (end - cur > max_privmsg_arg_len) {
442 /* this call is used to find the last valid character position in the first
443 * max_privmsg_arg_len bytes of the utf-8 message
445 g_utf8_validate(cur, max_privmsg_arg_len, &end);
448 msg = g_strndup(cur, end - cur);
450 if(!strcmp(cmd, "notice"))
451 buf = irc_format(irc, "vt:", "NOTICE", args[0], msg);
452 else
453 buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg);
455 irc_send(irc, buf);
456 g_free(msg);
457 g_free(buf);
458 cur = end;
459 if(*cur == '\n') {
460 cur++;
464 g_free(salvaged);
466 return 0;
469 int irc_cmd_quit(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
471 char *buf;
473 if (!irc->quitting) {
475 * Use purple_account_get_string(irc->account, "quitmsg", IRC_DEFAULT_QUIT)
476 * and uncomment the appropriate account preference in irc.c if we
477 * decide we want custom quit messages.
479 buf = irc_format(irc, "v:", "QUIT", (args && args[0]) ? args[0] : IRC_DEFAULT_QUIT);
480 irc_send(irc, buf);
481 g_free(buf);
483 irc->quitting = TRUE;
485 if (!purple_account_is_disconnecting(irc->account))
486 purple_account_set_status(irc->account, "offline", TRUE, NULL);
489 return 0;
492 int irc_cmd_quote(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
494 char *buf;
496 if (!args || !args[0])
497 return 0;
499 buf = irc_format(irc, "n", args[0]);
500 irc_send(irc, buf);
501 g_free(buf);
503 return 0;
506 int irc_cmd_query(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
508 PurpleIMConversation *im;
509 PurpleConnection *gc;
511 if (!args || !args[0])
512 return 0;
514 im = purple_im_conversation_new(irc->account, args[0]);
515 purple_conversation_present(PURPLE_CONVERSATION(im));
517 if (args[1]) {
518 gc = purple_account_get_connection(irc->account);
519 irc_cmd_privmsg(irc, cmd, target, args);
520 purple_conversation_write_message(PURPLE_CONVERSATION(im),
521 purple_message_new_outgoing(
522 purple_connection_get_display_name(gc), args[1], 0));
525 return 0;
528 int irc_cmd_remove(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
530 char *buf;
532 if (!args || !args[0])
533 return 0;
535 if (!irc_ischannel(target)) /* not a channel, punt */
536 return 0;
538 if (args[1])
539 buf = irc_format(irc, "vcn:", "REMOVE", target, args[0], args[1]);
540 else
541 buf = irc_format(irc, "vcn", "REMOVE", target, args[0]);
542 irc_send(irc, buf);
543 g_free(buf);
545 return 0;
548 int irc_cmd_service(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
550 char *capital_cmd, *buf;
552 if (!args || !args[0])
553 return 0;
555 /* cmd will be one of nickserv, chanserv, memoserv or operserv */
556 capital_cmd = g_ascii_strup(cmd, -1);
557 buf = irc_format(irc, "v:", capital_cmd, args[0]);
558 irc_send(irc, buf);
559 g_free(capital_cmd);
560 g_free(buf);
562 return 0;
565 int irc_cmd_time(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
567 char *buf;
569 buf = irc_format(irc, "v", "TIME");
570 irc_send(irc, buf);
571 g_free(buf);
573 return 0;
576 int irc_cmd_topic(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
578 char *buf;
579 const char *topic;
580 PurpleChatConversation *chat;
582 if (!args)
583 return 0;
585 chat = purple_conversations_find_chat_with_account(target, irc->account);
586 if (!chat)
587 return 0;
589 if (!args[0]) {
590 topic = purple_chat_conversation_get_topic (chat);
592 if (topic) {
593 char *tmp, *tmp2;
594 tmp = g_markup_escape_text(topic, -1);
595 tmp2 = purple_markup_linkify(tmp);
596 buf = g_strdup_printf(_("current topic is: %s"), tmp2);
597 g_free(tmp);
598 g_free(tmp2);
599 } else
600 buf = g_strdup(_("No topic is set"));
601 purple_conversation_write_system_message(
602 PURPLE_CONVERSATION(chat), buf, PURPLE_MESSAGE_NO_LOG);
603 g_free(buf);
605 return 0;
608 buf = irc_format(irc, "vt:", "TOPIC", target, args[0]);
609 irc_send(irc, buf);
610 g_free(buf);
612 return 0;
615 int irc_cmd_wallops(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
617 char *buf;
619 if (!args || !args[0])
620 return 0;
622 if (!strcmp(cmd, "wallops"))
623 buf = irc_format(irc, "v:", "WALLOPS", args[0]);
624 else if (!strcmp(cmd, "operwall"))
625 buf = irc_format(irc, "v:", "OPERWALL", args[0]);
626 else
627 return 0;
629 irc_send(irc, buf);
630 g_free(buf);
632 return 0;
635 int irc_cmd_whois(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
637 char *buf;
639 if (!args || !args[0])
640 return 0;
642 if (args[1]) {
643 buf = irc_format(irc, "vvn", "WHOIS", args[0], args[1]);
644 irc->whois.nick = g_strdup(args[1]);
645 } else {
646 buf = irc_format(irc, "vn", "WHOIS", args[0]);
647 irc->whois.nick = g_strdup(args[0]);
650 irc_send(irc, buf);
651 g_free(buf);
653 return 0;
656 int irc_cmd_whowas(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
658 char *buf;
660 if (!args || !args[0])
661 return 0;
663 buf = irc_format(irc, "vn", "WHOWAS", args[0]);
665 irc->whois.nick = g_strdup(args[0]);
666 irc_send(irc, buf);
667 g_free(buf);
669 return 0;
672 static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops)
674 char *buf, mode[5];
675 int i = 0;
677 if (!sign)
678 return;
680 while (ops[i]) {
681 if (ops[i + 2] && ops[i + 4]) {
682 g_snprintf(mode, sizeof(mode), "%s%s%s%s", sign,
683 ops[i], ops[i + 2], ops[i + 4]);
684 buf = irc_format(irc, "vcvnnn", "MODE", target, mode,
685 ops[i + 1], ops[i + 3], ops[i + 5]);
686 i += 6;
687 } else if (ops[i + 2]) {
688 g_snprintf(mode, sizeof(mode), "%s%s%s",
689 sign, ops[i], ops[i + 2]);
690 buf = irc_format(irc, "vcvnn", "MODE", target, mode,
691 ops[i + 1], ops[i + 3]);
692 i += 4;
693 } else {
694 g_snprintf(mode, sizeof(mode), "%s%s", sign, ops[i]);
695 buf = irc_format(irc, "vcvn", "MODE", target, mode, ops[i + 1]);
696 i += 2;
698 irc_send(irc, buf);
699 g_free(buf);
702 return;