rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / irc / cmds.c
blob5a418539701eaf5a9ad5082b689d83cca05683c8
1 /**
2 * purple
4 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
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
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include "internal.h"
22 #include <purple.h>
24 #include "irc.h"
27 static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops);
29 int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
31 PurpleConversation *convo = purple_conversations_find_with_account(target, irc->account);
32 char *buf;
34 if (!convo)
35 return 1;
37 buf = g_strdup_printf(_("Unknown command: %s"), cmd);
38 purple_conversation_write_system_message(convo, buf, PURPLE_MESSAGE_NO_LOG);
39 g_free(buf);
41 return 1;
44 int irc_cmd_away(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
46 char *buf, *message;
48 if (args[0] && !purple_strequal(cmd, "back")) {
49 message = purple_markup_strip_html(args[0]);
50 purple_util_chrreplace(message, '\n', ' ');
51 buf = irc_format(irc, "v:", "AWAY", message);
52 g_free(message);
53 } else {
54 buf = irc_format(irc, "v", "AWAY");
56 irc_send(irc, buf);
57 g_free(buf);
59 return 0;
62 int irc_cmd_ctcp(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
64 /* we have defined args as args[0] is target and args[1] is ctcp command */
65 char *buf;
66 GString *string;
68 /* check if we have args */
69 if (!args || !args[0] || !args[1])
70 return 0;
72 /* TODO:strip newlines or send each line as separate ctcp or something
73 * actually, this shouldn't be done here but somewhere else since irc should support escaping newlines */
75 string = g_string_new(args[1]);
76 g_string_prepend_c (string,'\001');
77 g_string_append_c (string,'\001');
78 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], string->str);
79 g_string_free(string,TRUE);
81 irc_send(irc, buf);
82 g_free(buf);
84 return 1;
87 int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
89 PurpleConnection *gc = purple_account_get_connection(irc->account);
90 char *action, *escaped, *dst, **newargs;
91 const char *src;
92 char *msg;
93 PurpleConversation *convo;
94 PurpleMessage *pmsg;
96 if (!args || !args[0] || !gc)
97 return 0;
99 convo = purple_conversations_find_with_account(target, irc->account);
101 msg = g_strdup_printf("/me %s", args[0]);
103 /* XXX: we'd prefer to keep this in conversation.c */
104 if (PURPLE_IS_IM_CONVERSATION(convo)) {
105 pmsg = purple_message_new_outgoing(
106 purple_conversation_get_name(convo), msg, 0);
108 purple_signal_emit(purple_conversations_get_handle(),
109 "sending-im-msg", irc->account, pmsg);
110 } else {
111 pmsg = purple_message_new_outgoing(NULL, msg, 0);
113 purple_signal_emit(purple_conversations_get_handle(),
114 "sending-chat-msg", irc->account, pmsg,
115 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
118 g_free(msg);
119 if (purple_message_is_empty(pmsg))
120 return 0;
121 msg = g_strdup(purple_message_get_contents(pmsg)); /* XXX: is it really necessary? */
123 if (strncmp(msg, "/me ", 4) != 0) {
124 newargs = g_new0(char *, 2);
125 newargs[0] = g_strdup(target);
126 newargs[1] = msg;
128 irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
130 g_free(newargs[0]);
131 g_free(newargs);
132 } else {
133 action = g_malloc(strlen(&msg[4]) + 10);
135 sprintf(action, "\001ACTION ");
137 src = &msg[4];
138 dst = action + 8;
139 while (*src) {
140 if (*src == '\n') {
141 if (*(src + 1) == '\0') {
142 break;
143 } else {
144 *dst++ = ' ';
145 src++;
146 continue;
149 *dst++ = *src++;
151 *dst++ = '\001';
152 *dst = '\0';
154 newargs = g_new0(char *, 2);
155 newargs[0] = g_strdup(target);
156 newargs[1] = action;
157 irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
158 g_free(newargs[0]);
159 g_free(newargs);
160 g_free(action);
163 /* XXX: we'd prefer to keep this in conversation.c */
164 if (PURPLE_IS_IM_CONVERSATION(convo)) {
165 purple_signal_emit(purple_conversations_get_handle(),
166 "sent-im-msg", irc->account, pmsg);
167 } else {
168 purple_signal_emit(purple_conversations_get_handle(),
169 "sent-chat-msg", irc->account, pmsg,
170 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
173 g_free(msg);
175 if (convo) {
176 escaped = g_markup_escape_text(args[0], -1);
177 action = g_strdup_printf("/me %s", escaped);
178 g_free(escaped);
179 if (action[strlen(action) - 1] == '\n')
180 action[strlen(action) - 1] = '\0';
181 if (PURPLE_IS_CHAT_CONVERSATION(convo))
182 purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)),
183 purple_connection_get_display_name(gc),
184 PURPLE_MESSAGE_SEND, action, time(NULL));
185 else
186 purple_conversation_write_message(convo, purple_message_new_outgoing(
187 purple_connection_get_display_name(gc), action, 0));
188 g_free(action);
191 return 1;
194 int irc_cmd_ctcp_version(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
196 char *buf;
198 if (!args || !args[0])
199 return 0;
201 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], "\001VERSION\001");
202 irc_send(irc, buf);
203 g_free(buf);
205 return 0;
208 int irc_cmd_invite(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
210 char *buf;
212 if (!args || !args[0] || !(args[1] || target))
213 return 0;
215 buf = irc_format(irc, "vnc", "INVITE", args[0], args[1] ? args[1] : target);
216 irc_send(irc, buf);
217 g_free(buf);
219 return 0;
222 int irc_cmd_join(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
224 char *buf;
226 if (!args || !args[0])
227 return 0;
229 if (args[1])
230 buf = irc_format(irc, "vcv", "JOIN", args[0], args[1]);
231 else
232 buf = irc_format(irc, "vc", "JOIN", args[0]);
233 irc_send(irc, buf);
234 g_free(buf);
236 return 0;
239 int irc_cmd_kick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
241 char *buf;
243 if (!args || !args[0])
244 return 0;
246 if (!purple_conversations_find_chat_with_account(target, irc->account))
247 return 0;
249 if (args[1])
250 buf = irc_format(irc, "vcn:", "KICK", target, args[0], args[1]);
251 else
252 buf = irc_format(irc, "vcn", "KICK", target, args[0]);
253 irc_send(irc, buf);
254 g_free(buf);
256 return 0;
259 int irc_cmd_list(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
261 purple_roomlist_show_with_account(irc->account);
263 return 0;
266 int irc_cmd_mode(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
268 PurpleConnection *gc;
269 char *buf;
271 if (!args)
272 return 0;
274 if (purple_strequal(cmd, "mode")) {
275 if (!args[0] && irc_ischannel(target))
276 buf = irc_format(irc, "vc", "MODE", target);
277 else if (args[0] && (*args[0] == '+' || *args[0] == '-'))
278 buf = irc_format(irc, "vcn", "MODE", target, args[0]);
279 else if (args[0])
280 buf = irc_format(irc, "vn", "MODE", args[0]);
281 else
282 return 0;
283 } else if (purple_strequal(cmd, "umode")) {
284 if (!args[0])
285 return 0;
286 gc = purple_account_get_connection(irc->account);
287 buf = irc_format(irc, "vnc", "MODE", purple_connection_get_display_name(gc), args[0]);
288 } else {
289 return 0;
292 irc_send(irc, buf);
293 g_free(buf);
295 return 0;
298 int irc_cmd_names(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
300 char *buf;
302 if (!args || (!args[0] && !irc_ischannel(target)))
303 return 0;
305 buf = irc_format(irc, "vc", "NAMES", args[0] ? args[0] : target);
306 irc_send(irc, buf);
307 g_free(buf);
309 return 0;
312 int irc_cmd_nick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
314 char *buf;
316 if (!args || !args[0])
317 return 0;
319 buf = irc_format(irc, "v:", "NICK", args[0]);
320 g_free(irc->reqnick);
321 irc->reqnick = g_strdup(args[0]);
322 irc->nickused = FALSE;
323 irc_send(irc, buf);
324 g_free(buf);
326 return 0;
329 int irc_cmd_op(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
331 char **nicks, **ops, *sign, *mode;
332 int i = 0, used = 0;
334 if (!args || !args[0] || !*args[0])
335 return 0;
337 if (purple_strequal(cmd, "op")) {
338 sign = "+";
339 mode = "o";
340 } else if (purple_strequal(cmd, "deop")) {
341 sign = "-";
342 mode = "o";
343 } else if (purple_strequal(cmd, "voice")) {
344 sign = "+";
345 mode = "v";
346 } else if (purple_strequal(cmd, "devoice")) {
347 sign = "-";
348 mode = "v";
349 } else {
350 purple_debug(PURPLE_DEBUG_ERROR, "irc", "invalid 'op' command '%s'\n", cmd);
351 return 0;
354 nicks = g_strsplit(args[0], " ", -1);
356 for (i = 0; nicks[i]; i++)
357 /* nothing */;
358 ops = g_new0(char *, i * 2 + 1);
360 for (i = 0; nicks[i]; i++) {
361 if (*nicks[i]) {
362 ops[used++] = mode;
363 ops[used++] = nicks[i];
367 irc_do_mode(irc, target, sign, ops);
368 g_free(ops);
369 g_strfreev(nicks);
371 return 0;
374 int irc_cmd_part(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
376 char *buf;
378 if (!args)
379 return 0;
381 if (args[1])
382 buf = irc_format(irc, "vc:", "PART", args[0] ? args[0] : target, args[1]);
383 else
384 buf = irc_format(irc, "vc", "PART", args[0] ? args[0] : target);
385 irc_send(irc, buf);
386 g_free(buf);
388 return 0;
391 int irc_cmd_ping(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
393 char *stamp;
394 char *buf;
396 if (args && args[0]) {
397 if (irc_ischannel(args[0]))
398 return 0;
399 stamp = g_strdup_printf("\001PING %lu\001", time(NULL));
400 buf = irc_format(irc, "vn:", "PRIVMSG", args[0], stamp);
401 g_free(stamp);
402 } else if (target) {
403 stamp = g_strdup_printf("%s %lu", target, time(NULL));
404 buf = irc_format(irc, "v:", "PING", stamp);
405 g_free(stamp);
406 } else {
407 stamp = g_strdup_printf("%lu", time(NULL));
408 buf = irc_format(irc, "vv", "PING", stamp);
409 g_free(stamp);
411 irc_send(irc, buf);
412 g_free(buf);
414 return 0;
417 int irc_cmd_privmsg(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
419 int max_privmsg_arg_len;
420 const char *cur, *end;
421 gchar *salvaged;
422 char *msg, *buf;
424 if (!args || !args[0] || !args[1])
425 return 0;
427 max_privmsg_arg_len = IRC_MAX_MSG_SIZE - strlen(args[0]) - 64;
428 salvaged = purple_utf8_salvage(args[1]);
429 cur = salvaged;
430 end = salvaged;
431 while (*end && *cur) {
432 end = strchr(cur, '\n');
433 if (!end)
434 end = cur + strlen(cur);
435 if (end - cur > max_privmsg_arg_len) {
436 /* this call is used to find the last valid character position in the first
437 * max_privmsg_arg_len bytes of the utf-8 message
439 g_utf8_validate(cur, max_privmsg_arg_len, &end);
442 msg = g_strndup(cur, end - cur);
444 if(purple_strequal(cmd, "notice"))
445 buf = irc_format(irc, "vt:", "NOTICE", args[0], msg);
446 else
447 buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg);
449 irc_send(irc, buf);
450 g_free(msg);
451 g_free(buf);
452 cur = end;
453 if(*cur == '\n') {
454 cur++;
458 g_free(salvaged);
460 return 0;
463 int irc_cmd_quit(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
465 char *buf;
467 if (!irc->quitting) {
469 * Use purple_account_get_string(irc->account, "quitmsg", IRC_DEFAULT_QUIT)
470 * and uncomment the appropriate account preference in irc.c if we
471 * decide we want custom quit messages.
473 buf = irc_format(irc, "v:", "QUIT", (args && args[0]) ? args[0] : IRC_DEFAULT_QUIT);
474 irc_send(irc, buf);
475 g_free(buf);
477 irc->quitting = TRUE;
479 if (!purple_account_is_disconnecting(irc->account))
480 purple_account_set_status(irc->account, "offline", TRUE, NULL);
483 return 0;
486 int irc_cmd_quote(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
488 char *buf;
490 if (!args || !args[0])
491 return 0;
493 buf = irc_format(irc, "n", args[0]);
494 irc_send(irc, buf);
495 g_free(buf);
497 return 0;
500 int irc_cmd_query(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
502 PurpleIMConversation *im;
503 PurpleConnection *gc;
505 if (!args || !args[0])
506 return 0;
508 im = purple_im_conversation_new(irc->account, args[0]);
509 purple_conversation_present(PURPLE_CONVERSATION(im));
511 if (args[1]) {
512 gc = purple_account_get_connection(irc->account);
513 irc_cmd_privmsg(irc, cmd, target, args);
514 purple_conversation_write_message(PURPLE_CONVERSATION(im),
515 purple_message_new_outgoing(
516 purple_connection_get_display_name(gc), args[1], 0));
519 return 0;
522 int irc_cmd_remove(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
524 char *buf;
526 if (!args || !args[0])
527 return 0;
529 if (!irc_ischannel(target)) /* not a channel, punt */
530 return 0;
532 if (args[1])
533 buf = irc_format(irc, "vcn:", "REMOVE", target, args[0], args[1]);
534 else
535 buf = irc_format(irc, "vcn", "REMOVE", target, args[0]);
536 irc_send(irc, buf);
537 g_free(buf);
539 return 0;
542 int irc_cmd_service(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
544 char *capital_cmd, *buf;
546 if (!args || !args[0])
547 return 0;
549 /* cmd will be one of nickserv, chanserv, memoserv or operserv */
550 capital_cmd = g_ascii_strup(cmd, -1);
551 buf = irc_format(irc, "v:", capital_cmd, args[0]);
552 irc_send(irc, buf);
553 g_free(capital_cmd);
554 g_free(buf);
556 return 0;
559 int irc_cmd_time(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
561 char *buf;
563 buf = irc_format(irc, "v", "TIME");
564 irc_send(irc, buf);
565 g_free(buf);
567 return 0;
570 int irc_cmd_topic(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
572 char *buf;
573 const char *topic;
574 PurpleChatConversation *chat;
576 if (!args)
577 return 0;
579 chat = purple_conversations_find_chat_with_account(target, irc->account);
580 if (!chat)
581 return 0;
583 if (!args[0]) {
584 topic = purple_chat_conversation_get_topic (chat);
586 if (topic) {
587 char *tmp, *tmp2;
588 tmp = g_markup_escape_text(topic, -1);
589 tmp2 = purple_markup_linkify(tmp);
590 buf = g_strdup_printf(_("current topic is: %s"), tmp2);
591 g_free(tmp);
592 g_free(tmp2);
593 } else
594 buf = g_strdup(_("No topic is set"));
595 purple_conversation_write_system_message(
596 PURPLE_CONVERSATION(chat), buf, PURPLE_MESSAGE_NO_LOG);
597 g_free(buf);
599 return 0;
602 buf = irc_format(irc, "vt:", "TOPIC", target, args[0]);
603 irc_send(irc, buf);
604 g_free(buf);
606 return 0;
609 int irc_cmd_wallops(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
611 char *buf;
613 if (!args || !args[0])
614 return 0;
616 if (purple_strequal(cmd, "wallops"))
617 buf = irc_format(irc, "v:", "WALLOPS", args[0]);
618 else if (purple_strequal(cmd, "operwall"))
619 buf = irc_format(irc, "v:", "OPERWALL", args[0]);
620 else
621 return 0;
623 irc_send(irc, buf);
624 g_free(buf);
626 return 0;
629 int irc_cmd_whois(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
631 char *buf;
633 if (!args || !args[0])
634 return 0;
636 if (args[1]) {
637 buf = irc_format(irc, "vvn", "WHOIS", args[0], args[1]);
638 irc->whois.nick = g_strdup(args[1]);
639 } else {
640 buf = irc_format(irc, "vn", "WHOIS", args[0]);
641 irc->whois.nick = g_strdup(args[0]);
644 irc_send(irc, buf);
645 g_free(buf);
647 return 0;
650 int irc_cmd_whowas(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
652 char *buf;
654 if (!args || !args[0])
655 return 0;
657 buf = irc_format(irc, "vn", "WHOWAS", args[0]);
659 irc->whois.nick = g_strdup(args[0]);
660 irc_send(irc, buf);
661 g_free(buf);
663 return 0;
666 static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops)
668 char *buf, mode[5];
669 int i = 0;
671 if (!sign)
672 return;
674 while (ops[i]) {
675 if (ops[i + 2] && ops[i + 4]) {
676 g_snprintf(mode, sizeof(mode), "%s%s%s%s", sign,
677 ops[i], ops[i + 2], ops[i + 4]);
678 buf = irc_format(irc, "vcvnnn", "MODE", target, mode,
679 ops[i + 1], ops[i + 3], ops[i + 5]);
680 i += 6;
681 } else if (ops[i + 2]) {
682 g_snprintf(mode, sizeof(mode), "%s%s%s",
683 sign, ops[i], ops[i + 2]);
684 buf = irc_format(irc, "vcvnn", "MODE", target, mode,
685 ops[i + 1], ops[i + 3]);
686 i += 4;
687 } else {
688 g_snprintf(mode, sizeof(mode), "%s%s", sign, ops[i]);
689 buf = irc_format(irc, "vcvn", "MODE", target, mode, ops[i + 1]);
690 i += 2;
692 irc_send(irc, buf);
693 g_free(buf);
696 return;