r2021: Code tidying (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / support.c
blob54dd6c7dc4d59d6dda8d08282ca317675655654c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* support.c - (non-GUI) useful routines */
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <fcntl.h>
35 #include <sys/wait.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <libxml/parser.h>
40 #include <math.h>
42 #include "global.h"
44 #include "choices.h"
45 #include "main.h"
46 #include "options.h"
47 #include "support.h"
48 #include "my_vfs.h"
49 #include "fscache.h"
50 #include "main.h"
51 #include "xml.h"
53 static GHashTable *uid_hash = NULL; /* UID -> User name */
54 static GHashTable *gid_hash = NULL; /* GID -> Group name */
56 /* Static prototypes */
57 static void MD5Transform(guint32 buf[4], guint32 const in[16]);
59 /****************************************************************
60 * EXTERNAL INTERFACE *
61 ****************************************************************/
63 /* g_object_unref() the result! */
64 XMLwrapper *xml_cache_load(const gchar *pathname)
66 static GFSCache *xml_cache = NULL;
68 if (!xml_cache)
69 xml_cache = g_fscache_new((GFSLoadFunc) xml_new, NULL, NULL);
70 return g_fscache_lookup(xml_cache, pathname);
73 /* Save doc as XML as filename, 0 on success or -1 on failure */
74 int save_xml_file(xmlDocPtr doc, const gchar *filename)
76 #if LIBXML_VERSION > 20400
77 if (xmlSaveFormatFileEnc(filename, doc, NULL, 1) < 0)
78 return 1;
79 #else
80 FILE *out;
82 out = fopen(filename, "w");
83 if (!out)
84 return 1;
86 xmlDocDump(out, doc); /* Some versions return void */
88 if (fclose(out))
89 return 1;
90 #endif
92 return 0;
95 /* Create a new SOAP message and return the document and the (empty)
96 * body node.
98 xmlDocPtr soap_new(xmlNodePtr *ret_body)
100 xmlDocPtr doc;
101 xmlNodePtr root;
102 xmlNs *env_ns;
104 doc = xmlNewDoc("1.0");
105 root = xmlNewDocNode(doc, NULL, "Envelope", NULL);
106 xmlDocSetRootElement(doc, root);
108 env_ns = xmlNewNs(root, SOAP_ENV_NS, "env");
109 xmlSetNs(root, env_ns);
111 *ret_body = xmlNewTextChild(root, env_ns, "Body", NULL);
112 xmlNewNs(*ret_body, ROX_NS, "rox");
114 return doc;
117 /* Like g_strdup, but does realpath() too (if possible) */
118 char *pathdup(const char *path)
120 char real[MAXPATHLEN];
122 g_return_val_if_fail(path != NULL, NULL);
124 if (realpath(path, real))
125 return g_strdup(real);
127 return g_strdup(path);
130 /* Join the path to the leaf (adding a / between them) and
131 * return a pointer to a buffer with the result. Buffer is valid until
132 * the next call to make_path.
134 GString *make_path(const char *dir, const char *leaf)
136 static GString *buffer = NULL;
138 if (!buffer)
139 buffer = g_string_new(NULL);
141 g_return_val_if_fail(dir != NULL, buffer);
142 g_return_val_if_fail(leaf != NULL, buffer);
144 g_string_printf(buffer, "%s%s%s",
145 dir,
146 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
147 leaf);
149 return buffer;
152 /* Return our complete host name for DND */
153 const char *our_host_name_for_dnd(void)
155 if (o_dnd_no_hostnames.int_value)
156 return "";
157 return our_host_name();
160 /* Return our complete host name, unconditionally */
161 const char *our_host_name(void)
163 static char *name = NULL;
165 if (!name)
167 char buffer[4096];
169 if (gethostname(buffer, 4096) == 0)
171 /* gethostname doesn't always return the full name... */
172 struct hostent *ent;
174 buffer[4095] = '\0';
175 ent = gethostbyname(buffer);
176 name = g_strdup(ent ? ent->h_name : buffer);
178 else
180 g_warning("gethostname() failed - using localhost\n");
181 name = g_strdup("localhost");
185 return name;
188 void debug_free_string(void *data)
190 g_print("Freeing string '%s'\n", (char *) data);
191 g_free(data);
194 const char *user_name(uid_t uid)
196 const char *retval;
198 if (!uid_hash)
199 uid_hash = g_hash_table_new(NULL, NULL);
201 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
203 if (!retval)
205 struct passwd *passwd;
207 passwd = getpwuid(uid);
208 retval = passwd ? g_strdup(passwd->pw_name)
209 : g_strdup_printf("[%d]", (int) uid);
210 g_hash_table_insert(uid_hash, GINT_TO_POINTER(uid),
211 (gchar *) retval);
214 return retval;
217 const char *group_name(gid_t gid)
219 const char *retval;
221 if (!gid_hash)
222 gid_hash = g_hash_table_new(NULL, NULL);
224 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
226 if (!retval)
228 struct group *group;
230 group = getgrgid(gid);
231 retval = group ? g_strdup(group->gr_name)
232 : g_strdup_printf("[%d]", (int) gid);
233 g_hash_table_insert(gid_hash, GINT_TO_POINTER(gid),
234 (gchar *) retval);
237 return retval;
240 /* Return a string in the form '23Mb' in a static buffer valid until
241 * the next call.
243 char *format_size(off_t size)
245 static char *buffer = NULL;
246 const char *units;
248 if (size >= PRETTY_SIZE_LIMIT)
250 size += 1023;
251 size >>= 10;
252 if (size >= PRETTY_SIZE_LIMIT)
254 size += 1023;
255 size >>= 10;
256 if (size >= PRETTY_SIZE_LIMIT)
258 size += 1023;
259 size >>= 10;
260 units = "G";
262 else
263 units = "M";
265 else
266 units = "K";
268 else if (size == 1)
269 units = _("byte");
270 else
271 units = _("bytes");
273 g_free(buffer);
274 buffer = g_strdup_printf("%" SIZE_FMT " %s", size, units);
276 return buffer;
279 /* Return a string in the form '23M' in a static buffer valid until
280 * the next call. Aligned to the right (5 chars).
282 char *format_size_aligned(off_t size)
284 static char *buffer = NULL;
285 char units;
287 if (size >= PRETTY_SIZE_LIMIT)
289 size += 1023;
290 size >>= 10;
291 if (size >= PRETTY_SIZE_LIMIT)
293 size += 1023;
294 size >>= 10;
295 if (size >= PRETTY_SIZE_LIMIT)
297 size += 1023;
298 size >>= 10;
299 units = 'G';
301 else
302 units = 'M';
304 else
305 units = 'K';
307 else
308 units = ' ';
310 g_free(buffer);
311 buffer = g_strdup_printf("%4" SIZE_FMT "%c", size, units);
313 return buffer;
317 * Similar to format_size(), but this one uses a double argument since
318 * unsigned long isn't wide enough on all platforms and we must be able to
319 * sum sizes above 4 GB.
321 gchar *format_double_size(double size)
323 static gchar *buf = NULL;
324 const char *units;
326 if (size >= PRETTY_SIZE_LIMIT)
328 size += 1023;
329 size /= 1024;
330 if (size >= PRETTY_SIZE_LIMIT)
332 size += 1023;
333 size /= 1024;
334 if (size >= PRETTY_SIZE_LIMIT)
336 size += 1023;
337 size /= 1024;
338 units = "G";
340 else
341 units = "M";
343 else
344 units = "K";
347 else if (size != 1)
348 units = _("bytes");
349 else
350 units = _("byte");
352 g_free(buf);
353 buf = g_strdup_printf("%.0f %s", floor(size), units);
355 return buf;
358 /* Fork and exec argv. Wait and return the child's exit status.
359 * -1 if spawn fails.
360 * Returns the error string from the command if any, or NULL on success.
361 * If the process returns a non-zero exit status without producing a message,
362 * a suitable message is created.
363 * g_free() the result.
365 char *fork_exec_wait(const char **argv)
367 int status;
368 gchar *errors = NULL;
369 GError *error = NULL;
371 if (!g_spawn_sync(NULL, (char **) argv, NULL,
372 G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
373 NULL, NULL,
374 NULL, &errors, &status, &error))
376 char *msg;
378 msg = g_strdup(error->message);
379 g_error_free(error);
380 return msg;
383 if (errors && !*errors)
384 null_g_free(&errors);
386 if (!WIFEXITED(status))
388 if (!errors)
389 errors = g_strdup("(Subprocess crashed?)");
391 else if (WEXITSTATUS(status))
393 if (!errors)
394 errors = g_strdup(_("ERROR"));
397 if (errors)
398 g_strstrip(errors);
400 return errors;
403 /* If a file has this UID and GID, which permissions apply to us?
404 * 0 = User, 1 = Group, 2 = World
406 gint applicable(uid_t uid, gid_t gid)
408 int i;
410 if (uid == euid)
411 return 0;
413 if (gid == egid)
414 return 1;
416 for (i = 0; i < ngroups; i++)
418 if (supplemental_groups[i] == gid)
419 return 1;
422 return 2;
425 /* Converts a file's mode to a string. Result is a pointer
426 * to a static buffer, valid until the next call.
428 char *pretty_permissions(mode_t m)
430 static char buffer[] = "rwx,rwx,rwx/UG"
431 #ifdef S_ISVTX
433 #endif
436 buffer[0] = m & S_IRUSR ? 'r' : '-';
437 buffer[1] = m & S_IWUSR ? 'w' : '-';
438 buffer[2] = m & S_IXUSR ? 'x' : '-';
440 buffer[4] = m & S_IRGRP ? 'r' : '-';
441 buffer[5] = m & S_IWGRP ? 'w' : '-';
442 buffer[6] = m & S_IXGRP ? 'x' : '-';
444 buffer[8] = m & S_IROTH ? 'r' : '-';
445 buffer[9] = m & S_IWOTH ? 'w' : '-';
446 buffer[10] = m & S_IXOTH ? 'x' : '-';
448 buffer[12] = m & S_ISUID ? 'U' : '-';
449 buffer[13] = m & S_ISGID ? 'G' : '-';
450 #ifdef S_ISVTX
451 buffer[14] = m & S_ISVTX ? 'T' : '-';
452 #endif
454 return buffer;
457 /* Gets the canonical name for address and compares to our_host_name() */
458 static gboolean is_local_address(char *address)
460 struct hostent *ent;
462 ent = gethostbyname(address);
464 return strcmp(our_host_name(), ent ? ent->h_name : address) == 0;
467 /* Convert a URI to a local pathname (or NULL if it isn't local).
468 * The returned pointer points inside the input string.
469 * Possible formats:
470 * /path
471 * ///path
472 * //host/path
473 * file://host/path
475 const char *get_local_path(const char *uri)
477 if (*uri == '/')
479 char *path, *uri_host;
481 if (uri[1] != '/')
482 return uri; /* Just a local path - no host part */
484 path = strchr(uri + 2, '/');
485 if (!path)
486 return NULL; /* //something */
488 if (path - uri == 2)
489 return path; /* ///path */
491 uri_host = g_strndup(uri + 2, path - uri - 2);
492 if (is_local_address(uri_host))
494 g_free(uri_host);
495 return path; /* //myhost/path */
497 g_free(uri_host);
499 return NULL; /* From a different host */
501 else
503 if (strncasecmp(uri, "file:", 5))
504 return NULL; /* Don't know this format */
506 uri += 5;
508 if (*uri == '/')
509 return get_local_path(uri);
511 return NULL;
515 /* Set the close-on-exec flag for this FD.
516 * TRUE means that an exec()'d process will not get the FD.
518 void close_on_exec(int fd, gboolean close)
520 if (fcntl(fd, F_SETFD, close))
521 g_warning("fcntl() failed: %s\n", g_strerror(errno));
524 void set_blocking(int fd, gboolean blocking)
526 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
527 g_warning("fcntl() failed: %s\n", g_strerror(errno));
530 /* Format this time nicely.
531 * g_free() the result.
533 char *pretty_time(const time_t *time)
535 char time_buf[32];
537 if (strftime(time_buf, sizeof(time_buf),
538 TIME_FORMAT, localtime(time)) == 0)
539 time_buf[0]= 0;
541 return to_utf8(time_buf);
544 #ifndef O_NOFOLLOW
545 # define O_NOFOLLOW 0x0
546 #endif
548 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
549 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
550 * do the copy manually using vfs.
552 * Returns an error string, or NULL on success. g_free() the result.
554 * XXX: This was only used for libvfs...
556 guchar *copy_file(const guchar *from, const guchar *to)
558 const char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
560 argv[2] = from;
561 argv[3] = to;
563 return fork_exec_wait(argv);
566 /* 'word' has all special characters escaped so that it may be inserted
567 * into a shell command.
568 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
570 guchar *shell_escape(const guchar *word)
572 GString *tmp;
573 guchar *retval;
575 tmp = g_string_new(NULL);
577 while (*word)
579 if (strchr(" ?*['\"$~\\|();!`&", *word))
580 g_string_append_c(tmp, '\\');
581 g_string_append_c(tmp, *word);
582 word++;
585 retval = tmp->str;
586 g_string_free(tmp, FALSE);
587 return retval;
590 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
591 * (or the two are the same item/directory).
592 * FALSE if parent doesn't exist.
594 gboolean is_sub_dir(const char *sub_obj, const char *parent)
596 struct stat parent_info;
597 char *sub;
599 if (mc_lstat(parent, &parent_info))
600 return FALSE; /* Parent doesn't exist */
602 /* For checking Copy/Move operations do a realpath first on sub
603 * (the destination), since copying into a symlink is the same as
604 * copying into the thing it points to. Don't realpath 'parent' though;
605 * copying a symlink just makes a new symlink.
607 * When checking if an icon depends on a file (parent), use realpath on
608 * sub (the icon) too.
610 sub = pathdup(sub_obj);
612 while (1)
614 char *slash;
615 struct stat info;
617 if (mc_lstat(sub, &info) == 0)
619 if (info.st_dev == parent_info.st_dev &&
620 info.st_ino == parent_info.st_ino)
622 g_free(sub);
623 return TRUE;
627 slash = strrchr(sub, '/');
628 if (!slash)
629 break;
630 if (slash == sub)
632 if (sub[1])
633 sub[1] = '\0';
634 else
635 break;
637 else
638 *slash = '\0';
641 g_free(sub);
643 return FALSE;
646 /* True if the string 'list' contains 'item'.
647 * Eg ("close", "close, help") -> TRUE
649 gboolean in_list(const guchar *item, const guchar *list)
651 int len;
653 len = strlen(item);
655 while (*list)
657 if (strncmp(item, list, len) == 0 && !isalpha(list[len]))
658 return TRUE;
659 list = strchr(list, ',');
660 if (!list)
661 return FALSE;
662 while (isspace(*++list))
666 return FALSE;
669 /* Split a path into its components. Eg:
671 * /bob/fred -> ["bob", "fred"]
672 * ///a//b// -> ["a", "b"]
673 * / -> []
675 * The array and the strings in it must be freed after use.
677 GPtrArray *split_path(const guchar *path)
679 GPtrArray *array;
680 guchar *slash;
682 g_return_val_if_fail(path != NULL, NULL);
684 array = g_ptr_array_new();
686 while (1)
688 while (path[0] == '/')
689 path++;
690 if (path[0] == '\0')
691 break;
693 slash = strchr(path, '/');
694 if (slash)
696 g_ptr_array_add(array, g_strndup(path, slash - path));
697 path = slash + 1;
698 continue;
700 g_ptr_array_add(array, g_strdup(path));
701 break;
704 return array;
707 /* Return the shortest path from 'from' to 'to'.
708 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
710 guchar *get_relative_path(const guchar *from, const guchar *to)
712 GString *path;
713 guchar *retval;
714 GPtrArray *src, *dst;
715 int i, j;
717 src = split_path(from);
718 dst = split_path(to);
720 /* The last component of src doesn't matter... */
721 if (src->len)
723 g_free(src->pdata[src->len - 1]);
724 g_ptr_array_remove_index(src, src->len - 1);
727 /* Strip off common path elements... */
728 i = 0;
729 while (i < src->len && i < dst->len)
731 guchar *a = (guchar *) src->pdata[i];
732 guchar *b = (guchar *) dst->pdata[i];
734 if (strcmp(a, b) != 0)
735 break;
736 i++;
739 /* Go up one dir for each element remaining in src */
740 path = g_string_new(NULL);
741 for (j = i; j < src->len; j++)
742 g_string_append(path, "../");
744 /* Go down one dir for each element remaining in dst */
745 for (j = i; j < dst->len; j++)
747 g_string_append(path, (guchar *) dst->pdata[j]);
748 g_string_append_c(path, '/');
751 if (path->str[path->len - 1] == '/')
752 g_string_truncate(path, path->len - 1);
753 if (path->len == 0)
754 g_string_assign(path, ".");
756 /* Free the arrays */
757 for (i = 0; i < src->len; i++)
758 g_free(src->pdata[i]);
759 g_ptr_array_free(src, TRUE);
760 for (i = 0; i < dst->len; i++)
761 g_free(dst->pdata[i]);
762 g_ptr_array_free(dst, TRUE);
764 retval = path->str;
765 g_string_free(path, FALSE);
767 return retval;
771 * Interperet text as a boolean value. Return defvalue if we don't
772 * recognise it
774 int text_to_boolean(const char *text, int defvalue)
776 if (g_strcasecmp(text, "true")==0)
777 return TRUE;
778 else if (g_strcasecmp(text, "false")==0)
779 return FALSE;
780 else if (g_strcasecmp(text, "yes")==0)
781 return TRUE;
782 else if (g_strcasecmp(text, "no")==0)
783 return FALSE;
784 else if (isdigit(text[0]))
785 return !!atoi(text);
787 return defvalue;
790 /* Return the pathname that this symlink points to.
791 * NULL on error (not a symlink, path too long) and errno set.
792 * g_free() the result.
794 char *readlink_dup(const char *source)
796 char path[MAXPATHLEN + 1];
797 int got;
799 got = readlink(source, path, MAXPATHLEN);
800 if (got < 0 || got > MAXPATHLEN)
801 return NULL;
803 return g_strndup(path, got);
807 * This code implements the MD5 message-digest algorithm.
808 * The algorithm is due to Ron Rivest. The original code was
809 * written by Colin Plumb in 1993, and put in the public domain.
811 * Modified to use glib datatypes. Put under GPL to simplify
812 * licensing for ROX-Filer. Taken from Debian's dpkg package.
815 #define md5byte unsigned char
817 typedef struct _MD5Context MD5Context;
819 struct _MD5Context {
820 guint32 buf[4];
821 guint32 bytes[2];
822 guint32 in[16];
825 #if G_BYTE_ORDER == G_BIG_ENDIAN
826 void byteSwap(guint32 *buf, unsigned words)
828 md5byte *p = (md5byte *)buf;
830 do {
831 *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
832 ((unsigned)p[1] << 8 | p[0]);
833 p += 4;
834 } while (--words);
836 #else
837 #define byteSwap(buf,words)
838 #endif
841 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
842 * initialization constants.
844 static void MD5Init(MD5Context *ctx)
846 ctx->buf[0] = 0x67452301;
847 ctx->buf[1] = 0xefcdab89;
848 ctx->buf[2] = 0x98badcfe;
849 ctx->buf[3] = 0x10325476;
851 ctx->bytes[0] = 0;
852 ctx->bytes[1] = 0;
856 * Update context to reflect the concatenation of another buffer full
857 * of bytes.
859 static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
861 guint32 t;
863 /* Update byte count */
865 t = ctx->bytes[0];
866 if ((ctx->bytes[0] = t + len) < t)
867 ctx->bytes[1]++; /* Carry from low to high */
869 t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
870 if (t > len) {
871 memcpy((md5byte *)ctx->in + 64 - t, buf, len);
872 return;
874 /* First chunk is an odd size */
875 memcpy((md5byte *)ctx->in + 64 - t, buf, t);
876 byteSwap(ctx->in, 16);
877 MD5Transform(ctx->buf, ctx->in);
878 buf += t;
879 len -= t;
881 /* Process data in 64-byte chunks */
882 while (len >= 64) {
883 memcpy(ctx->in, buf, 64);
884 byteSwap(ctx->in, 16);
885 MD5Transform(ctx->buf, ctx->in);
886 buf += 64;
887 len -= 64;
890 /* Handle any remaining bytes of data. */
891 memcpy(ctx->in, buf, len);
895 * Final wrapup - pad to 64-byte boundary with the bit pattern
896 * 1 0* (64-bit count of bits processed, MSB-first)
897 * Returns the newly allocated string of the hash.
899 static char *MD5Final(MD5Context *ctx)
901 char *retval;
902 int i;
903 int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
904 md5byte *p = (md5byte *)ctx->in + count;
905 guint8 *bytes;
907 /* Set the first char of padding to 0x80. There is always room. */
908 *p++ = 0x80;
910 /* Bytes of padding needed to make 56 bytes (-8..55) */
911 count = 56 - 1 - count;
913 if (count < 0) { /* Padding forces an extra block */
914 memset(p, 0, count + 8);
915 byteSwap(ctx->in, 16);
916 MD5Transform(ctx->buf, ctx->in);
917 p = (md5byte *)ctx->in;
918 count = 56;
920 memset(p, 0, count);
921 byteSwap(ctx->in, 14);
923 /* Append length in bits and transform */
924 ctx->in[14] = ctx->bytes[0] << 3;
925 ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
926 MD5Transform(ctx->buf, ctx->in);
928 byteSwap(ctx->buf, 4);
930 retval = g_malloc(33);
931 bytes = (guint8 *) ctx->buf;
932 for (i = 0; i < 16; i++)
933 sprintf(retval + (i * 2), "%02x", bytes[i]);
934 retval[32] = '\0';
936 return retval;
939 # ifndef ASM_MD5
941 /* The four core functions - F1 is optimized somewhat */
943 /* #define F1(x, y, z) (x & y | ~x & z) */
944 #define F1(x, y, z) (z ^ (x & (y ^ z)))
945 #define F2(x, y, z) F1(z, x, y)
946 #define F3(x, y, z) (x ^ y ^ z)
947 #define F4(x, y, z) (y ^ (x | ~z))
949 /* This is the central step in the MD5 algorithm. */
950 #define MD5STEP(f,w,x,y,z,in,s) \
951 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
954 * The core of the MD5 algorithm, this alters an existing MD5 hash to
955 * reflect the addition of 16 longwords of new data. MD5Update blocks
956 * the data and converts bytes into longwords for this routine.
958 static void MD5Transform(guint32 buf[4], guint32 const in[16])
960 register guint32 a, b, c, d;
962 a = buf[0];
963 b = buf[1];
964 c = buf[2];
965 d = buf[3];
967 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
968 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
969 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
970 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
971 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
972 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
973 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
974 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
975 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
976 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
977 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
978 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
979 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
980 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
981 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
982 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
984 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
985 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
986 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
987 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
988 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
989 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
990 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
991 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
992 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
993 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
994 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
995 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
996 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
997 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
998 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
999 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1001 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1002 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1003 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1004 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1005 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1006 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1007 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1008 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1009 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1010 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1011 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1012 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1013 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1014 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1015 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1016 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1018 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1019 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1020 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1021 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1022 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1023 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1024 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1025 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1026 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1027 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1028 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1029 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1030 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1031 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1032 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1033 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1035 buf[0] += a;
1036 buf[1] += b;
1037 buf[2] += c;
1038 buf[3] += d;
1041 # endif /* ASM_MD5 */
1043 char *md5_hash(const char *message)
1045 MD5Context ctx;
1047 MD5Init(&ctx);
1048 MD5Update(&ctx, message, strlen(message));
1049 return MD5Final(&ctx);
1052 /* Convert string 'src' from the current locale to UTF-8 */
1053 gchar *to_utf8(const gchar *src)
1055 gchar *retval;
1057 if (!src)
1058 return NULL;
1060 retval = g_locale_to_utf8(src, -1, NULL, NULL, NULL);
1062 return retval ? retval : g_strdup(src);
1065 /* Ensure string at 'sp' is UTF-8. g_free() and replace by
1066 * UTF-8 version if not.
1068 void ensure_utf8(gchar **sp)
1070 gchar *s = *sp;
1072 if (!g_utf8_validate(s, -1, NULL))
1074 *sp = to_utf8(s);
1075 g_free(s);
1079 /* Removes trailing / chars and converts a leading '~/' (if any) to
1080 * the user's home dir. g_free() the result.
1082 gchar *expand_path(const gchar *path)
1084 guchar *retval;
1085 int path_len;
1087 g_return_val_if_fail(path != NULL, NULL);
1089 path_len = strlen(path);
1090 while (path_len > 1 && path[path_len - 1] == '/')
1091 path_len--;
1093 retval = g_strndup(path, path_len);
1095 if (path[0] == '~' && (path[1] == '\0' || path[1] == '/'))
1097 guchar *tmp = retval;
1099 retval = g_strconcat(home_dir, retval + 1, NULL);
1100 g_free(tmp);
1103 return retval;
1106 /* g_free() every element in the list, then free the list itself and
1107 * NULL the pointer to the list.
1109 void destroy_glist(GList **list)
1111 GList *l = *list;
1112 g_list_foreach(l, (GFunc) g_free, NULL);
1113 g_list_free(l);
1114 *list = NULL;
1117 void null_g_free(gpointer p)
1119 g_free(*(gpointer *)p);
1120 *(gpointer *)p = NULL;