1 /*
2  * irssi - Implements several irssi features for Purple
3  * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
4  * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org>
5  * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02111-1301, USA.
21  */
22 
23 /* If you can't figure out what this line is for, DON'T TOUCH IT. */
24 #include "../common/pp_internal.h"
25 
26 #ifdef HAVE_REGEX_H
27 # include <regex.h>
28 #endif
29 
30 #include <plugin.h>
31 #include <cmds.h>
32 
33 #include "textfmt.h"
34 
35 /******************************************************************************
36  * Globals
37  *****************************************************************************/
38 #define EXPRESSION	"(^|[ 	])(%s)([^ ]+)(%s)($|[ 	])"
39 
40 enum {
41 	GROUP_ALL = 0,
42 	GROUP_LEADING,
43 	GROUP_START_TOKEN,
44 	GROUP_TEXT,
45 	GROUP_END_TOKEN,
46 	GROUP_TRAILING,
47 	GROUP_TOTAL
48 };
49 
50 /******************************************************************************
51  * Helpers
52  *****************************************************************************/
53 
54 #ifdef HAVE_REGEX_H
55 
56 #define FORMAT(account, message) { \
57 	if(!(message)) \
58 		return FALSE; \
59 	if(!(*(message))) \
60 		return FALSE; \
61 	if(!(account)) \
62 		return FALSE; \
63 	\
64 	if(!(account)->gc) \
65 		return FALSE; \
66 	\
67 	if(!((account)->gc->flags & PURPLE_CONNECTION_HTML)) \
68 		return FALSE; \
69 	\
70 	if(!(message)) \
71 		return FALSE; \
72 	\
73 	*(message) = irssi_textfmt_regex(*(message)); \
74 	\
75 	return FALSE; \
76 }
77 
78 #define APPEND_GROUP(group) { \
79 	t = iter + matches[(group)].rm_so; \
80 	offset = matches[(group)].rm_eo - matches[(group)].rm_so; \
81 	str = g_string_append_len(str, t, offset); \
82 }
83 
84 static gchar *
irssi_textfmt_regex_helper(gchar * message,const gchar * token,const gchar * tag)85 irssi_textfmt_regex_helper(gchar *message, const gchar *token,
86 						   const gchar *tag)
87 {
88 	GString *str = NULL;
89 	gchar *ret = NULL, *expr = NULL, *iter = message, *t = NULL;
90 	regex_t regex;
91 	regmatch_t matches[GROUP_TOTAL];
92 
93 	/* build our expression */
94 	expr = g_strdup_printf(EXPRESSION, token, token);
95 
96 	/* compile the expression.
97 	 * We may want to move this so we only do it once for each type, but this
98 	 * is pretty cheap, resource-wise.
99 	 */
100 	if(regcomp(&regex, expr, REG_EXTENDED) != 0) {
101 		g_free(expr);
102 		return message;
103 	}
104 
105 	/* ok, we compiled fine, lets clean up our expression */
106 	g_free(expr);
107 
108 	/* now let's replce the matches.
109 	 * If we don't have any, drop out right away.
110 	 */
111 	if(regexec(&regex, iter, GROUP_TOTAL, matches, 0) != 0) {
112 		regfree(&regex);
113 		return message;
114 	}
115 
116 	/* create our GString.  Heh heh */
117 	str = g_string_new("");
118 
119 	/* now loop through the string looking for more matches */
120 	do {
121 		gint offset = 0;
122 
123 		if(matches[0].rm_eo == -1)
124 			break;
125 
126 		/* append everything up to the match */
127 		str = g_string_append_len(str, iter, matches[GROUP_ALL].rm_so);
128 
129 		/* determine the leading white space */
130 		APPEND_GROUP(GROUP_LEADING);
131 
132 		/* append the starting tag */
133 		g_string_append_printf(str, "<%s>", tag);
134 
135 		/* append the starting token */
136 		APPEND_GROUP(GROUP_START_TOKEN);
137 
138 		/* append the text */
139 		APPEND_GROUP(GROUP_TEXT);
140 
141 		/* append the ending token */
142 		APPEND_GROUP(GROUP_END_TOKEN);
143 
144 		/* append the ending tag */
145 		g_string_append_printf(str, "</%s>", tag);
146 
147 		/* append the trailing whitespace */
148 		APPEND_GROUP(GROUP_TRAILING);
149 
150 		/* move iter to the end of the match */
151 		iter += matches[GROUP_ALL].rm_eo;
152 	} while(regexec(&regex, iter, GROUP_TOTAL, matches, REG_NOTBOL) == 0);
153 
154 	regfree(&regex);
155 
156 	/* at this point, iter is either the remains of the text, of a single null
157 	 * terminator.  So throw it onto the GString.
158 	 */
159 	str = g_string_append(str, iter);
160 
161 	/* kill the passed in message */
162 	g_free(message);
163 
164 	/* take off our GString */
165 	ret = str->str;
166 	g_string_free(str, FALSE);
167 
168 	return ret;
169 }
170 
171 static gchar *
irssi_textfmt_regex(gchar * message)172 irssi_textfmt_regex(gchar *message) {
173 	message = irssi_textfmt_regex_helper(message, "\\*", "b");
174 	message = irssi_textfmt_regex_helper(message, "/", "i");
175 	message = irssi_textfmt_regex_helper(message, "-", "s");
176 	message = irssi_textfmt_regex_helper(message, "_", "u");
177 
178 #if 0
179 	/* don't add these until imhtml support them again */
180 	message = irssi_textfmt_regex_helper(message, "\\^", "sup");
181 	message = irssi_textfmt_regex_helper(message, "~", "sub");
182 	message = irssi_textfmt_regex_helper(message, "!", "pre");
183 #endif
184 
185 	return message;
186 }
187 
188 #endif
189 
190 /******************************************************************************
191  * Callbacks
192  *****************************************************************************/
193 #ifdef HAVE_REGEX_H
194 
195 static gboolean
irssi_textfmt_writing_cb(PurpleAccount * account,const gchar * who,gchar ** message,PurpleConversation * conv,PurpleMessageFlags flags)196 irssi_textfmt_writing_cb(PurpleAccount *account, const gchar *who,
197 						 gchar **message, PurpleConversation *conv,
198 						 PurpleMessageFlags flags)
199 {
200 	if(!(flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
201 	{
202 		return FALSE;
203 	}
204 
205 	FORMAT(account, message);
206 }
207 
208 static gboolean
irssi_textfmt_sending_im_cb(PurpleAccount * account,const gchar * receiver,gchar ** message)209 irssi_textfmt_sending_im_cb(PurpleAccount *account, const gchar *receiver,
210 							gchar **message)
211 {
212 	FORMAT(account, message);
213 }
214 
215 static gboolean
irssi_textfmt_sending_chat_cb(PurpleAccount * account,gchar ** message,gint id)216 irssi_textfmt_sending_chat_cb(PurpleAccount *account, gchar **message, gint id) {
217 	FORMAT(account, message);
218 }
219 
220 #endif
221 
222 /******************************************************************************
223  * "API"
224  *****************************************************************************/
225 void
irssi_textfmt_init(PurplePlugin * plugin)226 irssi_textfmt_init(PurplePlugin *plugin) {
227 #ifdef HAVE_REGEX_H
228 	void *handle;
229 
230 	handle = purple_conversations_get_handle();
231 
232 	purple_signal_connect(handle, "writing-im-msg", plugin,
233 						PURPLE_CALLBACK(irssi_textfmt_writing_cb), NULL);
234 	purple_signal_connect(handle, "writing-chat-msg", plugin,
235 						PURPLE_CALLBACK(irssi_textfmt_writing_cb), NULL);
236 	purple_signal_connect(handle, "sending-im-msg", plugin,
237 						PURPLE_CALLBACK(irssi_textfmt_sending_im_cb), NULL);
238 	purple_signal_connect(handle, "sending-chat-msg", plugin,
239 						PURPLE_CALLBACK(irssi_textfmt_sending_chat_cb), NULL);
240 #endif
241 }
242 
243 void
irssi_textfmt_uninit(PurplePlugin * plugin)244 irssi_textfmt_uninit(PurplePlugin *plugin) {
245 	/* Nothing to do here, purple kills our callbacks for us. */
246 }
247 
248