1 /*        Misc utility functions for the Pidgin-Encryption plugin         */
2 /*             Copyright (C) 2001-2003 William Tompkins                   */
3 
4 /* This plugin is free software, distributed under the GNU General Public */
5 /* License.                                                               */
6 /* Please see the file "COPYING" distributed with this source code        */
7 /* for more details                                                       */
8 /*                                                                        */
9 /*                                                                        */
10 /*    This software is distributed in the hope that it will be useful,    */
11 /*   but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    */
13 /*   General Public License for more details.                             */
14 
15 /*   To compile and use:                                                  */
16 /*     See INSTALL file.                                                  */
17 
18 #include "internal.h"
19 
20 #include <string.h>
21 #include <stdlib.h>
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <unistd.h>
28 
29 
30 #include <debug.h>
31 
32 #ifdef _WIN32
33 #include <win32dep.h>
34 #endif
35 
36 #include "nls.h"
37 #include "cryptutil.h"
38 #include "rsa_nss.h"
39 
40 #include <base64.h>
41 
42 
43 
PE_clear_string(char * s)44 void PE_clear_string(char* s) {
45    while(*s != 0) {
46       *(s++) = 0;
47    }
48 }
49 
PE_escape_name(GString * name)50 void PE_escape_name(GString* name) {
51    int pos = 0;
52 
53    // Note: name->len and name->str can change as we modify name...
54    while (pos < name->len) {
55       switch (name->str[pos]) {
56       case ' ':
57          // space -> "\s"
58          g_string_erase(name, pos, 1);
59          g_string_insert(name, pos, "\\s");
60          pos += 2;
61          break;
62       case ',':
63          // comma -> "\c"
64          g_string_erase(name, pos, 1);
65          g_string_insert(name, pos, "\\c");
66          pos += 2;
67          break;
68       case '\\':
69          // backslash -> "\\"
70          g_string_erase(name, pos, 1);
71          g_string_insert(name, pos, "\\");
72          pos += 2;
73          break;
74       default:
75          ++pos;
76          break;
77       }
78    }
79 }
80 
PE_unescape_name(char * origname)81 void PE_unescape_name(char* origname) {
82    GString *name = g_string_new(origname);
83    int pos = 0;
84 
85    while (pos < name->len) {
86       if (name->str[pos] == '\\') {
87          g_string_erase(name, pos, 1);
88          switch (name->str[pos]) {
89          case 's':
90             // \s -> space
91             name->str[pos] = ' ';
92             ++pos;
93             break;
94          case 'c':
95             // \c -> comma
96             name->str[pos] = ',';
97             ++pos;
98             break;
99          default:
100             // leave the char that followed the backslash
101             ++pos;
102             break;
103          }
104       } else {
105          ++pos;
106       }
107    }
108 
109    // string can only be shorter, so this copy is ok
110    strcpy(origname, name->str);
111 
112    g_string_free(name, TRUE);
113 }
114 
115 /* Convert 'num' bytes into an ascii string.  */
PE_bytes_to_str(char * str,unsigned char * bytes,int num)116 void PE_bytes_to_str(char *str, unsigned char *bytes, int num) {
117    char* tmp = BTOA_DataToAscii(bytes, num);
118    GString* tmp2 = g_string_new(tmp);
119 
120    PE_strip_returns(tmp2);
121    strcpy(str, tmp2->str);
122 
123    PORT_Free(tmp);
124    g_string_free(tmp2, TRUE);
125 }
126 
127 /* Convert ascii string back into bytes.  Returns number of bytes */
PE_str_to_bytes(unsigned char * bytes,char * cstr)128 unsigned int PE_str_to_bytes(unsigned char *bytes, char *cstr) {
129    unsigned int tmplen;
130    unsigned char* tmp = ATOB_AsciiToData(cstr, &tmplen);
131    if (tmp == NULL) {
132       purple_debug(PURPLE_DEBUG_ERROR, "pidgin-encryption", _("Invalid Base64 data, length %u\n"),
133                    (unsigned)strlen(cstr));
134       return 0;
135    }
136 
137    memcpy(bytes, tmp, tmplen);
138    PORT_Free(tmp);
139 
140    return tmplen;
141 }
142 
143 
PE_strip_returns(GString * s)144 GString* PE_strip_returns(GString* s) {
145    gchar *strippedStr, **strArray;
146    int i;
147 
148    strArray = g_strsplit(s->str, "\n", 100);
149 
150    for (i = 0; strArray[i] != 0; ++i) {
151       g_strstrip(strArray[i]);
152    }
153 
154    strippedStr = g_strjoinv(0, strArray);
155 
156    g_string_assign(s, strippedStr);
157 
158    g_strfreev(strArray);
159    g_free(strippedStr);
160    return s;
161 }
162 
PE_msg_starts_with_link(const char * c)163 gboolean PE_msg_starts_with_link(const char* c) {
164    /* This seems easy.  But, we need to filter out intermediate HTML
165       (like <font>) as well.  And, to be really compliant, we should
166       parse things like "< a href=" (even if noone else seems to).
167    */
168 
169    while (*c != 0) {
170       /* If we don't start with a tag, we can't start with a link */
171       if (*(c++) != '<') return FALSE;
172 
173       while (isspace(*c)) ++c;     /* skip leading whitespace in tag */
174       if (*c == 'A' || *c == 'a') return TRUE;
175       c = strchr(c, '>');          /* skip to end of tag */
176       if (*c) ++c;                 /* watch out for unclosed tags! */
177    }
178    return FALSE;
179 }
180 
181 /* Removed from Gaim/Pidgin, so added in here :)  */
182 
PE_message_split(char * message,int limit)183 GSList *PE_message_split(char *message, int limit) {
184 	static GSList *ret = NULL;
185 	int lastgood = 0, curgood = 0, curpos = 0, len = strlen(message);
186 	gboolean intag = FALSE;
187 
188 	if (ret) {
189 		GSList *tmp = ret;
190 		while (tmp) {
191 			g_free(tmp->data);
192 			tmp = g_slist_remove(tmp, tmp->data);
193 		}
194 		ret = NULL;
195 	}
196 
197 	while (TRUE) {
198 		if (lastgood >= len)
199 			return ret;
200 
201 		if (len - lastgood < limit) {
202 			ret = g_slist_append(ret, g_strdup(&message[lastgood]));
203 			return ret;
204 		}
205 
206 		curgood = curpos = 0;
207 		intag = FALSE;
208 		while (curpos <= limit) {
209 			if (isspace(message[curpos + lastgood]) && !intag)
210 				curgood = curpos;
211 			if (message[curpos + lastgood] == '<')
212 				intag = TRUE;
213 			if (message[curpos + lastgood] == '>')
214 				intag = FALSE;
215 			curpos++;
216 		}
217 
218 		if (curgood) {
219 			ret = g_slist_append(ret, g_strndup(&message[lastgood], curgood));
220 			if (isspace(message[curgood + lastgood]))
221 				lastgood += curgood + 1;
222 			else
223 				lastgood += curgood;
224 		} else {
225 			/* whoops, guess we have to fudge it here */
226 			ret = g_slist_append(ret, g_strndup(&message[lastgood], limit));
227 			lastgood += limit;
228 		}
229 	}
230 }
231 
232 
233 
234 /* Old versions of utility functions, using Base16 rather than Base64 */
235 
236 
237 /* Note: str will _NOT_ be null terminated.  Its length will be returned
238    from the function */
239 
240 /* int PE_bytes_to_colonstr(unsigned char *str, unsigned char *bytes, int num) { */
241 /*    int bytes_cursor=0, str_cursor=0; */
242 
243 /*    while (bytes_cursor < num) { */
244 /*       sprintf(str + str_cursor, "%02x", bytes[bytes_cursor++]); */
245 /*       str_cursor += 2; */
246 /*       if (bytes_cursor < num) str[str_cursor++] = ':'; */
247 /*    } */
248 
249 /*    return str_cursor; */
250 /* } */
251 
252 /* /\* Note: str will _NOT_ be null terminated.  Its length will be returned */
253 /*    from the function *\/    */
254 /* int PE_bytes_to_str(unsigned char *str, unsigned char *bytes, int num) { */
255 /*    int bytes_cursor=0, str_cursor=0; */
256 
257 /*    while (bytes_cursor < num) { */
258 /*       sprintf(str + str_cursor, "%02x", bytes[bytes_cursor++]); */
259 /*       str_cursor += 2; */
260 /*    } */
261 /*    return str_cursor; */
262 /* } */
263 
264 /* /\* Note: str does not need to be null terminated.  num bytes are pulled */
265 /*          off the string (unless the end of the string is reached first). */
266 /*          The number of chars pulled off the string is returned, or -1 */
267 /*          if there is an error or the end of the string is reached first.     *\/ */
268 
269 /* int PE_nstr_to_bytes(unsigned char *bytes, unsigned char *nstr, int num) { */
270 /*    int bytes_cursor, str_cursor = 0; */
271 /*    unsigned char minibuf[3] = "00"; */
272 
273 /*    for (bytes_cursor = 0; bytes_cursor < num; ++bytes_cursor) { */
274 /*       minibuf[0] = nstr[str_cursor++]; */
275 /*       if (minibuf[0] == 0) return -1; */
276 /*       minibuf[1] = nstr[str_cursor++]; */
277 /*       if (minibuf[1] == 0) return -1;       */
278 /*       bytes[bytes_cursor] = (unsigned char) strtoul(minibuf, 0, 16); */
279 /*    } */
280 /*    return str_cursor; */
281 /* } */
282 
283 /* /\* Note: cstr must be null terminated.  Up to num bytes are pulled */
284 /*          off the string (unless the end of the string is reached first). */
285 /*          The number of bytes gotten is returned.          */
286 /*          A parse error returns -1                                        *\/ */
287 
288 /* int PE_cstr_to_bytes(unsigned char *bytes, unsigned char *cstr, int num) { */
289 /*    int bytes_cursor, str_cursor = 0; */
290 /*    unsigned char minibuf[3] = "00"; */
291 
292 /*    for (bytes_cursor = 0; bytes_cursor < num; ++bytes_cursor) { */
293 /*       minibuf[0] = cstr[str_cursor++]; */
294 /*       if (minibuf[0] == 0) return bytes_cursor; */
295 /*       minibuf[1] = cstr[str_cursor++]; */
296 /*       if (minibuf[1] == 0) return -1;       */
297 /*       bytes[bytes_cursor] = (unsigned char) strtoul(minibuf, 0, 16); */
298 /*    } */
299 /*    return bytes_cursor; */
300 /* } */
301 
302